All of lore.kernel.org
 help / color / mirror / Atom feed
From: Hans de Goede <hdegoede@redhat.com>
To: Cezary Rojewski <cezary.rojewski@intel.com>,
	Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>,
	Liam Girdwood <lgirdwood@gmail.com>,
	Jie Yang <yang.jie@linux.intel.com>,
	Mark Brown <broonie@kernel.org>
Cc: Oder Chiou <oder_chiou@realtek.com>,
	Hans de Goede <hdegoede@redhat.com>,
	alsa-devel@alsa-project.org
Subject: [PATCH 3/5] ASoC: rt5640: Add emulated 'DAC1 Playback Switch' control
Date: Fri, 26 Feb 2021 15:38:15 +0100	[thread overview]
Message-ID: <20210226143817.84287-4-hdegoede@redhat.com> (raw)
In-Reply-To: <20210226143817.84287-1-hdegoede@redhat.com>

When using AIF1 the 'DAC1 Playback Volume' control will be used as the
PlaybackMasterElem in UCM.

We need a matching 'DAC1 Playback Switch' for 2 reasons:

1. To be able to truely fully mute the output (the softest volume setting
   is not fully muted).
2. For reliable output-mute LED control.

The path from the IF1_DAC data input to the 'Stereo DAC MIXL' /
'Stereo DAC MIXR' digital mixer has a 'DAC MIXL' / 'DAC MIXR' digital
mixer with IF1_DAC data as one of its inputs direclty after the
'DAC1 Playback Volume' control.

This commit adds an emulated "DAC1 Playback Switch" control by:

1. Declaring the enable flag for the mixers IF1_DAC input as well as the
"DAC1 Playback Switch" control both as SND_SOC_NOPM controls.

2. Storing the settings of both controls as driver-private data.

3. Only clearing the mute flag for the IF1_DAC input of that mixer if the
stored values indicate both controls are enabled.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 sound/soc/codecs/rt5640.c | 96 +++++++++++++++++++++++++++++++++++++--
 sound/soc/codecs/rt5640.h |  4 ++
 2 files changed, 96 insertions(+), 4 deletions(-)

diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index a5674c227b3a..c143ca174921 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -378,6 +378,56 @@ static const char * const rt5640_clsd_spk_ratio[] = {"1.66x", "1.83x", "1.94x",
 static SOC_ENUM_SINGLE_DECL(rt5640_clsd_spk_ratio_enum, RT5640_CLS_D_OUT,
 			    RT5640_CLSD_RATIO_SFT, rt5640_clsd_spk_ratio);
 
+/*
+ * For reliable output-mute LED control we need a "DAC1 Playback Switch" control.
+ * We emulate this by only clearing the RT5640_M_IF1_DAC_L/_R AD_DA_MIXER register
+ * bits when both our emulated DAC1 Playback Switch control and the DAC1 MIXL/R
+ * DAPM-mixer DAC1 input are enabled.
+ */
+static void rt5640_update_ad_da_mixer_if1_dac_m_bits(struct rt5640_priv *rt5640)
+{
+	int val = RT5640_M_IF1_DAC_L | RT5640_M_IF1_DAC_R;
+
+	if (rt5640->dac1_mixl_if1_switch && rt5640->dac1_playback_switch_l)
+		val &= ~RT5640_M_IF1_DAC_L;
+
+	if (rt5640->dac1_mixr_if1_switch && rt5640->dac1_playback_switch_r)
+		val &= ~RT5640_M_IF1_DAC_R;
+
+	regmap_update_bits(rt5640->regmap, RT5640_AD_DA_MIXER,
+			   RT5640_M_IF1_DAC_L | RT5640_M_IF1_DAC_R, val);
+}
+
+static int rt5640_dac1_playback_switch_get(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.integer.value[0] = rt5640->dac1_playback_switch_l;
+	ucontrol->value.integer.value[1] = rt5640->dac1_playback_switch_r;
+
+	return 0;
+}
+
+static int rt5640_dac1_playback_switch_put(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+	if (rt5640->dac1_playback_switch_l == ucontrol->value.integer.value[0] &&
+	    rt5640->dac1_playback_switch_r == ucontrol->value.integer.value[1])
+		return 0;
+
+	rt5640->dac1_playback_switch_l = ucontrol->value.integer.value[0];
+	rt5640->dac1_playback_switch_r = ucontrol->value.integer.value[1];
+
+	rt5640_update_ad_da_mixer_if1_dac_m_bits(rt5640);
+
+	return 1;
+}
+
 static const struct snd_kcontrol_new rt5640_snd_controls[] = {
 	/* Speaker Output Volume */
 	SOC_DOUBLE("Speaker Channel Switch", RT5640_SPK_VOL,
@@ -400,6 +450,8 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
 	/* DAC Digital Volume */
 	SOC_DOUBLE("DAC2 Playback Switch", RT5640_DAC2_CTRL,
 		RT5640_M_DAC_L2_VOL_SFT, RT5640_M_DAC_R2_VOL_SFT, 1, 1),
+	SOC_DOUBLE_EXT("DAC1 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+			rt5640_dac1_playback_switch_get, rt5640_dac1_playback_switch_put),
 	SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL,
 			RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
 			175, 0, dac_vol_tlv),
@@ -515,18 +567,44 @@ static const struct snd_kcontrol_new rt5640_mono_adc_r_mix[] = {
 			RT5640_M_MONO_ADC_R2_SFT, 1, 1),
 };
 
+/* See comment above rt5640_update_ad_da_mixer_if1_dac_m_bits() */
+static int rt5640_put_dac1_mix_if1_switch(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
+	struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+	int ret;
+
+	if (mc->shift == 0)
+		rt5640->dac1_mixl_if1_switch = ucontrol->value.integer.value[0];
+	else
+		rt5640->dac1_mixr_if1_switch = ucontrol->value.integer.value[0];
+
+	/* Apply the update (if any) */
+	ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+	if (ret == 0)
+		return 0;
+
+	rt5640_update_ad_da_mixer_if1_dac_m_bits(rt5640);
+
+	return 1;
+}
+
+#define SOC_DAPM_SINGLE_RT5640_IF1_SW(name, shift) \
+	SOC_SINGLE_EXT(name, SND_SOC_NOPM, shift, 1, 0, \
+		       snd_soc_dapm_get_volsw, rt5640_put_dac1_mix_if1_switch)
+
 static const struct snd_kcontrol_new rt5640_dac_l_mix[] = {
 	SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER,
 			RT5640_M_ADCMIX_L_SFT, 1, 1),
-	SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER,
-			RT5640_M_IF1_DAC_L_SFT, 1, 1),
+	SOC_DAPM_SINGLE_RT5640_IF1_SW("INF1 Switch", 0),
 };
 
 static const struct snd_kcontrol_new rt5640_dac_r_mix[] = {
 	SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER,
 			RT5640_M_ADCMIX_R_SFT, 1, 1),
