Skip to content

Commit 0d24269

Browse files
pujarsbroonie
authored andcommitted
ASoC: tegra: Add master volume/mute control support
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> Link: https://lore.kernel.org/r/1638278605-28225-1-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent 67140b6 commit 0d24269

2 files changed

Lines changed: 169 additions & 45 deletions

File tree

sound/soc/tegra/tegra210_mvc.c

Lines changed: 164 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -108,67 +108,152 @@ static void tegra210_mvc_conv_vol(struct tegra210_mvc *mvc, u8 chan, s32 val)
108108
}
109109
}
110110

111-
static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol,
112-
struct snd_ctl_elem_value *ucontrol)
111+
static u32 tegra210_mvc_get_ctrl_reg(struct snd_kcontrol *kcontrol)
113112
{
114113
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
115114
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
116-
u8 mute_mask;
117115
u32 val;
118116

119117
pm_runtime_get_sync(cmpnt->dev);
120118
regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &val);
121119
pm_runtime_put(cmpnt->dev);
122120

123-
mute_mask = (val >> TEGRA210_MVC_MUTE_SHIFT) &
124-
TEGRA210_MUTE_MASK_EN;
121+
return val;
122+
}
123+
124+
static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol,
125+
struct snd_ctl_elem_value *ucontrol)
126+
{
127+
u32 val = tegra210_mvc_get_ctrl_reg(kcontrol);
128+
u8 mute_mask = TEGRA210_GET_MUTE_VAL(val);
125129

126-
ucontrol->value.integer.value[0] = mute_mask;
130+
/*
131+
* If per channel control is enabled, then return
132+
* exact mute/unmute setting of all channels.
133+
*
134+
* Else report setting based on CH0 bit to reflect
135+
* the correct HW state.
136+
*/
137+
if (val & TEGRA210_MVC_PER_CHAN_CTRL_EN) {
138+
ucontrol->value.integer.value[0] = mute_mask;
139+
} else {
140+
if (mute_mask & TEGRA210_MVC_CH0_MUTE_EN)
141+
ucontrol->value.integer.value[0] =
142+
TEGRA210_MUTE_MASK_EN;
143+
else
144+
ucontrol->value.integer.value[0] = 0;
145+
}
127146

128147
return 0;
129148
}
130149

131-
static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol,
132-
struct snd_ctl_elem_value *ucontrol)
150+
static int tegra210_mvc_get_master_mute(struct snd_kcontrol *kcontrol,
151+
struct snd_ctl_elem_value *ucontrol)
152+
{
153+
u32 val = tegra210_mvc_get_ctrl_reg(kcontrol);
154+
u8 mute_mask = TEGRA210_GET_MUTE_VAL(val);
155+
156+
/*
157+
* If per channel control is disabled, then return
158+
* master mute/unmute setting based on CH0 bit.
159+
*
160+
* Else report settings based on state of all
161+
* channels.
162+
*/
163+
if (!(val & TEGRA210_MVC_PER_CHAN_CTRL_EN)) {
164+
ucontrol->value.integer.value[0] =
165+
mute_mask & TEGRA210_MVC_CH0_MUTE_EN;
166+
} else {
167+
if (mute_mask == TEGRA210_MUTE_MASK_EN)
168+
ucontrol->value.integer.value[0] =
169+
TEGRA210_MVC_CH0_MUTE_EN;
170+
else
171+
ucontrol->value.integer.value[0] = 0;
172+
}
173+
174+
return 0;
175+
}
176+
177+
static int tegra210_mvc_volume_switch_timeout(struct snd_soc_component *cmpnt)
133178
{
134-
struct soc_mixer_control *mc =
135-
(struct soc_mixer_control *)kcontrol->private_value;
136-
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
137179
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
138-
unsigned int value;
139-
u8 new_mask, old_mask;
180+
u32 value;
140181
int err;
141182

142-
pm_runtime_get_sync(cmpnt->dev);
143-
144-
/* Check if VOLUME_SWITCH is triggered */
145183
err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH,
146184
value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK),
147185
10, 10000);
148186
if (err < 0)
149-
goto end;
187+
dev_err(cmpnt->dev,
188+
"Volume switch trigger is still active, err = %d\n",
189+
err);
150190

