Skip to content

Commit 0fa382a

Browse files
committed
Add support for CS35L63 Smart Amplifier
Merge series from Stefan Binding <[email protected]>: CS35L63 is a Mono Class-D PC Smart Amplifier, with Speaker Protection and Audio Enhancement Algorithms. CS35L63 uses a similar control interface to CS35L56 so support for it can be added into the CS35L56 driver. CS35L63 only has SoundWire and I2C control interfaces.
2 parents e6a40d5 + 406fbc4 commit 0fa382a

File tree

10 files changed

+413
-37
lines changed

10 files changed

+413
-37
lines changed

include/sound/cs35l56.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@
7171
#define CS35L56_DSP_VIRTUAL1_MBOX_6 0x0011034
7272
#define CS35L56_DSP_VIRTUAL1_MBOX_7 0x0011038
7373
#define CS35L56_DSP_VIRTUAL1_MBOX_8 0x001103C
74+
#define CS35L56_DIE_STS1 0x0017040
75+
#define CS35L56_DIE_STS2 0x0017044
7476
#define CS35L56_DSP_RESTRICT_STS1 0x00190F0
7577
#define CS35L56_DSP1_XMEM_PACKED_0 0x2000000
7678
#define CS35L56_DSP1_XMEM_PACKED_6143 0x2005FFC
@@ -104,6 +106,15 @@
104106
#define CS35L56_DSP1_PMEM_0 0x3800000
105107
#define CS35L56_DSP1_PMEM_5114 0x3804FE8
106108

109+
#define CS35L63_DSP1_FW_VER CS35L56_DSP1_FW_VER
110+
#define CS35L63_DSP1_HALO_STATE 0x280396C
111+
#define CS35L63_DSP1_PM_CUR_STATE 0x28042C8
112+
#define CS35L63_PROTECTION_STATUS 0x340009C
113+
#define CS35L63_TRANSDUCER_ACTUAL_PS 0x34000F4
114+
#define CS35L63_MAIN_RENDER_USER_MUTE 0x3400020
115+
#define CS35L63_MAIN_RENDER_USER_VOLUME 0x3400028
116+
#define CS35L63_MAIN_POSTURE_NUMBER 0x3400068
117+
107118
/* DEVID */
108119
#define CS35L56_DEVID_MASK 0x00FFFFFF
109120

@@ -267,6 +278,17 @@ struct cs35l56_spi_payload {
267278
} __packed;
268279
static_assert(sizeof(struct cs35l56_spi_payload) == 10);
269280

