linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sameer Pujar <spujar@nvidia.com>
To: <broonie@kernel.org>, <lgirdwood@gmail.com>, <perex@perex.cz>,
	<tiwai@suse.com>
Cc: <jonathanh@nvidia.com>, <thierry.reding@gmail.com>,
	<alsa-devel@alsa-project.org>, <linux-tegra@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, Sameer Pujar <spujar@nvidia.com>
Subject: [PATCH] ASoC: tegra: Add master volume/mute control support
Date: Mon, 25 Oct 2021 16:36:16 +0530	[thread overview]
Message-ID: <1635159976-17355-1-git-send-email-spujar@nvidia.com> (raw)

The MVC module has a per channel control bit, based on which it decides
to apply channel specific volume/mute settings. When per channel control
bit is enabled (which is the default HW configuration), all MVC channel
volume/mute can be independently controlled. If the control is disabled,
channel-0 volume/mute setting is applied by HW to all remaining channels.
Thus add support to leverage this HW feature by exposing master controls
for volume/mute.

With this, now there are per channel and master volume/mute controls.
Users need to just use controls which are suitable for their applications.
The per channel control enable/disable is mananged in driver and hidden
from users, so that they need to just worry about respective volume/mute
controls.

Signed-off-by: Sameer Pujar <spujar@nvidia.com>
---
 sound/soc/tegra/tegra210_mvc.c | 95 +++++++++++++++++++++++++++++++++++++-----
 sound/soc/tegra/tegra210_mvc.h |  2 +
 2 files changed, 87 insertions(+), 10 deletions(-)

diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c
index 7b9c700..40cd21a 100644
--- a/sound/soc/tegra/tegra210_mvc.c
+++ b/sound/soc/tegra/tegra210_mvc.c
@@ -123,7 +123,42 @@ static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol,
 	mute_mask = (val >>  TEGRA210_MVC_MUTE_SHIFT) &
 		TEGRA210_MUTE_MASK_EN;
 
-	ucontrol->value.integer.value[0] = mute_mask;
+	if (strstr(kcontrol->id.name, "Per Chan Mute Mask")) {
+		/*
+		 * If per channel control is enabled, then return
+		 * exact mute/unmute setting of all channels.
+		 *
+		 * Else report setting based on CH0 bit to reflect
+		 * the correct HW state.
+		 */
+		if (val & TEGRA210_MVC_PER_CHAN_CTRL_EN) {
+			ucontrol->value.integer.value[0] = mute_mask;
+		} else {
+			if (mute_mask & TEGRA210_MVC_CH0_MUTE_EN)
+				ucontrol->value.integer.value[0] =
+					TEGRA210_MUTE_MASK_EN;
+			else
+				ucontrol->value.integer.value[0] = 0;
+		}
+	} else {
+		/*
+		 * If per channel control is disabled, then return
+		 * master mute/unmute setting based on CH0 bit.
+		 *
+		 * Else report settings based on state of all
+		 * channels.
+		 */
+		if (!(val & TEGRA210_MVC_PER_CHAN_CTRL_EN)) {
+			ucontrol->value.integer.value[0] =
+				mute_mask & TEGRA210_MVC_CH0_MUTE_EN;
+		} else {
+			if (mute_mask == TEGRA210_MUTE_MASK_EN)
+				ucontrol->value.integer.value[0] =
+					TEGRA210_MVC_CH0_MUTE_EN;
+			else
+				ucontrol->value.integer.value[0] = 0;
+		}
+	}
 
 	return 0;
 }
@@ -136,6 +171,7 @@ static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol,
 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
 	struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
 	unsigned int value;
+	u32 reg_mask;
 	u8 mute_mask;
 	int err;
 
@@ -150,11 +186,22 @@ static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol,
 
 	mute_mask = ucontrol->value.integer.value[0];
 
