Skip to contentSkip to conversation

hda_codec: Added power management for ADC/DAC's

PATCH In review Matthew Ranostay 2 participants 2 messages Jan 18, 2008 – Jan 19, 2008

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 hda_codec: Added power management for ADC/DAC's

Added power management support to muted DACs and ADCs. They are set into D3 state when both channels are muted, and D0 when unmuted. We specify the ADC in the HDA_CODEC_MUTE_IDX instead of the ADC mux, so that we can easily know which node to power manage, and we use the connection list to get the needed mux node.

---

pci/hda/hda_codec.c +38 -2
diff -r 1cb5fe1b2443 pci/hda/hda_codec.c
@@ -1091,11 +1091,19 @@ int snd_hda_mixer_amp_switch_get(struct 
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = get_amp_nid(kcontrol);
+	hda_nid_t conn_list[1];
 	int chs = get_amp_channels(kcontrol);
 	int dir = get_amp_direction(kcontrol);
 	int idx = get_amp_index(kcontrol);
+	int wcaps = get_wcaps(codec, nid);
+	int wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
 	long *valp = ucontrol->value.integer.value;
 
+	/* if pin is a adc get the mux connection */
+	if (wtype == AC_WID_AUD_IN) {
+		snd_hda_get_connections(codec, nid, conn_list, 1);
+		nid = conn_list[0];
+	}
 	if (chs & 1)
 		*valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) &
 			   HDA_AMP_MUTE) ? 0 : 1;
@@ -1109,24 +1117,52 @@ int snd_hda_mixer_amp_switch_put(struct 
 				 struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t ctl_nid = get_amp_nid(kcontrol);
 	hda_nid_t nid = get_amp_nid(kcontrol);
+	hda_nid_t conn_list[1];
 	int chs = get_amp_channels(kcontrol);
 	int dir = get_amp_direction(kcontrol);
 	int idx = get_amp_index(kcontrol);
+	int wcaps = get_wcaps(codec, nid);
+	int wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
 	long *valp = ucontrol->value.integer.value;
-	int change = 0;
+	int change = 0, val = 0;
 
 	snd_hda_power_up(codec);
+	/* if pin is a adc get the mux connection */
+	if (wtype == AC_WID_AUD_IN) {
+		snd_hda_get_connections(codec, nid, conn_list, 1);
+		nid = conn_list[0];
+	}
 	if (chs & 1) {
 		change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
 						  HDA_AMP_MUTE,
 						  *valp ? 0 : HDA_AMP_MUTE);
+		val = !*valp;
 		valp++;
 	}
-	if (chs & 2)
+	if (chs & 2) {
 		change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
 						   HDA_AMP_MUTE,
 						   *valp ? 0 : HDA_AMP_MUTE);
+		val &= !*valp;
+	}
+
+	/* check for power management support */
+	if ((wcaps & AC_WCAP_POWER) && (wcaps != AC_WID_AUD_MIX)) {
+		if ((chs != 3) && (wcaps & AC_WCAP_STEREO)) {
+			struct hda_amp_info *info;
+			info = get_alloc_amp_hash(codec,
+						HDA_HASH_KEY(nid, dir, idx));
+			/* get the other channel value */
+			val &= !(get_vol_mute(codec, info, nid,
+					~chs & 3, dir, idx) & HDA_AMP_MUTE);
+		}
+		/* power down adc/dac if both channels are muted */
+		snd_hda_codec_write(codec, ctl_nid, 0, AC_VERB_SET_POWER_STATE,
+							val ? AC_PWRST_D3 : 0);
+	}
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	if (codec->patch_ops.check_power_status)
 		codec->patch_ops.check_power_status(codec, nid);
pci/hda/patch_sigmatel.c +10 -10
diff -r 1cb5fe1b2443 pci/hda/patch_sigmatel.c
@@ -638,10 +638,10 @@ static struct snd_kcontrol_new stac92hd7
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1a, 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),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1b, 0x0, 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),
@@ -664,10 +664,10 @@ static struct snd_kcontrol_new stac92hd7
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1a, 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),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1b, 0x0, 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),
@@ -690,10 +690,10 @@ static struct snd_kcontrol_new stac92hd7
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1a, 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),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1b, 0x0, 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),
@@ -716,11 +716,11 @@ static struct snd_kcontrol_new stac92hd7
 	STAC_INPUT_SOURCE(2),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x12, 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_MUTE_IDX("Capture Switch", 0x1, 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
 
 	HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),
@@ -735,11 +735,11 @@ static struct snd_kcontrol_new stac92hd7
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x12, 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_MUTE_IDX("Capture Switch", 0x1, 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
 
 	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x14, 0x1, 0, HDA_INPUT),
#1 lore raw

At Fri, 18 Jan 2008 17:15:52 -0500,

Quoted 4 lines · Matthew Ranostay

