Skip to content

Commit a77e284

Browse files
Vlad Karpovichrriveramcrus
authored andcommitted
ASoC: cs35l45: Analog PCM Volume and Amplifier Mode controls
Adds "Analog PCM Volume" control with supported values 0 = 10dB,1 = 13dB,2 = 16dB and 3 = 19dB. The amplifier can operate either in Speaker Mode or Receiver Mode as configured by the user. Speaker Mode has four gain options to support maximum amplifier output amplitude for loud speaker application. Receiver Mode has further optimized noise performance while maintaining sufficient output to support phone receiver application. While configured in Receiver Mode, the analog PCM Volume control is disabled and the analog gain is fixed to 1dB. Change-Id: Idd78ecee02bb1cdceb3cc0d84e16452b75e6bd83 Signed-off-by: Vlad Karpovich <[email protected]> Acked-by: Charles Keepax <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 16a5852 commit a77e284

File tree

3 files changed

+184
-2
lines changed

3 files changed

+184
-2
lines changed

sound/soc/codecs/cs35l45-tables.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ static const struct reg_default cs35l45_defaults[] = {
9191
{ CS35L45_DSP1RX7_INPUT, 0x0000003A },
9292
{ CS35L45_DSP1RX8_INPUT, 0x00000028 },
9393
{ CS35L45_AMP_PCM_CONTROL, 0x00100000 },
94+
{ CS35L45_AMP_GAIN, 0x00002300 },
9495
{ CS35L45_IRQ1_CFG, 0x00000000 },
9596
{ CS35L45_IRQ1_MASK_1, 0xBFEFFFBF },
9697
{ CS35L45_IRQ1_MASK_2, 0xFFFFFFFF },
@@ -156,7 +157,9 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
156157
case CS35L45_DSP1RX6_INPUT:
157158
case CS35L45_DSP1RX7_INPUT:
158159
case CS35L45_DSP1RX8_INPUT:
160+
case CS35L45_HVLV_CONFIG:
159161
case CS35L45_AMP_PCM_CONTROL:
162+
case CS35L45_AMP_GAIN:
160163
case CS35L45_AMP_PCM_HPF_TST:
161164
case CS35L45_IRQ1_CFG:
162165
case CS35L45_IRQ1_STATUS:

sound/soc/codecs/cs35l45.c

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,142 @@ static int cs35l45_dsp_audio_ev(struct snd_soc_dapm_widget *w,
169169
return 0;
170170
}
171171

172+
static int cs35l45_activate_ctl(struct snd_soc_component *component,
173+
const char *ctl_name, bool active)
174+
{
175+
struct snd_card *card = component->card->snd_card;
176+
struct snd_kcontrol *kcontrol;
177+
struct snd_kcontrol_volatile *vd;
178+
unsigned int index_offset;
179+
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
180+
181+
if (component->name_prefix)
182+
snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s",
183+
component->name_prefix, ctl_name);
184+
else
185+
snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", ctl_name);
186+
187+
kcontrol = snd_soc_card_get_kcontrol(component->card, name);
188+
if (!kcontrol) {
189+
dev_err(component->dev, "Can't find kcontrol %s\n", name);
190+
return -EINVAL;
191+
}
192+
193+
index_offset = snd_ctl_get_ioff(kcontrol, &kcontrol->id);
194+
vd = &kcontrol->vd[index_offset];
195+
if (active)
196+
vd->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
197+
else
198+
vd->access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
199+
200+
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &kcontrol->id);
201+
202+
return 0;
203+
}
204+
205+
static int cs35l45_amplifier_mode_get(struct snd_kcontrol *kcontrol,
206+
struct snd_ctl_elem_value *ucontrol)
207+
{
208+
struct snd_soc_component *component =
209+
snd_soc_kcontrol_component(kcontrol);
210+
struct cs35l45_private *cs35l45 =
211+
snd_soc_component_get_drvdata(component);
212+
213+
ucontrol->value.integer.value[0] = cs35l45->amplifier_mode;
214+
215+
return 0;
216+
}
217+
218+
static int cs35l45_amplifier_mode_put(struct snd_kcontrol *kcontrol,
219+
struct snd_ctl_elem_value *ucontrol)
220+
{
221+
struct snd_soc_component *component =
222+
snd_soc_kcontrol_component(kcontrol);
223+
struct cs35l45_private *cs35l45 =
224+
snd_soc_component_get_drvdata(component);
225+
struct snd_soc_dapm_context *dapm =
226+
snd_soc_component_get_dapm(component);
227+
unsigned int amp_state;
228+
int ret;
229+
230+
if ((ucontrol->value.integer.value[0] == cs35l45->amplifier_mode) ||
231+
(ucontrol->value.integer.value[0] > AMP_MODE_RCV))
232+
return 0;
233+
234+
snd_soc_dapm_mutex_lock(dapm);
235+
236+
ret = regmap_read(cs35l45->regmap, CS35L45_BLOCK_ENABLES, &amp_state);
237+
if (ret < 0) {
238+
dev_err(cs35l45->dev, "Failed to read AMP state: %d\n", ret);
239+
snd_soc_dapm_mutex_unlock(dapm);
240+
return ret;
241+
}
242+
243+
regmap_clear_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
244+
CS35L45_AMP_EN_MASK);
245+
snd_soc_component_disable_pin_unlocked(component, "SPK");
246+
snd_soc_dapm_sync_unlocked(dapm);
247+
248+
if (ucontrol->value.integer.value[0] == AMP_MODE_SPK) {
249+
regmap_clear_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
250+
CS35L45_RCV_EN_MASK);
251+
252+
regmap_update_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
253+
CS35L45_BST_EN_MASK,
254+
CS35L45_BST_ENABLE << CS35L45_BST_EN_SHIFT);
255+
256+
regmap_update_bits(cs35l45->regmap, CS35L45_HVLV_CONFIG,
257+
CS35L45_HVLV_MODE_MASK,
258+
CS35L45_HVLV_OPERATION <<
259+
CS35L45_HVLV_MODE_SHIFT);
260+
261+
ret = cs35l45_activate_ctl(component, "Analog PCM Volume", true);
262+
if (ret < 0)
263+
dev_err(cs35l45->dev,
264+
"Unable to deactivate ctl (%d)\n", ret);
265+
266+
} else /* AMP_MODE_RCV */ {
267+
regmap_set_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
268+
CS35L45_RCV_EN_MASK);
269+
270+
regmap_update_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
271+
CS35L45_BST_EN_MASK,
272+
CS35L45_BST_DISABLE_FET_OFF <<
273+
CS35L45_BST_EN_SHIFT);
274+
275+
regmap_update_bits(cs35l45->regmap, CS35L45_HVLV_CONFIG,
276+
CS35L45_HVLV_MODE_MASK,
277+
CS35L45_FORCE_LV_OPERATION <<
278+
CS35L45_HVLV_MODE_SHIFT);
279+
280+
regmap_clear_bits(cs35l45->regmap,
281+
CS35L45_BLOCK_ENABLES2,
282+
CS35L45_AMP_DRE_EN_MASK);
283+
284+
regmap_update_bits(cs35l45->regmap, CS35L45_AMP_GAIN,
285+
CS35L45_AMP_GAIN_PCM_MASK,
286+
CS35L45_AMP_GAIN_PCM_13DBV <<
287+
CS35L45_AMP_GAIN_PCM_SHIFT);
288+
289+
ret = cs35l45_activate_ctl(component, "Analog PCM Volume", false);
290+
if (ret < 0)
291+
dev_err(cs35l45->dev,
292+
"Unable to deactivate ctl (%d)\n", ret);
293+
}
294+
295+
if (amp_state & CS35L45_AMP_EN_MASK)
296+
regmap_set_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
297+
CS35L45_AMP_EN_MASK);
298+
299+
snd_soc_component_enable_pin_unlocked(component, "SPK");
300+
snd_soc_dapm_sync_unlocked(dapm);
301+
snd_soc_dapm_mutex_unlock(dapm);
302+
303+
cs35l45->amplifier_mode = ucontrol->value.integer.value[0];
304+
305+
return 1;
306+
}
307+
172308
static const char * const cs35l45_asp_tx_txt[] = {
173309
"Zero", "ASP_RX1", "ASP_RX2",
174310
"VMON", "IMON", "ERR_VOL",
@@ -432,9 +568,19 @@ static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = {
432568
{ "SPK", NULL, "AMP"},
433569
};
434570

571+
static const char * const amplifier_mode_texts[] = {"SPK", "RCV"};
572+
static SOC_ENUM_SINGLE_DECL(amplifier_mode_enum, SND_SOC_NOPM, 0,
573+
amplifier_mode_texts);
574+
static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 1000, 300, 0);
435575
static const DECLARE_TLV_DB_SCALE(cs35l45_dig_pcm_vol_tlv, -10225, 25, true);
436576

