Headphone volume adjustment

James Cameron quozl at laptop.org
Thu Jul 4 05:05:18 EDT 2013


On Tue, Jun 25, 2013 at 01:57:28AM -0400, John Watlington wrote:
> We have noticed that under Fedora 18 (13.2.0 os10 on XO-4), the
> headphone volume can't be adjusted to complete silence.

I've looked into this, and have a tentative patch.  I'd like it
reviewed to see if the general direction is good.  It works for me.
Once the volume falls below a minimum value the audio is muted.

Now, one would think that the mute bits should work, but they did not;
still had audio in headphones after being sure the mute bits in the
headphone volume register were set.  Perhaps the documentation is
wrong.  So I've ended up using the enable bits for the headphone
volume section.

Also, since we were exposing the mute bits to the layer above, the
mute control was changed to be virtual, and we manage the register
mute bits within the driver instead of exposing them.

This preserves the function of the mute control at the ALSA level
despite our now fiddling with the bits underneath.

Tested here.

diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 399e5c4..2b44bea 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -94,7 +94,7 @@ struct rt5631_priv {
 	int rx_rate;
 	int bclk_rate;
 	int dmic_used_flag;
-	int spkr_mute;		/* saved speaker mute at headphone plug time */
+	int mute;		/* user level mute */
 	int hp;			/* headphones are plugged */
 };
 
@@ -348,16 +348,22 @@ static int snd_soc_put_vol_rt5631(struct snd_kcontrol *kcontrol,
 				    struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
 	unsigned short val, val2, val_mask;
+	unsigned short mutes = (RT5631_L_MUTE | RT5631_R_MUTE);
 	int ret;
 
 	/* Invert so that 0 means +12dB, 39 means -46.5 dB */
 	val = 39 - ucontrol->value.integer.value[0];
 	pr_debug("%s: %d (codec) <- %ld (control)\n", __func__,
-	       val & 0x3f, ucontrol->value.integer.value[0]);
+	       val, ucontrol->value.integer.value[0]);
 
 	val2 = (val << 8) | val;
 	val_mask = 0x3f3f;
+	if (rt5631->mute || rt5631->hp) {
+		val2 |= mutes;
+		val_mask |= mutes;
+	}
 
 	ret = snd_soc_update_bits(codec, RT5631_SPK_OUT_VOL, val_mask, val2);
 
@@ -365,11 +371,19 @@ static int snd_soc_put_vol_rt5631(struct snd_kcontrol *kcontrol,
 	/* for the fact that the HP range is -46.5dB (31) to 0dB (0) */
 	/* We truncate at the low-volume end of the range, so that high */
 	/* volume settings have distinct steps for both SPK and HP. */
-	if (val > 31)
-		val = 31;
-
-	val2 = (val << 8) | val;
-	val_mask = 0x1f1f;
+	unsigned short en = (RT5631_L_EN | RT5631_R_EN);
+	val_mask = 0x1f1f | en;
+	if (val > 31) {
+		val2 = 0x1f1f;
+	} else {
+		val2 = (val << 8) | val | en;
+	}
+	if (rt5631->mute) {
+		val2 &= ~en;
+		val_mask &= ~en;
+	}
+	pr_err("rt5631pv: %x/%x (regs) <- %d (codec)\n",
+	       val2, val_mask, val);
 
 	return snd_soc_update_bits(codec, RT5631_HP_OUT_VOL, val_mask, val2);
 }
@@ -379,29 +393,38 @@ static int snd_soc_get_volsw_rt5631(struct snd_kcontrol *kcontrol,
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
-	int reg;
-	unsigned short val;
 
-	reg = rt5631->hp ? RT5631_HP_OUT_VOL : RT5631_SPK_OUT_VOL;
-	val = snd_soc_read(codec, reg) & (RT5631_L_MUTE | RT5631_R_MUTE);
-	ucontrol->value.enumerated.item[0] = val ? 0 : 1;
+	ucontrol->value.enumerated.item[0] = rt5631->mute ? 0 : 1;
 
 	pr_debug("%s: %d (control) <- (codec)\n", __func__,
 	       ucontrol->value.enumerated.item[0]);
 	return 0;
 }
 
+static void update_mutes(struct snd_soc_codec *codec, struct rt5631_priv *rt5631)
+{
+	unsigned short hp_out_vol, spk_out_vol;
+	unsigned short mutes = RT5631_L_MUTE | RT5631_R_MUTE;
+
+	if (!rt5631->hp) {
+		spk_out_vol = rt5631->mute ? mutes : 0;
+		hp_out_vol = mutes;
+	} else {
+		spk_out_vol = mutes;
+		hp_out_vol = rt5631->mute ? mutes : 0;
+	}
+	snd_soc_update_bits(codec, RT5631_HP_OUT_VOL, mutes, hp_out_vol);
+	snd_soc_update_bits(codec, RT5631_SPK_OUT_VOL, mutes, spk_out_vol);
+}
+
 static int snd_soc_put_volsw_rt5631(struct snd_kcontrol *kcontrol,
 				    struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
-	int reg;
-	unsigned short val, val_mask = RT5631_L_MUTE | RT5631_R_MUTE;
 
-	reg = rt5631->hp ? RT5631_HP_OUT_VOL : RT5631_SPK_OUT_VOL;
-	val = ucontrol->value.enumerated.item[0] ? 0 : val_mask;
-	snd_soc_update_bits(codec, reg, val_mask, val);
+	rt5631->mute = ucontrol->value.enumerated.item[0] ? 0 : 1;
+	update_mutes(codec, rt5631);
 
 	pr_debug("%s: %d (control) -> (codec)\n", __func__,
 	       ucontrol->value.enumerated.item[0]);
@@ -2009,23 +2032,12 @@ static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
 }
 
 /* Used by olpc-xo-1.75.c */
-int rt5631_spkr_mute(struct snd_soc_codec *codec, int mute)
+int rt5631_spkr_mute(struct snd_soc_codec *codec, int hp)
 {
 	struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
 
-	if (mute) {
-		rt5631->spkr_mute = snd_soc_read(codec, RT5631_SPK_OUT_VOL);
-		snd_soc_update_bits(codec, RT5631_SPK_OUT_VOL,
-				RT5631_L_MUTE | RT5631_R_MUTE,
-				RT5631_L_MUTE | RT5631_R_MUTE);
-	} else {
-		if (!rt5631->spkr_mute)
-			rt5631->spkr_mute = 0;
-		snd_soc_update_bits(codec, RT5631_SPK_OUT_VOL,
-				RT5631_L_MUTE | RT5631_R_MUTE,
-				rt5631->spkr_mute);
-	}
-	rt5631->hp = mute;
+	rt5631->hp = hp;
+	update_mutes(codec, rt5631);
 
 	msleep(100);
 	return 0;

-- 
James Cameron
http://quozl.linux.org.au/



More information about the Devel mailing list