[alsa-devel] [PATCH] hda: Advanced power management for codecs.
Added support for advanced power management features in new IDT codecs. Inactive ADCs and DACs are changed to D3 mode when not in playback or capture, also inactive line-out/headphone ports are disabled when jack detect does not sense a connection. --- Signed-off-by: Matthew Ranostay mranostay@embeddedalley.com
diff -r 5b03c0176aa5 pci/hda/patch_sigmatel.c --- a/pci/hda/patch_sigmatel.c Mon Jan 07 13:33:45 2008 +0100 +++ b/pci/hda/patch_sigmatel.c Mon Jan 07 11:22:56 2008 -0500 @@ -160,6 +160,7 @@ struct sigmatel_spec {
/* i/o switches */ unsigned int io_switch[2]; + unsigned int io_state[2]; unsigned int clfe_swap; unsigned int aloopback;
@@ -438,6 +439,52 @@ static int stac92xx_aloopback_put(struct kcontrol->private_value >> 16, dac_mode);
return 1; +} + +static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) +{ + int i; + + for (i = 0; i < spec->multiout.num_dacs; i++) { + if (spec->multiout.dac_nids[i] == nid) + return 1; + } + + return 0; +} + +static int stac92xx_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid = kcontrol->private_value & 0xffff; + int idx = (kcontrol->private_value >> 19) & 0xf; + int chs = (kcontrol->private_value >> 16) & 0x3; + long *valp = ucontrol->value.integer.value; + unsigned int wid_caps, val, change; + + change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!is_in_dac_nids(spec, nid)) + nid = spec->adc_nids[idx]; + + val = *valp++; + wid_caps = get_wcaps(codec, nid); + if (wid_caps & AC_WCAP_POWER) { + if (chs == 3) + val |= *valp; + else { /* Center/LFE mixers workaround */ + spec->io_state[--chs] = val; + val |= spec->io_state[chs ? 0 : 1]; + } + + /* set to power state D3 mode if DAC/ADC isn't being used */ + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, + val ? 0: AC_PWRST_D3); + } +#endif + return change; }
static struct hda_verb stac9200_core_init[] = { @@ -622,6 +669,20 @@ static struct hda_verb stac9205_core_ini .private_value = verb_read | (verb_write << 16), \ }
+#define STAC_CODEC_MUTE_IDX(xname, xcidx, nid, xindex, direction) \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xcidx, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = stac92xx_mixer_amp_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, 3, xindex, direction), \ + } + +#define STAC_CODEC_MUTE(xname, nid, xindex, direction) \ + STAC_CODEC_MUTE_IDX(xname, 0, nid, xindex, direction) + static struct snd_kcontrol_new stac9200_mixer[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT), @@ -640,10 +701,10 @@ static struct snd_kcontrol_new stac92hd7 HDA_CODEC_VOLUME_IDX("Digital Mic Volume", 0x1, 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), + STAC_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x1, HDA_OUTPUT), + STAC_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x1, HDA_OUTPUT),
HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), @@ -670,10 +731,10 @@ static struct snd_kcontrol_new stac92hd7 HDA_CODEC_VOLUME_IDX("Digital Mic Volume", 0x1, 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), + STAC_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x1, HDA_OUTPUT), + STAC_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x1, HDA_OUTPUT),
HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), @@ -700,10 +761,10 @@ static struct snd_kcontrol_new stac92hd7 HDA_CODEC_VOLUME_IDX("Digital Mic Volume", 0x1, 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), + STAC_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x1, HDA_OUTPUT), + STAC_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x1, HDA_OUTPUT),
HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), @@ -730,12 +791,12 @@ static struct snd_kcontrol_new stac92hd7 HDA_CODEC_VOLUME_IDX("Digital Mic Volume", 0x1, 0x19, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), + STAC_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x1, HDA_OUTPUT), + STAC_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x1, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x1, HDA_OUTPUT),
HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT), HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT), @@ -751,12 +812,12 @@ static struct snd_kcontrol_new stac92hd7 HDA_CODEC_VOLUME_IDX("Digital Mic Volume", 0x1, 0x19, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), + STAC_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x1, HDA_OUTPUT), + STAC_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x1, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x1, HDA_OUTPUT), { } /* end */ };
@@ -857,7 +918,7 @@ static int stac92xx_build_controls(struc if (err < 0) return err; } - return 0; + return 0; }
static unsigned int ref9200_pin_configs[8] = { @@ -1962,7 +2023,7 @@ enum {
static struct snd_kcontrol_new stac92xx_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), + STAC_CODEC_MUTE(NULL, 0, 0, 0), STAC_CODEC_IO_SWITCH(NULL, 0), STAC_CODEC_CLFE_SWITCH(NULL, 0), }; @@ -2059,19 +2120,6 @@ static int stac92xx_add_dyn_out_pins(str return 0; }
- -static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) -{ - int i; - - for (i = 0; i < spec->multiout.num_dacs; i++) { - if (spec->multiout.dac_nids[i] == nid) - return 1; - } - - return 0; -} - /* * Fill in the dac_nids table from the parsed pin configuration * This function only works when every pin in line_out_pins[] @@ -2806,6 +2854,7 @@ static void stac92xx_hp_detect(struct hd struct sigmatel_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int i, presence; + unsigned char pwr_state = 0;
presence = 0; for (i = 0; i < cfg->hp_outs; i++) { @@ -2816,12 +2865,16 @@ static void stac92xx_hp_detect(struct hd
if (presence) { /* disable lineouts, enable hp */ - for (i = 0; i < cfg->line_outs; i++) + for (i = 0; i < cfg->line_outs; i++) { stac92xx_reset_pinctl(codec, cfg->line_out_pins[i], AC_PINCTL_OUT_EN); - for (i = 0; i < cfg->speaker_outs; i++) + pwr_state |= 1 << (cfg->line_out_pins[i] - 0xa); + } + for (i = 0; i < cfg->speaker_outs; i++) { stac92xx_reset_pinctl(codec, cfg->speaker_pins[i], AC_PINCTL_OUT_EN); + pwr_state |= 1 << (cfg->speaker_pins[i] - 0xa); + } } else { /* enable lineouts, disable hp */ for (i = 0; i < cfg->line_outs; i++) @@ -2830,7 +2883,12 @@ static void stac92xx_hp_detect(struct hd for (i = 0; i < cfg->speaker_outs; i++) stac92xx_set_pinctl(codec, cfg->speaker_pins[i], AC_PINCTL_OUT_EN); - } + for (i = 0; i < cfg->hp_outs; i++) + pwr_state |= 1 << (cfg->hp_pins[i] - 0xa); + } + + /* power down unused ports */ + snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, pwr_state); }
static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
At Mon, 07 Jan 2008 11:44:08 -0500, Matthew Ranostay wrote:
[1 <text/plain; ISO-8859-1 (7bit)>] Added support for advanced power management features in new IDT codecs. Inactive ADCs and DACs are changed to D3 mode when not in playback or capture, also inactive line-out/headphone ports are disabled when jack detect does not sense a connection.
Thanks. This is an interesting patch.
Signed-off-by: Matthew Ranostay mranostay@embeddedalley.com [2 stac92hd7xxx_power_save.patch <text/plain (7bit)>] diff -r 5b03c0176aa5 pci/hda/patch_sigmatel.c --- a/pci/hda/patch_sigmatel.c Mon Jan 07 13:33:45 2008 +0100 +++ b/pci/hda/patch_sigmatel.c Mon Jan 07 11:22:56 2008 -0500 @@ -160,6 +160,7 @@ struct sigmatel_spec {
/* i/o switches */ unsigned int io_switch[2];
- unsigned int io_state[2]; unsigned int clfe_swap; unsigned int aloopback;
@@ -438,6 +439,52 @@ static int stac92xx_aloopback_put(struct kcontrol->private_value >> 16, dac_mode);
return 1; +}
+static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) +{
- int i;
- for (i = 0; i < spec->multiout.num_dacs; i++) {
if (spec->multiout.dac_nids[i] == nid)
return 1;
- }
- return 0;
+}
+static int stac92xx_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- int idx = (kcontrol->private_value >> 19) & 0xf;
- int chs = (kcontrol->private_value >> 16) & 0x3;
- long *valp = ucontrol->value.integer.value;
- unsigned int wid_caps, val, change;
- change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!is_in_dac_nids(spec, nid))
nid = spec->adc_nids[idx];
- val = *valp++;
- wid_caps = get_wcaps(codec, nid);
- if (wid_caps & AC_WCAP_POWER) {
if (chs == 3)
val |= *valp;
else { /* Center/LFE mixers workaround */
spec->io_state[--chs] = val;
val |= spec->io_state[chs ? 0 : 1];
}
/* set to power state D3 mode if DAC/ADC isn't being used */
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
val ? 0: AC_PWRST_D3);
- }
+#endif
- return change;
This stuff should go rather to snd_hda_mixer_amp_switch_put(). It's not specific to STAC codecs.
The #ifdef doesn't suffice, BTW. The power-saving is a dynamic switch (via module parameter and/or sysfs), so you need to check whether power-saving mode is active nor not. Also, these calls should be within the snd_hda_power_up/down() block.
@@ -2830,7 +2883,12 @@ static void stac92xx_hp_detect(struct hd for (i = 0; i < cfg->speaker_outs; i++) stac92xx_set_pinctl(codec, cfg->speaker_pins[i], AC_PINCTL_OUT_EN);
- }
for (i = 0; i < cfg->hp_outs; i++)
pwr_state |= 1 << (cfg->hp_pins[i] - 0xa);
- }
- /* power down unused ports */
- snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, pwr_state);
Is this verb available on every STAC codec? I took a look at some specs but they don't show this verb at all.
Takashi
participants (2)
-
Matthew Ranostay
-
Takashi Iwai