-	err = regmap_update_bits(mvc->regmap, mc->reg,
-				 TEGRA210_MVC_MUTE_MASK,
-				 mute_mask << TEGRA210_MVC_MUTE_SHIFT);
-	if (err < 0)
-		goto end;
+	if (strstr(kcontrol->id.name, "Per Chan Mute Mask")) {
+		regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
+				   TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
+				   TEGRA210_MVC_PER_CHAN_CTRL_EN);
+
+		reg_mask = TEGRA210_MVC_MUTE_MASK;
+	} else {
+		regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
+				   TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
+				   0);
+
+		reg_mask = TEGRA210_MVC_CH0_MUTE_MASK;
+	}
+
+	regmap_update_bits(mvc->regmap, mc->reg, reg_mask,
+			   mute_mask << TEGRA210_MVC_MUTE_SHIFT);
 
 	return 1;
 
@@ -212,11 +259,31 @@ static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
 			      ucontrol->value.integer.value[0]);
 
 	/* Configure init volume same as target volume */
-	regmap_write(mvc->regmap,
-		TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan),
-		mvc->volume[chan]);
+	if (strstr(kcontrol->id.name, "Channel")) {
+		regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
+				   TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
+				   TEGRA210_MVC_PER_CHAN_CTRL_EN);
+
+		regmap_write(mvc->regmap,
+			TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan),
+			mvc->volume[chan]);
+
+		regmap_write(mvc->regmap, reg, mvc->volume[chan]);
+	} else {
+		int i;
+
+		regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
+				   TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
+				   0);
+
+		for (i = 1; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
+			mvc->volume[i] = mvc->volume[0];
 
-	regmap_write(mvc->regmap, reg, mvc->volume[chan]);
+		regmap_write(mvc->regmap, TEGRA210_MVC_INIT_VOL,
+			     mvc->volume[0]);
+
+		regmap_write(mvc->regmap, reg, mvc->volume[0]);
+	}
 
 	regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
 			   TEGRA210_MVC_VOLUME_SWITCH_MASK,
@@ -422,6 +489,14 @@ static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = {
 		       TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0,
 		       tegra210_mvc_get_mute, tegra210_mvc_put_mute),
 
+	/* Master volume */
+	SOC_SINGLE_EXT("Volume", TEGRA210_MVC_TARGET_VOL, 0, 16000, 0,
+		       tegra210_mvc_get_vol, tegra210_mvc_put_vol),
+
+	/* Master mute */
+	SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0,
+		       tegra210_mvc_get_mute, tegra210_mvc_put_mute),
+
 	SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl,
 		     tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type),
 };
diff --git a/sound/soc/tegra/tegra210_mvc.h b/sound/soc/tegra/tegra210_mvc.h
index def29c4..7f2567e 100644
--- a/sound/soc/tegra/tegra210_mvc.h
+++ b/sound/soc/tegra/tegra210_mvc.h
@@ -59,6 +59,8 @@
 #define TEGRA210_MUTE_MASK_EN			0xff
 #define TEGRA210_MVC_MUTE_MASK			(TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT)
 #define TEGRA210_MVC_MUTE_EN			(TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT)
+#define TEGRA210_MVC_CH0_MUTE_EN		1
+#define TEGRA210_MVC_CH0_MUTE_MASK		(TEGRA210_MVC_CH0_MUTE_EN << TEGRA210_MVC_MUTE_SHIFT)
 
 #define TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT	30
 #define TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK	(1 << TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT)
-- 
2.7.4


             reply	other threads:[~2021-10-25 11:06 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-10-25 11:06 Sameer Pujar [this message]
2021-10-25 12:58 ` [PATCH] ASoC: tegra: Add master volume/mute control support Jaroslav Kysela
2021-10-26  6:23   ` Sameer Pujar
2021-10-29 15:08     ` Sameer Pujar
2021-10-29 15:22       ` Mark Brown
2021-10-29 15:26       ` Jaroslav Kysela
2021-11-03  5:12         ` Sameer Pujar

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=1635159976-17355-1-git-send-email-spujar@nvidia.com \
    --to=spujar@nvidia.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    --cc=jonathanh@nvidia.com \
    --cc=lgirdwood@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-tegra@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=thierry.reding@gmail.com \
    --cc=tiwai@suse.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).