aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_codec.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2007-08-10 11:09:26 -0400
committerJaroslav Kysela <perex@perex.cz>2007-10-16 09:58:43 -0400
commit82beb8fd365afe3891b277c46425083f13e23c56 (patch)
treea564d7228b59170aa490d4fc9284b5fa4442adb0 /sound/pci/hda/hda_codec.c
parentb3ac56364126f78cae94eb2a75b72d9ea85aca9d (diff)
[ALSA] hda-codec - optimize resume using caches
So far, the driver looked the table of snd_kcontrol_new used for creating mixer elements and forces to call each of its put callbacks in PM resume code. This is too ugly and hackish. Now, the resume is simplified using the codec amp and command register caches. The driver simply restores the values that have been written in the cache table. With this simplification, most codec support codes don't require any special resume callback. Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r--sound/pci/hda/hda_codec.c115
1 files changed, 37 insertions, 78 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 6652a531980d..1d31da47bc9b 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -836,12 +836,13 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
836 return 0; 836 return 0;
837 val &= mask; 837 val &= mask;
838 val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask; 838 val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
839 if (info->vol[ch] == val && !codec->in_resume) 839 if (info->vol[ch] == val)
840 return 0; 840 return 0;
841 put_vol_mute(codec, info, nid, ch, direction, idx, val); 841 put_vol_mute(codec, info, nid, ch, direction, idx, val);
842 return 1; 842 return 1;
843} 843}
844 844
845#ifdef CONFIG_PM
845/* resume the all amp commands from the cache */ 846/* resume the all amp commands from the cache */
846void snd_hda_codec_resume_amp(struct hda_codec *codec) 847void snd_hda_codec_resume_amp(struct hda_codec *codec)
847{ 848{
@@ -865,6 +866,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
865 } 866 }
866 } 867 }
867} 868}
869#endif /* CONFIG_PM */
868 870
869/* 871/*
870 * AMP control callbacks 872 * AMP control callbacks
@@ -1272,11 +1274,13 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
1272 change = codec->spdif_ctls != val; 1274 change = codec->spdif_ctls != val;
1273 codec->spdif_ctls = val; 1275 codec->spdif_ctls = val;
1274 1276
1275 if (change || codec->in_resume) { 1277 if (change) {
1276 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, 1278 snd_hda_codec_write_cache(codec, nid, 0,
1277 val & 0xff); 1279 AC_VERB_SET_DIGI_CONVERT_1,
1278 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, 1280 val & 0xff);
1279 val >> 8); 1281 snd_hda_codec_write_cache(codec, nid, 0,
1282 AC_VERB_SET_DIGI_CONVERT_2,
1283 val >> 8);
1280 } 1284 }
1281 1285
1282 mutex_unlock(&codec->spdif_mutex); 1286 mutex_unlock(&codec->spdif_mutex);
@@ -1307,17 +1311,19 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
1307 if (ucontrol->value.integer.value[0]) 1311 if (ucontrol->value.integer.value[0])
1308 val |= AC_DIG1_ENABLE; 1312 val |= AC_DIG1_ENABLE;
1309 change = codec->spdif_ctls != val; 1313 change = codec->spdif_ctls != val;
1310 if (change || codec->in_resume) { 1314 if (change) {
1311 codec->spdif_ctls = val; 1315 codec->spdif_ctls = val;
1312 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, 1316 snd_hda_codec_write_cache(codec, nid, 0,
1313 val & 0xff); 1317 AC_VERB_SET_DIGI_CONVERT_1,
1318 val & 0xff);
1314 /* unmute amp switch (if any) */ 1319 /* unmute amp switch (if any) */
1315 if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && 1320 if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
1316 (val & AC_DIG1_ENABLE)) 1321 (val & AC_DIG1_ENABLE)) {
1317 snd_hda_codec_write(codec, nid, 0, 1322 snd_hda_codec_amp_update(codec, nid, 0, HDA_OUTPUT, 0,
1318 AC_VERB_SET_AMP_GAIN_MUTE, 1323 0x80, 0x00);
1319 AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | 1324 snd_hda_codec_amp_update(codec, nid, 1, HDA_OUTPUT, 0,
1320 AC_AMP_SET_OUTPUT); 1325 0x80, 0x00);
1326 }
1321 } 1327 }
1322 mutex_unlock(&codec->spdif_mutex); 1328 mutex_unlock(&codec->spdif_mutex);
1323 return change; 1329 return change;
@@ -1409,10 +1415,10 @@ static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol,
1409 1415
1410 mutex_lock(&codec->spdif_mutex); 1416 mutex_lock(&codec->spdif_mutex);
1411 change = codec->spdif_in_enable != val; 1417 change = codec->spdif_in_enable != val;
1412 if (change || codec->in_resume) { 1418 if (change) {
1413 codec->spdif_in_enable = val; 1419 codec->spdif_in_enable = val;
1414 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, 1420 snd_hda_codec_write_cache(codec, nid, 0,
1415 val); 1421 AC_VERB_SET_DIGI_CONVERT_1, val);
1416 } 1422 }
1417 mutex_unlock(&codec->spdif_mutex); 1423 mutex_unlock(&codec->spdif_mutex);
1418 return change; 1424 return change;
@@ -1482,6 +1488,10 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
1482 return 0; 1488 return 0;
1483} 1489}
1484 1490
1491#ifdef CONFIG_PM
1492/*
1493 * command cache
1494 */
1485 1495
1486/* build a 32bit cache key with the widget id and the command parameter */ 1496/* build a 32bit cache key with the widget id and the command parameter */
1487#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid) 1497#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
@@ -1548,6 +1558,7 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
1548 snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb, 1558 snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
1549 seq->param); 1559 seq->param);
1550} 1560}
1561#endif /* CONFIG_PM */
1551 1562
1552/* 1563/*
1553 * set power state of the codec 1564 * set power state of the codec
@@ -2122,12 +2133,12 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
2122 2133
2123 mode = ucontrol->value.enumerated.item[0]; 2134 mode = ucontrol->value.enumerated.item[0];
2124 snd_assert(mode < num_chmodes, return -EINVAL); 2135 snd_assert(mode < num_chmodes, return -EINVAL);
2125 if (*max_channelsp == chmode[mode].channels && !codec->in_resume) 2136 if (*max_channelsp == chmode[mode].channels)
2126 return 0; 2137 return 0;
2127 /* change the current channel setting */ 2138 /* change the current channel setting */
2128 *max_channelsp = chmode[mode].channels; 2139 *max_channelsp = chmode[mode].channels;
2129 if (chmode[mode].sequence) 2140 if (chmode[mode].sequence)
2130 snd_hda_sequence_write(codec, chmode[mode].sequence); 2141 snd_hda_sequence_write_cache(codec, chmode[mode].sequence);
2131 return 1; 2142 return 1;
2132} 2143}
2133 2144
@@ -2160,10 +2171,10 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
2160 idx = ucontrol->value.enumerated.item[0]; 2171 idx = ucontrol->value.enumerated.item[0];
2161 if (idx >= imux->num_items) 2172 if (idx >= imux->num_items)
2162 idx = imux->num_items - 1; 2173 idx = imux->num_items - 1;
2163 if (*cur_val == idx && !codec->in_resume) 2174 if (*cur_val == idx)
2164 return 0; 2175 return 0;
2165 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, 2176 snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
2166 imux->items[idx].index); 2177 imux->items[idx].index);
2167 *cur_val = idx; 2178 *cur_val = idx;
2168 return 1; 2179 return 1;
2169} 2180}
@@ -2608,65 +2619,13 @@ int snd_hda_resume(struct hda_bus *bus)
2608 AC_PWRST_D0); 2619 AC_PWRST_D0);
2609 if (codec->patch_ops.resume) 2620 if (codec->patch_ops.resume)
2610 codec->patch_ops.resume(codec); 2621 codec->patch_ops.resume(codec);
2611 } 2622 else {
2612 return 0; 2623 codec->patch_ops.init(codec);
2613} 2624 snd_hda_codec_resume_amp(codec);
2614 2625 snd_hda_codec_resume_cache(codec);
2615/**
2616 * snd_hda_resume_ctls - resume controls in the new control list
2617 * @codec: the HDA codec
2618 * @knew: the array of struct snd_kcontrol_new
2619 *
2620 * This function resumes the mixer controls in the struct snd_kcontrol_new array,
2621 * originally for snd_hda_add_new_ctls().
2622 * The array must be terminated with an empty entry as terminator.
2623 */
2624int snd_hda_resume_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
2625{
2626 struct snd_ctl_elem_value *val;
2627
2628 val = kmalloc(sizeof(*val), GFP_KERNEL);
2629 if (!val)
2630 return -ENOMEM;
2631 codec->in_resume = 1;
2632 for (; knew->name; knew++) {
2633 int i, count;
2634 count = knew->count ? knew->count : 1;
2635 for (i = 0; i < count; i++) {
2636 memset(val, 0, sizeof(*val));
2637 val->id.iface = knew->iface;
2638 val->id.device = knew->device;
2639 val->id.subdevice = knew->subdevice;
2640 strcpy(val->id.name, knew->name);
2641 val->id.index = knew->index ? knew->index : i;
2642 /* Assume that get callback reads only from cache,
2643 * not accessing to the real hardware
2644 */
2645 if (snd_ctl_elem_read(codec->bus->card, val) < 0)
2646 continue;
2647 snd_ctl_elem_write(codec->bus->card, NULL, val);
2648 } 2626 }
2649 } 2627 }
2650 codec->in_resume = 0;
2651 kfree(val);
2652 return 0; 2628 return 0;
2653} 2629}
2654 2630
2655/**
2656 * snd_hda_resume_spdif_out - resume the digital out
2657 * @codec: the HDA codec
2658 */
2659int snd_hda_resume_spdif_out(struct hda_codec *codec)
2660{
2661 return snd_hda_resume_ctls(codec, dig_mixes);
2662}
2663
2664/**
2665 * snd_hda_resume_spdif_in - resume the digital in
2666 * @codec: the HDA codec
2667 */
2668int snd_hda_resume_spdif_in(struct hda_codec *codec)
2669{
2670 return snd_hda_resume_ctls(codec, dig_in_ctls);
2671}
2672#endif 2631#endif