437577
static const struct snd_kcontrol_new cs35l45_controls[] = {
578+
SOC_ENUM_EXT("Amplifier Mode", amplifier_mode_enum,
579+
cs35l45_amplifier_mode_get, cs35l45_amplifier_mode_put),
580+
SOC_SINGLE_TLV("Analog PCM Volume", CS35L45_AMP_GAIN,
581+
CS35L45_AMP_GAIN_PCM_SHIFT,
582+
CS35L45_AMP_GAIN_PCM_MASK >> CS35L45_AMP_GAIN_PCM_SHIFT,
583+
0, amp_gain_tlv),
438584
/* Ignore bit 0: it is beyond the resolution of TLV_DB_SCALE */
439585
SOC_SINGLE_S_TLV("Digital PCM Volume",
440586
CS35L45_AMP_PCM_CONTROL,
@@ -1104,6 +1250,8 @@ static int cs35l45_initialize(struct cs35l45_private *cs35l45)
11041250
if (ret < 0)
11051251
return ret;
11061252

1253+
cs35l45->amplifier_mode = AMP_MODE_SPK;
1254+
11071255
return 0;
11081256
}
11091257

sound/soc/codecs/cs35l45.h

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@
6161
#define CS35L45_DSP1RX6_INPUT 0x00004C54
6262
#define CS35L45_DSP1RX7_INPUT 0x00004C58
6363
#define CS35L45_DSP1RX8_INPUT 0x00004C5C
64+
#define CS35L45_HVLV_CONFIG 0x00006400
6465
#define CS35L45_LDPM_CONFIG 0x00006404
6566
#define CS35L45_AMP_PCM_CONTROL 0x00007000
6667
#define CS35L45_AMP_PCM_HPF_TST 0x00007004
68+
#define CS35L45_AMP_GAIN 0x00007800
6769
#define CS35L45_IRQ1_CFG 0x0000E000
6870
#define CS35L45_IRQ1_STATUS 0x0000E004
6971
#define CS35L45_IRQ1_EINT_1 0x0000E010
@@ -167,12 +169,19 @@
167169
#define CS35L45_VDD_BATTMON_EN_SHIFT 8
168170
#define CS35L45_BST_EN_SHIFT 4
169171
#define CS35L45_BST_EN_MASK GENMASK(5, 4)
172+
#define CS35L45_RCV_EN_SHIFT 2
173+
#define CS35L45_RCV_EN_MASK BIT(2)
174+
#define CS35L45_AMP_EN_SHIFT 0
175+
#define CS35L45_AMP_EN_MASK BIT(0)
170176

171-
#define CS35L45_BST_DISABLE_FET_ON 0x01
177+
#define CS35L45_BST_DISABLE_FET_OFF 0x00
178+
#define CS35L45_BST_DISABLE_FET_ON 0x01
179+
#define CS35L45_BST_ENABLE 0x02
172180

173181
/* BLOCK_ENABLES2 */
174182
#define CS35L45_ASP_EN_SHIFT 27
175-
183+
#define CS35L45_AMP_DRE_EN_SHIFT 20
184+
#define CS35L45_AMP_DRE_EN_MASK BIT(20)
176185
#define CS35L45_MEM_RDY_SHIFT 1
177186
#define CS35L45_MEM_RDY_MASK BIT(1)
178187

@@ -266,6 +275,13 @@
266275
#define CS35L45_ASP_WL_SHIFT 0
267276
#define CS35L45_ASP_WL_MASK GENMASK(5, 0)
268277

278+
/* HVLV_CONFIG */
279+
#define CS35L45_FORCE_LV_OPERATION 0x01
280+
#define CS35L45_FORCE_HV_OPERATION 0x02
281+
#define CS35L45_HVLV_OPERATION 0x03
282+
#define CS35L45_HVLV_MODE_SHIFT 0
283+
#define CS35L45_HVLV_MODE_MASK GENMASK(1, 0)
284+
269285
/* AMP_PCM_CONTROL */
270286
#define CS35L45_AMP_VOL_PCM_SHIFT 0
271287
#define CS35L45_AMP_VOL_PCM_WIDTH 11
@@ -275,6 +291,15 @@
275291
#define CS35L45_HPF_44P1 0x000108BD
276292
#define CS35L45_HPF_88P2 0x0001045F
277293

294+
/* AMP_GAIN_PCM */
295+
#define CS35L45_AMP_GAIN_PCM_10DBV 0x00
296+
#define CS35L45_AMP_GAIN_PCM_13DBV 0x01
297+
#define CS35L45_AMP_GAIN_PCM_16DBV 0x02
298+
#define CS35L45_AMP_GAIN_PCM_19DBV 0x03
299+
300+
#define CS35L45_AMP_GAIN_PCM_SHIFT 8
301+
#define CS35L45_AMP_GAIN_PCM_MASK GENMASK(9, 8)
302+
278303
/* IRQ1_EINT_4 */
279304
#define CS35L45_OTP_BOOT_DONE_STS_MASK BIT(1)
280305
#define CS35L45_OTP_BUSY_MASK BIT(0)
@@ -396,6 +421,11 @@ enum control_bus_type {
396421
CONTROL_BUS_SPI = 1,
397422
};
398423

424+
enum amp_mode {
425+
AMP_MODE_SPK = 0,
426+
AMP_MODE_RCV = 1,
427+
};
428+
399429
#define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
400430
SNDRV_PCM_FMTBIT_S24_3LE| \
401431
SNDRV_PCM_FMTBIT_S24_LE)
@@ -464,6 +494,7 @@ struct cs35l45_private {
464494
bool sysclk_set;
465495
u8 slot_width;
466496
u8 slot_count;
497+
int amplifier_mode;
467498
int irq_invert;
468499
int irq;
469500
unsigned int i2c_addr;

0 commit comments

Comments
 (0)