-	SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER,
-			RT5640_M_IF1_DAC_R_SFT, 1, 1),
+	SOC_DAPM_SINGLE_RT5640_IF1_SW("INF1 Switch", 1),
 };
 
 static const struct snd_kcontrol_new rt5640_sto_dac_l_mix[] = {
@@ -2831,6 +2909,16 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
 	INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work);
 	INIT_WORK(&rt5640->jack_work, rt5640_jack_work);
 
+	/*
+	 * Enable the emulated "DAC1 Playback Switch" by default to avoid
+	 * muting the output with older UCM profiles.
+	 */
+	rt5640->dac1_playback_switch_l = true;
+	rt5640->dac1_playback_switch_r = true;
+	/* The Power-On-Reset values for the DAC1 mixer have the INF1 input enabled. */
+	rt5640->dac1_mixl_if1_switch = true;
+	rt5640->dac1_mixr_if1_switch = true;
+
 	/* Make sure work is stopped on probe-error / remove */
 	ret = devm_add_action_or_reset(&i2c->dev, rt5640_cancel_work, rt5640);
 	if (ret)
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index 4fd47f2b936b..0d029f5dbb61 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -2135,6 +2135,10 @@ struct rt5640_priv {
 
 	bool hp_mute;
 	bool asrc_en;
+	bool dac1_mixl_if1_switch;
+	bool dac1_mixr_if1_switch;
+	bool dac1_playback_switch_l;
+	bool dac1_playback_switch_r;
 
 	/* Jack and button detect data */
 	bool ovcd_irq_enabled;
-- 
2.30.1


  parent reply	other threads:[~2021-02-26 14:41 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-26 14:38 [PATCH 0/5] AsoC: rt5640/rt5651: Volume control fixes Hans de Goede
2021-02-26 14:38 ` [PATCH 1/5] ASoC: rt5640: Fix dac- and adc- vol-tlv values being off by a factor of 10 Hans de Goede
2021-02-26 14:38 ` [PATCH 2/5] ASoC: rt5651: " Hans de Goede
2021-02-26 14:38 ` Hans de Goede [this message]
2021-03-01 18:55   ` [PATCH 3/5] ASoC: rt5640: Add emulated 'DAC1 Playback Switch' control Mark Brown
2021-03-01 19:21     ` Hans de Goede
2021-03-01 19:39       ` Hans de Goede
2021-03-01 20:19       ` Mark Brown
2021-02-26 14:38 ` [PATCH 4/5] ASoC: rt5640: Rename 'Mono DAC Playback Volume' to 'DAC2 Playback Volume' Hans de Goede
2021-02-26 14:38 ` [PATCH 5/5] ASoC: Intel: bytcr_rt5640: Add used AIF to the components string Hans de Goede
2021-03-01 23:34 ` (subset) [PATCH 0/5] AsoC: rt5640/rt5651: Volume control fixes Mark Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210226143817.84287-4-hdegoede@redhat.com \
    --to=hdegoede@redhat.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    --cc=cezary.rojewski@intel.com \
    --cc=lgirdwood@gmail.com \
    --cc=oder_chiou@realtek.com \
    --cc=pierre-louis.bossart@linux.intel.com \
    --cc=yang.jie@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.