Added power management support to muted DACs and ADCs. They are set into D3 state when both channels are muted, and D0 when unmuted. We specify the ADC in the HDA_CODEC_MUTE_IDX instead of the ADC mux, so that we can easily know which node to power manage, and we use the connection list to get the needed mux node.

Hmm, I'm afraid this could break the codes for other codecs. IIRC, some codecs use the audio-input widget itself as the input mux.

Besides, I'd like to keep this power-saving feature as an option, preferably dynamically activated. In that way, we can check easily whether any problem lies on the power-saving feature or not.

I feel it can be better put after the merge window of 2.6.25, in order to avoid a bug report flood :) But, let's keep discussing.

Thanks,

Takashi

Quoted 150 lines

---
diff -r 1cb5fe1b2443 pci/hda/hda_codec.c

pci/hda/hda_codec.c +38 -2
@@ -1091,11 +1091,19 @@ int snd_hda_mixer_amp_switch_get(struct 
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = get_amp_nid(kcontrol);
+	hda_nid_t conn_list[1];
 	int chs = get_amp_channels(kcontrol);
 	int dir = get_amp_direction(kcontrol);
 	int idx = get_amp_index(kcontrol);
+	int wcaps = get_wcaps(codec, nid);
+	int wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
 	long *valp = ucontrol->value.integer.value;
 
+	/* if pin is a adc get the mux connection */
+	if (wtype == AC_WID_AUD_IN) {
+		snd_hda_get_connections(codec, nid, conn_list, 1);
+		nid = conn_list[0];
+	}
 	if (chs & 1)
 		*valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) &
 			   HDA_AMP_MUTE) ? 0 : 1;
@@ -1109,24 +1117,52 @@ int snd_hda_mixer_amp_switch_put(struct 
 				 struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t ctl_nid = get_amp_nid(kcontrol);
 	hda_nid_t nid = get_amp_nid(kcontrol);
+	hda_nid_t conn_list[1];
 	int chs = get_amp_channels(kcontrol);
 	int dir = get_amp_direction(kcontrol);
 	int idx = get_amp_index(kcontrol);
+	int wcaps = get_wcaps(codec, nid);
+	int wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
 	long *valp = ucontrol->value.integer.value;
-	int change = 0;
+	int change = 0, val = 0;
 
 	snd_hda_power_up(codec);
+	/* if pin is a adc get the mux connection */
+	if (wtype == AC_WID_AUD_IN) {
+		snd_hda_get_connections(codec, nid, conn_list, 1);
+		nid = conn_list[0];
+	}
 	if (chs & 1) {
 		change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
 						  HDA_AMP_MUTE,
 						  *valp ? 0 : HDA_AMP_MUTE);
+		val = !*valp;
 		valp++;
 	}
-	if (chs & 2)
+	if (chs & 2) {
 		change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
 						   HDA_AMP_MUTE,
 						   *valp ? 0 : HDA_AMP_MUTE);
+		val &= !*valp;
+	}
+
+	/* check for power management support */
+	if ((wcaps & AC_WCAP_POWER) && (wcaps != AC_WID_AUD_MIX)) {
+		if ((chs != 3) && (wcaps & AC_WCAP_STEREO)) {
+			struct hda_amp_info *info;
+			info = get_alloc_amp_hash(codec,
+						HDA_HASH_KEY(nid, dir, idx));
+			/* get the other channel value */
+			val &= !(get_vol_mute(codec, info, nid,
+					~chs & 3, dir, idx) & HDA_AMP_MUTE);
+		}
+		/* power down adc/dac if both channels are muted */
+		snd_hda_codec_write(codec, ctl_nid, 0, AC_VERB_SET_POWER_STATE,
+							val ? AC_PWRST_D3 : 0);
+	}
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	if (codec->patch_ops.check_power_status)
 		codec->patch_ops.check_power_status(codec, nid);

diff -r 1cb5fe1b2443 pci/hda/patch_sigmatel.c

pci/hda/patch_sigmatel.c +10 -10
@@ -638,10 +638,10 @@ static struct snd_kcontrol_new stac92hd7
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1a, 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),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1b, 0x0, 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),
@@ -664,10 +664,10 @@ static struct snd_kcontrol_new stac92hd7
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1a, 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),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1b, 0x0, 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),
@@ -690,10 +690,10 @@ static struct snd_kcontrol_new stac92hd7
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1a, 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),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1b, 0x0, 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),
@@ -716,11 +716,11 @@ static struct snd_kcontrol_new stac92hd7
 	STAC_INPUT_SOURCE(2),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x12, 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_MUTE_IDX("Capture Switch", 0x1, 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
 
 	HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),
@@ -735,11 +735,11 @@ static struct snd_kcontrol_new stac92hd7
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x12, 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_MUTE_IDX("Capture Switch", 0x1, 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
 
 	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x14, 0x1, 0, HDA_INPUT),