151-
regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &value);
191+
return err;
192+
}
152193

153-
old_mask = (value >> TEGRA210_MVC_MUTE_SHIFT) & TEGRA210_MUTE_MASK_EN;
154-
new_mask = ucontrol->value.integer.value[0];
194+
static int tegra210_mvc_update_mute(struct snd_kcontrol *kcontrol,
195+
struct snd_ctl_elem_value *ucontrol,
196+
bool per_chan_ctrl)
197+
{
198+
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
199+
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
200+
u32 mute_val = ucontrol->value.integer.value[0];
201+
u32 per_ch_ctrl_val;
202+
bool change = false;
203+
int err;
155204

156-
if (new_mask == old_mask) {
157-
err = 0;
205+
pm_runtime_get_sync(cmpnt->dev);
206+
207+
err = tegra210_mvc_volume_switch_timeout(cmpnt);
208+
if (err < 0)
158209
goto end;
210+
211+
if (per_chan_ctrl) {
212+
per_ch_ctrl_val = TEGRA210_MVC_PER_CHAN_CTRL_EN;
213+
} else {
214+
per_ch_ctrl_val = 0;
215+
216+
if (mute_val)
217+
mute_val = TEGRA210_MUTE_MASK_EN;
159218
}
160219

161-
err = regmap_update_bits(mvc->regmap, mc->reg,
220+
regmap_update_bits_check(mvc->regmap, TEGRA210_MVC_CTRL,
162221
TEGRA210_MVC_MUTE_MASK,
163-
new_mask << TEGRA210_MVC_MUTE_SHIFT);
164-
if (err < 0)
165-
goto end;
222+
mute_val << TEGRA210_MVC_MUTE_SHIFT,
223+
&change);
166224

167-
err = 1;
225+
if (change) {
226+
regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
227+
TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
228+
per_ch_ctrl_val);
229+
230+
regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
231+
TEGRA210_MVC_VOLUME_SWITCH_MASK,
232+
TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
233+
}
168234

169235
end:
170236
pm_runtime_put(cmpnt->dev);
171-
return err;
237+
238+
if (err < 0)
239+
return err;
240+
241+
if (change)
242+
return 1;
243+
244+
return 0;
245+
}
246+
247+
static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol,
248+
struct snd_ctl_elem_value *ucontrol)
249+
{
250+
return tegra210_mvc_update_mute(kcontrol, ucontrol, true);
251+
}
252+
253+
static int tegra210_mvc_put_master_mute(struct snd_kcontrol *kcontrol,
254+
struct snd_ctl_elem_value *ucontrol)
255+
{
256+
return tegra210_mvc_update_mute(kcontrol, ucontrol, false);
172257
}
173258

174259
static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
@@ -178,7 +263,7 @@ static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
178263
(struct soc_mixer_control *)kcontrol->private_value;
179264
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
180265
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
181-
u8 chan = (mc->reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE;
266+
u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL);
182267
s32 val = mvc->volume[chan];
183268

184269
if (mvc->curve_type == CURVE_POLY) {
@@ -193,44 +278,55 @@ static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
193278
return 0;
194279
}
195280

196-
static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
197-
struct snd_ctl_elem_value *ucontrol)
281+
static int tegra210_mvc_get_master_vol(struct snd_kcontrol *kcontrol,
282+
struct snd_ctl_elem_value *ucontrol)
283+
{
284+
return tegra210_mvc_get_vol(kcontrol, ucontrol);
285+
}
286+
287+
static int tegra210_mvc_update_vol(struct snd_kcontrol *kcontrol,
288+
struct snd_ctl_elem_value *ucontrol,
289+
bool per_ch_enable)
198290
{
199291
struct soc_mixer_control *mc =
200292
(struct soc_mixer_control *)kcontrol->private_value;
201293
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
202294
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
203-
unsigned int reg = mc->reg;
204-
unsigned int value;
205-
u8 chan;
206-
int err, old_volume;
295+
u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL);
296+
int old_volume = mvc->volume[chan];
297+
int err, i;
207298

