aboutsummaryrefslogtreecommitdiff
path: root/sound/pci/hda/hda_codec.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-12-13 18:30:04 +0100
committerTakashi Iwai <tiwai@suse.de>2013-01-12 08:29:17 +0100
commitc370dd6e9faae4b2e699a1f210827aceaa0c3399 (patch)
tree8b80d63a3c3f7f42161e653acd71a1e27dd4284a /sound/pci/hda/hda_codec.c
parent8092e6065435d75a68873fa66cd003a1b829e0fe (diff)
ALSA: hda - Introduce cache & flush cmd / amp writes
For optimizing the verb executions, a new mechanism to cache the verbs and amp update commands is introduced. With the new "write to cache and flush" way, you can reduce the same verbs that have been written multiple times. When codec->cached_write flag is set, the further snd_hda_codec_write_cache() and snd_hda_codec_amp_stereo() calls will be performed only on the command or amp cache table, but not sent to the hardware yet. Once after you call all commands and update amps, call snd_hda_codec_resume_amp() and snd_hda_codec_resume_cache(). Then all cached writes and amp updates will be written to the hardware, and the dirty flags are cleared. In this implementation, the existing cache table is reused, so actually no big code change is seen here. Each cache entry has a new dirty flag now (so the cache key is now reduced to 31bit). As a good side-effect by this change, snd_hda_codec_resume_*() will no longer execute verbs that have been already issued during the resume phase by checking the dirty flags. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r--sound/pci/hda/hda_codec.c77
1 files changed, 61 insertions, 16 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index b8fb0a5adb9b..2f890af820b2 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1610,6 +1610,7 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
cur = snd_array_index(&cache->buf, info);
info->key = key;
info->val = 0;
+ info->dirty = 0;
idx = key % (u16)ARRAY_SIZE(cache->hash);
info->next = cache->hash[idx];
cache->hash[idx] = cur;
@@ -1873,8 +1874,11 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
return 0;
}
info->vol[ch] = val;
+ if (codec->cached_write)
+ info->head.dirty = 1;
mutex_unlock(&codec->hash_mutex);
- put_vol_mute(codec, info, nid, ch, direction, idx, val);
+ if (!codec->cached_write)
+ put_vol_mute(codec, info, nid, ch, direction, idx, val);
return 1;
}
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
@@ -1905,7 +1909,6 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
-#ifdef CONFIG_PM
/**
* snd_hda_codec_resume_amp - Resume all AMP commands from the cache
* @codec: HD-audio codec
@@ -1914,13 +1917,17 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
*/
void snd_hda_codec_resume_amp(struct hda_codec *codec)
{
- struct hda_amp_info *buffer = codec->amp_cache.buf.list;
int i;
- for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
- u32 key = buffer->head.key;
+ mutex_lock(&codec->hash_mutex);
+ for (i = 0; i < codec->amp_cache.buf.used; i++) {
+ struct hda_amp_info *buffer;
+ u32 key;
hda_nid_t nid;
unsigned int idx, dir, ch;
+
+ buffer = snd_array_elem(&codec->amp_cache.buf, i);
+ key = buffer->head.key;
if (!key)
continue;
nid = key & 0xff;
@@ -1929,13 +1936,18 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
for (ch = 0; ch < 2; ch++) {
if (!(buffer->head.val & INFO_AMP_VOL(ch)))
continue;
+ if (!buffer->head.dirty)
+ continue;
+ buffer->head.dirty = 0;
+ mutex_unlock(&codec->hash_mutex);
put_vol_mute(codec, buffer, nid, ch, dir, idx,
buffer->vol[ch]);
+ mutex_lock(&codec->hash_mutex);
}
}
+ mutex_unlock(&codec->hash_mutex);
}
EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
-#endif /* CONFIG_PM */
static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int ofs)
@@ -3375,12 +3387,11 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
}
EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
-#ifdef CONFIG_PM
/*
* command cache
*/
-/* build a 32bit cache key with the widget id and the command parameter */
+/* build a 31bit cache key with the widget id and the command parameter */
#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
#define get_cmd_cache_nid(key) ((key) & 0xff)
#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff)
@@ -3400,20 +3411,27 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
int direct, unsigned int verb, unsigned int parm)
{
- int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
+ int err;
struct hda_cache_head *c;
u32 key;
- if (err < 0)
- return err;
+ if (!codec->cached_write) {
+ err = snd_hda_codec_write(codec, nid, direct, verb, parm);
+ if (err < 0)
+ return err;
+ }
+
/* parm may contain the verb stuff for get/set amp */
verb = verb | (parm >> 8);
parm &= 0xff;
key = build_cmd_cache_key(nid, verb);
mutex_lock(&codec->bus->cmd_mutex);
c = get_alloc_hash(&codec->cmd_cache, key);
- if (c)
+ if (c) {
c->val = parm;
+ if (codec->cached_write)
+ c->dirty = 1;
+ }
mutex_unlock(&codec->bus->cmd_mutex);
return 0;
}
@@ -3462,16 +3480,26 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
*/
void snd_hda_codec_resume_cache(struct hda_codec *codec)
{
- struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
int i;
- for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
- u32 key = buffer->key;
+ mutex_lock(&codec->hash_mutex);
+ for (i = 0; i < codec->cmd_cache.buf.used; i++) {
+ struct hda_cache_head *buffer;
+ u32 key;
+
+ buffer = snd_array_elem(&codec->cmd_cache.buf, i);
+ key = buffer->key;
if (!key)
continue;
+ if (!buffer->dirty)
+ continue;
+ buffer->dirty = 0;
+ mutex_unlock(&codec->hash_mutex);
snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,
get_cmd_cache_cmd(key), buffer->val);
+ mutex_lock(&codec->hash_mutex);
}
+ mutex_unlock(&codec->hash_mutex);
}
EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
@@ -3492,7 +3520,6 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
seq->param);
}
EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
-#endif /* CONFIG_PM */
void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state,
@@ -3640,6 +3667,22 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
return state;
}
+/* mark all entries of cmd and amp caches dirty */
+static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
+{
+ int i;
+ for (i = 0; i < codec->cmd_cache.buf.used; i++) {
+ struct hda_cache_head *cmd;
+ cmd = snd_array_elem(&codec->cmd_cache.buf, i);
+ cmd->dirty = 1;
+ }
+ for (i = 0; i < codec->amp_cache.buf.used; i++) {
+ struct hda_amp_info *amp;
+ amp = snd_array_elem(&codec->cmd_cache.buf, i);
+ amp->head.dirty = 1;
+ }
+}
+
/*
* kick up codec; used both from PM and power-save
*/
@@ -3647,6 +3690,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
{
codec->in_pm = 1;
+ hda_mark_cmd_cache_dirty(codec);
+
/* set as if powered on for avoiding re-entering the resume
* in the resume / power-save sequence
*/