281+
struct cs35l56_fw_reg {
282+
unsigned int fw_ver;
283+
unsigned int halo_state;
284+
unsigned int pm_cur_stat;
285+
unsigned int prot_sts;
286+
unsigned int transducer_actual_ps;
287+
unsigned int user_mute;
288+
unsigned int user_volume;
289+
unsigned int posture_number;
290+
};
291+
270292
struct cs35l56_base {
271293
struct device *dev;
272294
struct regmap *regmap;
@@ -283,6 +305,7 @@ struct cs35l56_base {
283305
struct cirrus_amp_cal_data cal_data;
284306
struct gpio_desc *reset_gpio;
285307
struct cs35l56_spi_payload *spi_payload_buf;
308+
const struct cs35l56_fw_reg *fw_reg;
286309
};
287310

288311
static inline bool cs35l56_is_otp_register(unsigned int reg)
@@ -310,6 +333,11 @@ static inline bool cs35l56_is_spi(struct cs35l56_base *cs35l56)
310333
extern const struct regmap_config cs35l56_regmap_i2c;
311334
extern const struct regmap_config cs35l56_regmap_spi;
312335
extern const struct regmap_config cs35l56_regmap_sdw;
336+
extern const struct regmap_config cs35l63_regmap_i2c;
337+
extern const struct regmap_config cs35l63_regmap_sdw;
338+
339+
extern const struct cs35l56_fw_reg cs35l56_fw_reg;
340+
extern const struct cs35l56_fw_reg cs35l63_fw_reg;
313341

314342
extern const struct cirrus_amp_cal_controls cs35l56_calibration_controls;
315343

sound/pci/hda/cs35l56_hda.c

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ static void cs35l56_hda_play(struct cs35l56_hda *cs35l56)
6868
if (ret == 0) {
6969
/* Wait for firmware to enter PS0 power state */
7070
ret = regmap_read_poll_timeout(cs35l56->base.regmap,
71-
CS35L56_TRANSDUCER_ACTUAL_PS,
71+
cs35l56->base.fw_reg->transducer_actual_ps,
7272
val, (val == CS35L56_PS0),
7373
CS35L56_PS0_POLL_US,
7474
CS35L56_PS0_TIMEOUT_US);
@@ -237,7 +237,8 @@ static int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol,
237237

238238
cs35l56_hda_wait_dsp_ready(cs35l56);
239239

240-
ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_POSTURE_NUMBER, &pos);
240+
ret = regmap_read(cs35l56->base.regmap,
241+
cs35l56->base.fw_reg->posture_number, &pos);
241242
if (ret)
242243
return ret;
243244

@@ -260,10 +261,8 @@ static int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol,
260261

261262
cs35l56_hda_wait_dsp_ready(cs35l56);
262263

263-
ret = regmap_update_bits_check(cs35l56->base.regmap,
264-
CS35L56_MAIN_POSTURE_NUMBER,
265-
CS35L56_MAIN_POSTURE_MASK,
266-
pos, &changed);
264+
ret = regmap_update_bits_check(cs35l56->base.regmap, cs35l56->base.fw_reg->posture_number,
265+
CS35L56_MAIN_POSTURE_MASK, pos, &changed);
267266
if (ret)
268267
return ret;
269268

@@ -305,7 +304,7 @@ static int cs35l56_hda_vol_get(struct snd_kcontrol *kcontrol,
305304

306305
cs35l56_hda_wait_dsp_ready(cs35l56);
307306

308-
ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_RENDER_USER_VOLUME, &raw_vol);
307+
ret = regmap_read(cs35l56->base.regmap, cs35l56->base.fw_reg->user_volume, &raw_vol);
309308

310309
if (ret)
311310
return ret;
@@ -339,10 +338,8 @@ static int cs35l56_hda_vol_put(struct snd_kcontrol *kcontrol,
339338

340339
cs35l56_hda_wait_dsp_ready(cs35l56);
341340

342-
ret = regmap_update_bits_check(cs35l56->base.regmap,
343-
CS35L56_MAIN_RENDER_USER_VOLUME,
344-
CS35L56_MAIN_RENDER_USER_VOLUME_MASK,
345-
raw_vol, &changed);
341+
ret = regmap_update_bits_check(cs35l56->base.regmap, cs35l56->base.fw_reg->user_volume,
342+
CS35L56_MAIN_RENDER_USER_VOLUME_MASK, raw_vol, &changed);
346343
if (ret)
347344
return ret;
348345

@@ -665,7 +662,8 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
665662

666663
regcache_sync(cs35l56->base.regmap);
667664

668-
regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS,
665+
regmap_clear_bits(cs35l56->base.regmap,
666+
cs35l56->base.fw_reg->prot_sts,
669667
CS35L56_FIRMWARE_MISSING);
670668
cs35l56->base.fw_patched = true;
671669

sound/pci/hda/cs35l56_hda_i2c.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ static int cs35l56_hda_i2c_probe(struct i2c_client *clt)
2626
#ifdef CS35L56_WAKE_HOLD_TIME_US
2727
cs35l56->base.can_hibernate = true;
2828
#endif
29+
30+
cs35l56->base.fw_reg = &cs35l56_fw_reg;
31+
2932
cs35l56->base.regmap = devm_regmap_init_i2c(clt, &cs35l56_regmap_i2c);
3033
if (IS_ERR(cs35l56->base.regmap)) {
3134
ret = PTR_ERR(cs35l56->base.regmap);

sound/pci/hda/cs35l56_hda_spi.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ static int cs35l56_hda_spi_probe(struct spi_device *spi)
2929
#ifdef CS35L56_WAKE_HOLD_TIME_US
3030
cs35l56->base.can_hibernate = true;
3131
#endif
32+
33+
cs35l56->base.fw_reg = &cs35l56_fw_reg;
34+
3235
cs35l56->base.regmap = devm_regmap_init_spi(spi, &cs35l56_regmap_spi);
3336
if (IS_ERR(cs35l56->base.regmap)) {
3437
ret = PTR_ERR(cs35l56->base.regmap);

sound/soc/codecs/cs35l56-i2c.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717

1818
static int cs35l56_i2c_probe(struct i2c_client *client)
1919
{
20+
unsigned int id = (u32)(uintptr_t)i2c_get_match_data(client);
2021
struct cs35l56_private *cs35l56;
2122
struct device *dev = &client->dev;
22-
const struct regmap_config *regmap_config = &cs35l56_regmap_i2c;
23+
const struct regmap_config *regmap_config;
2324
int ret;
2425

2526
cs35l56 = devm_kzalloc(dev, sizeof(struct cs35l56_private), GFP_KERNEL);
@@ -30,6 +31,20 @@ static int cs35l56_i2c_probe(struct i2c_client *client)
3031
cs35l56->base.can_hibernate = true;
3132

3233
i2c_set_clientdata(client, cs35l56);
34+
35+
switch (id) {
36+
case 0x3556:
37+
regmap_config = &cs35l56_regmap_i2c;
38+
cs35l56->base.fw_reg = &cs35l56_fw_reg;
39+
break;
40+
case 0x3563:
41+
regmap_config = &cs35l63_regmap_i2c;
42+
cs35l56->base.fw_reg = &cs35l63_fw_reg;
43+
break;
44+
default:
45+
return -ENODEV;
46+
}
47+
3348
cs35l56->base.regmap = devm_regmap_init_i2c(client, regmap_config);
3449
if (IS_ERR(cs35l56->base.regmap)) {
3550
ret = PTR_ERR(cs35l56->base.regmap);
@@ -57,14 +72,16 @@ static void cs35l56_i2c_remove(struct i2c_client *client)
5772
}
5873

5974
static const struct i2c_device_id cs35l56_id_i2c[] = {
60-
{ "cs35l56" },
75+
{ "cs35l56", 0x3556 },
76+
{ "cs35l63", 0x3563 },
6177
{}
6278
};
6379
MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c);
6480

6581
#ifdef CONFIG_ACPI
6682
static const struct acpi_device_id cs35l56_asoc_acpi_match[] = {
67-
{ "CSC355C", 0 },
83+
{ "CSC355C", 0x3556 },
84+
{ "CSC356C", 0x3563 },
6885
{},
6986
};
7087
MODULE_DEVICE_TABLE(acpi, cs35l56_asoc_acpi_match);

sound/soc/codecs/cs35l56-sdw.c

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,74 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral,
393393
return 0;
394394
}
395395

396+
static int cs35l63_sdw_kick_divider(struct cs35l56_private *cs35l56,
397+
struct sdw_slave *peripheral)
398+
{
399+
unsigned int curr_scale_reg, next_scale_reg;
400+
int curr_scale, next_scale, ret;
401+
402+
if (!cs35l56->base.init_done)
403+
return 0;
404+
405+
if (peripheral->bus->params.curr_bank) {
406+
curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1;
407+
next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0;
408+
} else {
409+
curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0;
410+
next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1;
411+
}
412+
413+
/*
414+
* Current clock scale value must be different to new value.
415+
* Modify current to guarantee this. If next still has the dummy
416+
* value we wrote when it was current, the core code has not set
417+
* a new scale so restore its original good value
418+
*/
419+
curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg);
420+
if (curr_scale < 0) {
421+
dev_err(cs35l56->base.dev, "Failed to read current clock scale: %d\n", curr_scale);
422+
return curr_scale;
423+
}
424+
425+
next_scale = sdw_read_no_pm(peripheral, next_scale_reg);
426+
if (next_scale < 0) {
427+
dev_err(cs35l56->base.dev, "Failed to read next clock scale: %d\n", next_scale);
428+
return next_scale;
429+
}
430+
431+
if (next_scale == CS35L56_SDW_INVALID_BUS_SCALE) {
432+
next_scale = cs35l56->old_sdw_clock_scale;
433+
ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale);
434+
if (ret < 0) {
435+
dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n",
436+
ret);
437+
return ret;
438+
}
439+
}
440+
441+
cs35l56->old_sdw_clock_scale = curr_scale;
442+
ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE);
443+
if (ret < 0) {
444+
dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", ret);
445+
return ret;
446+
}
447+
448+
dev_dbg(cs35l56->base.dev, "Next bus scale: %#x\n", next_scale);
449+
450+
return 0;
451+
}
452+
453+
static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral,
454+
struct sdw_bus_params *params)
455+
{
456+
struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
457+
458+
if ((cs35l56->base.type == 0x63) && (cs35l56->base.rev < 0xa1))
459+
return cs35l63_sdw_kick_divider(cs35l56, peripheral);
460+
461+
return 0;
462+
}
463+
396464
static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral,
397465
enum sdw_clk_stop_mode mode,
398466
enum sdw_clk_stop_type type)
@@ -408,6 +476,7 @@ static const struct sdw_slave_ops cs35l56_sdw_ops = {
408476
.read_prop = cs35l56_sdw_read_prop,
409477
.interrupt_callback = cs35l56_sdw_interrupt,
410478
.update_status = cs35l56_sdw_update_status,
479+
.bus_config = cs35l56_sdw_bus_config,
411480
#ifdef DEBUG
412481
.clk_stop = cs35l56_sdw_clk_stop,
413482
#endif
@@ -509,6 +578,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi
509578
{
510579
struct device *dev = &peripheral->dev;
511580
struct cs35l56_private *cs35l56;
581+
const struct regmap_config *regmap_config;
512582
int ret;
513583

514584
cs35l56 = devm_kzalloc(dev, sizeof(*cs35l56), GFP_KERNEL);
@@ -521,8 +591,22 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi
521591

522592
dev_set_drvdata(dev, cs35l56);
523593

594+
switch ((unsigned int)id->driver_data) {
595+
case 0x3556:
596+
case 0x3557:
597+
regmap_config = &cs35l56_regmap_sdw;
598+
cs35l56->base.fw_reg = &cs35l56_fw_reg;
599+
break;
600+
case 0x3563:
601+
regmap_config = &cs35l63_regmap_sdw;
602+
cs35l56->base.fw_reg = &cs35l63_fw_reg;
603+
break;
604+
default:
605+
return -ENODEV;
606+
}
607+
524608
cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw,
525-
peripheral, &cs35l56_regmap_sdw);
609+
peripheral, regmap_config);
526610
if (IS_ERR(cs35l56->base.regmap)) {
527611
ret = PTR_ERR(cs35l56->base.regmap);
528612
return dev_err_probe(dev, ret, "Failed to allocate register map\n");
@@ -562,8 +646,9 @@ static const struct dev_pm_ops cs35l56_sdw_pm = {
562646
};
563647

564648
static const struct sdw_device_id cs35l56_sdw_id[] = {
565-
SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0),
566-
SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0),
649+
SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0x3556),
650+
SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557),
651+
SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563),
567652
{},
568653
};
569654
MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id);

0 commit comments

Comments
 (0)