aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_codec.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-12-13 12:30:04 -0500
committerTakashi Iwai <tiwai@suse.de>2013-01-12 02:29:17 -0500
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,
1610 cur = snd_array_index(&cache->buf, info); 1610 cur = snd_array_index(&cache->buf, info);
1611 info->key = key; 1611 info->key = key;
1612 info->val = 0; 1612 info->val = 0;
1613 info->dirty = 0;
1613 idx = key % (u16)ARRAY_SIZE(cache->hash); 1614 idx = key % (u16)ARRAY_SIZE(cache->hash);
1614 info->next = cache->hash[idx]; 1615 info->next = cache->hash[idx];
1615 cache->hash[idx] = cur; 1616 cache->hash[idx] = cur;
@@ -1873,8 +1874,11 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
1873 return 0; 1874 return 0;
1874 } 1875 }
1875 info->vol[ch] = val; 1876 info->vol[ch] = val;
1877 if (codec->cached_write)
1878 info->head.dirty = 1;
1876 mutex_unlock(&codec->hash_mutex); 1879 mutex_unlock(&codec->hash_mutex);
1877 put_vol_mute(codec, info, nid, ch, direction, idx, val); 1880 if (!codec->cached_write)
1881 put_vol_mute(codec, info, nid, ch, direction, idx, val);
1878 return 1; 1882 return 1;
1879} 1883}
1880EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); 1884EXPORT_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,
1905} 1909}
1906EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); 1910EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
1907 1911
1908#ifdef CONFIG_PM
1909/** 1912/**
1910 * snd_hda_codec_resume_amp - Resume all AMP commands from the cache 1913 * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
1911 * @codec: HD-audio codec 1914 * @codec: HD-audio codec
@@ -1914,13 +1917,17 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
1914 */ 1917 */
1915void snd_hda_codec_resume_amp(struct hda_codec *codec) 1918void snd_hda_codec_resume_amp(struct hda_codec *codec)
1916{ 1919{
1917 struct hda_amp_info *buffer = codec->amp_cache.buf.list;
1918 int i; 1920 int i;
1919 1921
1920 for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) { 1922 mutex_lock(&codec->hash_mutex);
1921 u32 key = buffer->head.key; 1923 for (i = 0; i < codec->amp_cache.buf.used; i++) {
1924 struct hda_amp_info *buffer;
1925 u32 key;
1922 hda_nid_t nid; 1926 hda_nid_t nid;
1923 unsigned int idx, dir, ch; 1927 unsigned int idx, dir, ch;
1928
1929 buffer = snd_array_elem(&codec->amp_cache.buf, i);
1930 key = buffer->head.key;
1924 if (!key) 1931 if (!key)
1925 continue; 1932 continue;
1926 nid = key & 0xff; 1933 nid = key & 0xff;
@@ -1929,13 +1936,18 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
1929 for (ch = 0; ch < 2; ch++) { 1936 for (ch = 0; ch < 2; ch++) {
1930 if (!(buffer->head.val & INFO_AMP_VOL(ch))) 1937 if (!(buffer->head.val & INFO_AMP_VOL(ch)))
1931 continue; 1938 continue;
1939 if (!buffer->head.dirty)
1940 continue;
1941 buffer->head.dirty = 0;
1942 mutex_unlock(&codec->hash_mutex);
1932 put_vol_mute(codec, buffer, nid, ch, dir, idx, 1943 put_vol_mute(codec, buffer, nid, ch, dir, idx,
1933 buffer->vol[ch]); 1944 buffer->vol[ch]);
1945 mutex_lock(&codec->hash_mutex);
1934 } 1946 }
1935 } 1947 }
1948 mutex_unlock(&codec->hash_mutex);
1936} 1949}
1937EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); 1950EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
1938#endif /* CONFIG_PM */
1939 1951
1940static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, 1952static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
1941 unsigned int ofs) 1953 unsigned int ofs)
@@ -3375,12 +3387,11 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
3375} 3387}
3376EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); 3388EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
3377 3389
3378#ifdef CONFIG_PM
3379/* 3390/*
3380 * command cache 3391 * command cache
3381 */ 3392 */
3382 3393
3383/* build a 32bit cache key with the widget id and the command parameter */ 3394/* build a 31bit cache key with the widget id and the command parameter */
3384#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid) 3395#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
3385#define get_cmd_cache_nid(key) ((key) & 0xff) 3396#define get_cmd_cache_nid(key) ((key) & 0xff)
3386#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff) 3397#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff)
@@ -3400,20 +3411,27 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
3400int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, 3411int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
3401 int direct, unsigned int verb, unsigned int parm) 3412 int direct, unsigned int verb, unsigned int parm)
3402{ 3413{
3403 int err = snd_hda_codec_write(codec, nid, direct, verb, parm); 3414 int err;
3404 struct hda_cache_head *c; 3415 struct hda_cache_head *c;
3405 u32 key; 3416 u32 key;
3406 3417
3407 if (err < 0) 3418 if (!codec->cached_write) {
3408 return err; 3419 err = snd_hda_codec_write(codec, nid, direct, verb, parm);
3420 if (err < 0)
3421 return err;
3422 }
3423
3409 /* parm may contain the verb stuff for get/set amp */ 3424 /* parm may contain the verb stuff for get/set amp */
3410 verb = verb | (parm >> 8); 3425 verb = verb | (parm >> 8);
3411 parm &= 0xff; 3426 parm &= 0xff;
3412 key = build_cmd_cache_key(nid, verb); 3427 key = build_cmd_cache_key(nid, verb);
3413 mutex_lock(&codec->bus->cmd_mutex); 3428 mutex_lock(&codec->bus->cmd_mutex);
3414 c = get_alloc_hash(&codec->cmd_cache, key); 3429 c = get_alloc_hash(&codec->cmd_cache, key);
3415 if (c) 3430 if (c) {
3416 c->val = parm; 3431 c->val = parm;
3432 if (codec->cached_write)
3433 c->dirty = 1;
3434 }
3417 mutex_unlock(&codec->bus->cmd_mutex); 3435 mutex_unlock(&codec->bus->cmd_mutex);
3418 return 0; 3436 return 0;
3419} 3437}
@@ -3462,16 +3480,26 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
3462 */ 3480 */
3463void snd_hda_codec_resume_cache(struct hda_codec *codec) 3481void snd_hda_codec_resume_cache(struct hda_codec *codec)
3464{ 3482{
3465 struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
3466 int i; 3483 int i;
3467 3484
3468 for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) { 3485 mutex_lock(&codec->hash_mutex);
3469 u32 key = buffer->key; 3486 for (i = 0; i < codec->cmd_cache.buf.used; i++) {
3487 struct hda_cache_head *buffer;
3488 u32 key;
3489
3490 buffer = snd_array_elem(&codec->cmd_cache.buf, i);
3491 key = buffer->key;
3470 if (!key) 3492 if (!key)
3471 continue; 3493 continue;
3494 if (!buffer->dirty)
3495 continue;
3496 buffer->dirty = 0;
3497 mutex_unlock(&codec->hash_mutex);
3472 snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0, 3498 snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,
3473 get_cmd_cache_cmd(key), buffer->val); 3499 get_cmd_cache_cmd(key), buffer->val);
3500 mutex_lock(&codec->hash_mutex);
3474 } 3501 }
3502 mutex_unlock(&codec->hash_mutex);
3475} 3503}
3476EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache); 3504EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
3477 3505
@@ -3492,7 +3520,6 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
3492 seq->param); 3520 seq->param);
3493} 3521}
3494EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); 3522EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
3495#endif /* CONFIG_PM */
3496 3523
3497void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, 3524void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
3498 unsigned int power_state, 3525 unsigned int power_state,
@@ -3640,6 +3667,22 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
3640 return state; 3667 return state;
3641} 3668}
3642 3669
3670/* mark all entries of cmd and amp caches dirty */
3671static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
3672{
3673 int i;
3674 for (i = 0; i < codec->cmd_cache.buf.used; i++) {
3675 struct hda_cache_head *cmd;
3676 cmd = snd_array_elem(&codec->cmd_cache.buf, i);
3677 cmd->dirty = 1;
3678 }
3679 for (i = 0; i < codec->amp_cache.buf.used; i++) {
3680 struct hda_amp_info *amp;
3681 amp = snd_array_elem(&codec->cmd_cache.buf, i);
3682 amp->head.dirty = 1;
3683 }
3684}
3685
3643/* 3686/*
3644 * kick up codec; used both from PM and power-save 3687 * kick up codec; used both from PM and power-save
3645 */ 3688 */
@@ -3647,6 +3690,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
3647{ 3690{
3648 codec->in_pm = 1; 3691 codec->in_pm = 1;
3649 3692
3693 hda_mark_cmd_cache_dirty(codec);
3694
3650 /* set as if powered on for avoiding re-entering the resume 3695 /* set as if powered on for avoiding re-entering the resume
3651 * in the resume / power-save sequence 3696 * in the resume / power-save sequence
3652 */ 3697 */