208299
pm_runtime_get_sync(cmpnt->dev);
209300

210-
/* Check if VOLUME_SWITCH is triggered */
211-
err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH,
212-
value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK),
213-
10, 10000);
301+
err = tegra210_mvc_volume_switch_timeout(cmpnt);
214302
if (err < 0)
215303
goto end;
216304

217-
chan = (reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE;
218-
old_volume = mvc->volume[chan];
219-
220-
tegra210_mvc_conv_vol(mvc, chan,
221-
ucontrol->value.integer.value[0]);
305+
tegra210_mvc_conv_vol(mvc, chan, ucontrol->value.integer.value[0]);
222306

223307
if (mvc->volume[chan] == old_volume) {
224308
err = 0;
225309
goto end;
226310
}
227311

312+
if (per_ch_enable) {
313+
regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
314+
TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
315+
TEGRA210_MVC_PER_CHAN_CTRL_EN);
316+
} else {
317+
regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
318+
TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, 0);
319+
320+
for (i = 1; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
321+
mvc->volume[i] = mvc->volume[chan];
322+
}
323+
228324
/* Configure init volume same as target volume */
229325
regmap_write(mvc->regmap,
230326
TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan),
231327
mvc->volume[chan]);
232328

233-
regmap_write(mvc->regmap, reg, mvc->volume[chan]);
329+
regmap_write(mvc->regmap, mc->reg, mvc->volume[chan]);
234330

235331
regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
236332
TEGRA210_MVC_VOLUME_SWITCH_MASK,
@@ -240,9 +336,22 @@ static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
240336

241337
end:
242338
pm_runtime_put(cmpnt->dev);
339+
243340
return err;
244341
}
245342

343+
static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
344+
struct snd_ctl_elem_value *ucontrol)
345+
{
346+
return tegra210_mvc_update_vol(kcontrol, ucontrol, true);
347+
}
348+
349+
static int tegra210_mvc_put_master_vol(struct snd_kcontrol *kcontrol,
350+
struct snd_ctl_elem_value *ucontrol)
351+
{
352+
return tegra210_mvc_update_vol(kcontrol, ucontrol, false);
353+
}
354+
246355
static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc,
247356
struct device *dev)
248357
{
@@ -436,6 +545,16 @@ static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = {
436545
TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0,
437546
tegra210_mvc_get_mute, tegra210_mvc_put_mute),
438547

548+
/* Master volume */
549+
SOC_SINGLE_EXT("Volume", TEGRA210_MVC_TARGET_VOL, 0, 16000, 0,
550+
tegra210_mvc_get_master_vol,
551+
tegra210_mvc_put_master_vol),
552+
553+
/* Master mute */
554+
SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0,
555+
tegra210_mvc_get_master_mute,
556+
tegra210_mvc_put_master_mute),
557+
439558
SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl,
440559
tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type),
441560
};

sound/soc/tegra/tegra210_mvc.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#define TEGRA210_MUTE_MASK_EN 0xff
6060
#define TEGRA210_MVC_MUTE_MASK (TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT)
6161
#define TEGRA210_MVC_MUTE_EN (TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT)
62+
#define TEGRA210_MVC_CH0_MUTE_EN 1
6263

6364
#define TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT 30
6465
#define TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK (1 << TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT)
@@ -92,6 +93,10 @@
9293
#define TEGRA210_MVC_MAX_CHAN_COUNT 8
9394
#define TEGRA210_MVC_REG_OFFSET(reg, i) (reg + (REG_SIZE * i))
9495

96+
#define TEGRA210_MVC_GET_CHAN(reg, base) (((reg) - (base)) / REG_SIZE)
97+
98+
#define TEGRA210_GET_MUTE_VAL(val) (((val) >> TEGRA210_MVC_MUTE_SHIFT) & TEGRA210_MUTE_MASK_EN)
99+
95100
#define NUM_GAIN_POLY_COEFFS 9
96101

97102
enum {

0 commit comments

Comments
 (0)