Add support for CS35L63 Smart Amplifier

Merge series from Stefan Binding <sbinding@opensource.cirrus.com>:

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.
This commit is contained in:
Mark Brown 2025-05-07 14:12:38 +09:00
commit 0fa382a4f5
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
10 changed files with 413 additions and 37 deletions

View File

@ -71,6 +71,8 @@
#define CS35L56_DSP_VIRTUAL1_MBOX_6 0x0011034
#define CS35L56_DSP_VIRTUAL1_MBOX_7 0x0011038
#define CS35L56_DSP_VIRTUAL1_MBOX_8 0x001103C
#define CS35L56_DIE_STS1 0x0017040
#define CS35L56_DIE_STS2 0x0017044
#define CS35L56_DSP_RESTRICT_STS1 0x00190F0
#define CS35L56_DSP1_XMEM_PACKED_0 0x2000000
#define CS35L56_DSP1_XMEM_PACKED_6143 0x2005FFC
@ -104,6 +106,15 @@
#define CS35L56_DSP1_PMEM_0 0x3800000
#define CS35L56_DSP1_PMEM_5114 0x3804FE8
#define CS35L63_DSP1_FW_VER CS35L56_DSP1_FW_VER
#define CS35L63_DSP1_HALO_STATE 0x280396C
#define CS35L63_DSP1_PM_CUR_STATE 0x28042C8
#define CS35L63_PROTECTION_STATUS 0x340009C
#define CS35L63_TRANSDUCER_ACTUAL_PS 0x34000F4
#define CS35L63_MAIN_RENDER_USER_MUTE 0x3400020
#define CS35L63_MAIN_RENDER_USER_VOLUME 0x3400028
#define CS35L63_MAIN_POSTURE_NUMBER 0x3400068
/* DEVID */
#define CS35L56_DEVID_MASK 0x00FFFFFF
@ -267,6 +278,17 @@ struct cs35l56_spi_payload {
} __packed;
static_assert(sizeof(struct cs35l56_spi_payload) == 10);
struct cs35l56_fw_reg {
unsigned int fw_ver;
unsigned int halo_state;
unsigned int pm_cur_stat;
unsigned int prot_sts;
unsigned int transducer_actual_ps;
unsigned int user_mute;
unsigned int user_volume;
unsigned int posture_number;
};
struct cs35l56_base {
struct device *dev;
struct regmap *regmap;
@ -283,6 +305,7 @@ struct cs35l56_base {
struct cirrus_amp_cal_data cal_data;
struct gpio_desc *reset_gpio;
struct cs35l56_spi_payload *spi_payload_buf;
const struct cs35l56_fw_reg *fw_reg;
};
static inline bool cs35l56_is_otp_register(unsigned int reg)
@ -310,6 +333,11 @@ static inline bool cs35l56_is_spi(struct cs35l56_base *cs35l56)
extern const struct regmap_config cs35l56_regmap_i2c;
extern const struct regmap_config cs35l56_regmap_spi;
extern const struct regmap_config cs35l56_regmap_sdw;
extern const struct regmap_config cs35l63_regmap_i2c;
extern const struct regmap_config cs35l63_regmap_sdw;
extern const struct cs35l56_fw_reg cs35l56_fw_reg;
extern const struct cs35l56_fw_reg cs35l63_fw_reg;
extern const struct cirrus_amp_cal_controls cs35l56_calibration_controls;

View File

@ -68,7 +68,7 @@ static void cs35l56_hda_play(struct cs35l56_hda *cs35l56)
if (ret == 0) {
/* Wait for firmware to enter PS0 power state */
ret = regmap_read_poll_timeout(cs35l56->base.regmap,
CS35L56_TRANSDUCER_ACTUAL_PS,
cs35l56->base.fw_reg->transducer_actual_ps,
val, (val == CS35L56_PS0),
CS35L56_PS0_POLL_US,
CS35L56_PS0_TIMEOUT_US);
@ -237,7 +237,8 @@ static int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol,
cs35l56_hda_wait_dsp_ready(cs35l56);
ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_POSTURE_NUMBER, &pos);
ret = regmap_read(cs35l56->base.regmap,
cs35l56->base.fw_reg->posture_number, &pos);
if (ret)
return ret;
@ -260,10 +261,8 @@ static int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol,
cs35l56_hda_wait_dsp_ready(cs35l56);
ret = regmap_update_bits_check(cs35l56->base.regmap,
CS35L56_MAIN_POSTURE_NUMBER,
CS35L56_MAIN_POSTURE_MASK,
pos, &changed);
ret = regmap_update_bits_check(cs35l56->base.regmap, cs35l56->base.fw_reg->posture_number,
CS35L56_MAIN_POSTURE_MASK, pos, &changed);
if (ret)
return ret;
@ -305,7 +304,7 @@ static int cs35l56_hda_vol_get(struct snd_kcontrol *kcontrol,
cs35l56_hda_wait_dsp_ready(cs35l56);
ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_RENDER_USER_VOLUME, &raw_vol);
ret = regmap_read(cs35l56->base.regmap, cs35l56->base.fw_reg->user_volume, &raw_vol);
if (ret)
return ret;
@ -339,10 +338,8 @@ static int cs35l56_hda_vol_put(struct snd_kcontrol *kcontrol,
cs35l56_hda_wait_dsp_ready(cs35l56);
ret = regmap_update_bits_check(cs35l56->base.regmap,
CS35L56_MAIN_RENDER_USER_VOLUME,
CS35L56_MAIN_RENDER_USER_VOLUME_MASK,
raw_vol, &changed);
ret = regmap_update_bits_check(cs35l56->base.regmap, cs35l56->base.fw_reg->user_volume,
CS35L56_MAIN_RENDER_USER_VOLUME_MASK, raw_vol, &changed);
if (ret)
return ret;
@ -665,7 +662,8 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
regcache_sync(cs35l56->base.regmap);
regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS,
regmap_clear_bits(cs35l56->base.regmap,
cs35l56->base.fw_reg->prot_sts,
CS35L56_FIRMWARE_MISSING);
cs35l56->base.fw_patched = true;

View File

@ -26,6 +26,9 @@ static int cs35l56_hda_i2c_probe(struct i2c_client *clt)
#ifdef CS35L56_WAKE_HOLD_TIME_US
cs35l56->base.can_hibernate = true;
#endif
cs35l56->base.fw_reg = &cs35l56_fw_reg;
cs35l56->base.regmap = devm_regmap_init_i2c(clt, &cs35l56_regmap_i2c);
if (IS_ERR(cs35l56->base.regmap)) {
ret = PTR_ERR(cs35l56->base.regmap);

View File

@ -29,6 +29,9 @@ static int cs35l56_hda_spi_probe(struct spi_device *spi)
#ifdef CS35L56_WAKE_HOLD_TIME_US
cs35l56->base.can_hibernate = true;
#endif
cs35l56->base.fw_reg = &cs35l56_fw_reg;
cs35l56->base.regmap = devm_regmap_init_spi(spi, &cs35l56_regmap_spi);
if (IS_ERR(cs35l56->base.regmap)) {
ret = PTR_ERR(cs35l56->base.regmap);

View File

@ -17,9 +17,10 @@
static int cs35l56_i2c_probe(struct i2c_client *client)
{
unsigned int id = (u32)(uintptr_t)i2c_get_match_data(client);
struct cs35l56_private *cs35l56;
struct device *dev = &client->dev;
const struct regmap_config *regmap_config = &cs35l56_regmap_i2c;
const struct regmap_config *regmap_config;
int ret;
cs35l56 = devm_kzalloc(dev, sizeof(struct cs35l56_private), GFP_KERNEL);
@ -30,6 +31,20 @@ static int cs35l56_i2c_probe(struct i2c_client *client)
cs35l56->base.can_hibernate = true;
i2c_set_clientdata(client, cs35l56);
switch (id) {
case 0x3556:
regmap_config = &cs35l56_regmap_i2c;
cs35l56->base.fw_reg = &cs35l56_fw_reg;
break;
case 0x3563:
regmap_config = &cs35l63_regmap_i2c;
cs35l56->base.fw_reg = &cs35l63_fw_reg;
break;
default:
return -ENODEV;
}
cs35l56->base.regmap = devm_regmap_init_i2c(client, regmap_config);
if (IS_ERR(cs35l56->base.regmap)) {
ret = PTR_ERR(cs35l56->base.regmap);
@ -57,14 +72,16 @@ static void cs35l56_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id cs35l56_id_i2c[] = {
{ "cs35l56" },
{ "cs35l56", 0x3556 },
{ "cs35l63", 0x3563 },
{}
};
MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c);
#ifdef CONFIG_ACPI
static const struct acpi_device_id cs35l56_asoc_acpi_match[] = {
{ "CSC355C", 0 },
{ "CSC355C", 0x3556 },
{ "CSC356C", 0x3563 },
{},
};
MODULE_DEVICE_TABLE(acpi, cs35l56_asoc_acpi_match);

View File

@ -393,6 +393,74 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral,
return 0;
}
static int cs35l63_sdw_kick_divider(struct cs35l56_private *cs35l56,
struct sdw_slave *peripheral)
{
unsigned int curr_scale_reg, next_scale_reg;
int curr_scale, next_scale, ret;
if (!cs35l56->base.init_done)
return 0;
if (peripheral->bus->params.curr_bank) {
curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1;
next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0;
} else {
curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0;
next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1;
}
/*
* Current clock scale value must be different to new value.
* Modify current to guarantee this. If next still has the dummy
* value we wrote when it was current, the core code has not set
* a new scale so restore its original good value
*/
curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg);
if (curr_scale < 0) {
dev_err(cs35l56->base.dev, "Failed to read current clock scale: %d\n", curr_scale);
return curr_scale;
}
next_scale = sdw_read_no_pm(peripheral, next_scale_reg);
if (next_scale < 0) {
dev_err(cs35l56->base.dev, "Failed to read next clock scale: %d\n", next_scale);
return next_scale;
}
if (next_scale == CS35L56_SDW_INVALID_BUS_SCALE) {
next_scale = cs35l56->old_sdw_clock_scale;
ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale);
if (ret < 0) {
dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n",
ret);
return ret;
}
}
cs35l56->old_sdw_clock_scale = curr_scale;
ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE);
if (ret < 0) {
dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", ret);
return ret;
}
dev_dbg(cs35l56->base.dev, "Next bus scale: %#x\n", next_scale);
return 0;
}
static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral,
struct sdw_bus_params *params)
{
struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
if ((cs35l56->base.type == 0x63) && (cs35l56->base.rev < 0xa1))
return cs35l63_sdw_kick_divider(cs35l56, peripheral);
return 0;
}
static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral,
enum sdw_clk_stop_mode mode,
enum sdw_clk_stop_type type)
@ -408,6 +476,7 @@ static const struct sdw_slave_ops cs35l56_sdw_ops = {
.read_prop = cs35l56_sdw_read_prop,
.interrupt_callback = cs35l56_sdw_interrupt,
.update_status = cs35l56_sdw_update_status,
.bus_config = cs35l56_sdw_bus_config,
#ifdef DEBUG
.clk_stop = cs35l56_sdw_clk_stop,
#endif
@ -509,6 +578,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi
{
struct device *dev = &peripheral->dev;
struct cs35l56_private *cs35l56;
const struct regmap_config *regmap_config;
int ret;
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
dev_set_drvdata(dev, cs35l56);
switch ((unsigned int)id->driver_data) {
case 0x3556:
case 0x3557:
regmap_config = &cs35l56_regmap_sdw;
cs35l56->base.fw_reg = &cs35l56_fw_reg;
break;
case 0x3563:
regmap_config = &cs35l63_regmap_sdw;
cs35l56->base.fw_reg = &cs35l63_fw_reg;
break;
default:
return -ENODEV;
}
cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw,
peripheral, &cs35l56_regmap_sdw);
peripheral, regmap_config);
if (IS_ERR(cs35l56->base.regmap)) {
ret = PTR_ERR(cs35l56->base.regmap);
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 = {
};
static const struct sdw_device_id cs35l56_sdw_id[] = {
SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0),
SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0),
SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0x3556),
SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557),
SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563),
{},
};
MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id);

View File

@ -38,17 +38,48 @@ static const struct reg_sequence cs35l56_patch[] = {
{ CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
{ CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 },
{ CS35L56_IRQ1_MASK_18, 0x1f7df0ff },
};
static const struct reg_sequence cs35l56_patch_fw[] = {
/* These are not reset by a soft-reset, so patch to defaults. */
{ CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 },
{ CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 },
{ CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 },
};
static const struct reg_sequence cs35l63_patch_fw[] = {
/* These are not reset by a soft-reset, so patch to defaults. */
{ CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 },
{ CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 },
{ CS35L63_MAIN_POSTURE_NUMBER, 0x00000000 },
};
int cs35l56_set_patch(struct cs35l56_base *cs35l56_base)
{
return regmap_register_patch(cs35l56_base->regmap, cs35l56_patch,
int ret;
ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch,
ARRAY_SIZE(cs35l56_patch));
if (ret)
return ret;
switch (cs35l56_base->type) {
case 0x54:
case 0x56:
case 0x57:
ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch_fw,
ARRAY_SIZE(cs35l56_patch_fw));
break;
case 0x63:
ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw,
ARRAY_SIZE(cs35l63_patch_fw));
break;
default:
break;
}
return ret;
}
EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, "SND_SOC_CS35L56_SHARED");
@ -82,6 +113,36 @@ static const struct reg_default cs35l56_reg_defaults[] = {
{ CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 },
};
static const struct reg_default cs35l63_reg_defaults[] = {
/* no defaults for OTP_MEM - first read populates cache */
{ CS35L56_ASP1_ENABLES1, 0x00000000 },
{ CS35L56_ASP1_CONTROL1, 0x00000028 },
{ CS35L56_ASP1_CONTROL2, 0x18180200 },
{ CS35L56_ASP1_CONTROL3, 0x00000002 },
{ CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 },
{ CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
{ CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
{ CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
{ CS35L56_ASP1TX1_INPUT, 0x00000000 },
{ CS35L56_ASP1TX2_INPUT, 0x00000000 },
{ CS35L56_ASP1TX3_INPUT, 0x00000000 },
{ CS35L56_ASP1TX4_INPUT, 0x00000000 },
{ CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
{ CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
{ CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
{ CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 },
{ CS35L56_IRQ1_MASK_1, 0x8003ffff },
{ CS35L56_IRQ1_MASK_2, 0xffff7fff },
{ CS35L56_IRQ1_MASK_4, 0xe0ffffff },
{ CS35L56_IRQ1_MASK_8, 0x8c000fff },
{ CS35L56_IRQ1_MASK_18, 0x0760f000 },
{ CS35L56_IRQ1_MASK_20, 0x15c00000 },
{ CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 },
{ CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 },
{ CS35L63_MAIN_POSTURE_NUMBER, 0x00000000 },
};
static bool cs35l56_is_dsp_memory(unsigned int reg)
{
switch (reg) {
@ -153,6 +214,8 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
case CS35L56_DSP_VIRTUAL1_MBOX_6:
case CS35L56_DSP_VIRTUAL1_MBOX_7:
case CS35L56_DSP_VIRTUAL1_MBOX_8:
case CS35L56_DIE_STS1:
case CS35L56_DIE_STS2:
case CS35L56_DSP_RESTRICT_STS1:
case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END:
case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0:
@ -179,7 +242,7 @@ static bool cs35l56_precious_reg(struct device *dev, unsigned int reg)
}
}
static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
static bool cs35l56_common_volatile_reg(unsigned int reg)
{
switch (reg) {
case CS35L56_DEVID:
@ -217,12 +280,32 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
case CS35L56_DSP1_SCRATCH3:
case CS35L56_DSP1_SCRATCH4:
return true;
default:
return cs35l56_is_dsp_memory(reg);
}
}
static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS35L56_MAIN_RENDER_USER_MUTE:
case CS35L56_MAIN_RENDER_USER_VOLUME:
case CS35L56_MAIN_POSTURE_NUMBER:
return false;
default:
return cs35l56_is_dsp_memory(reg);
return cs35l56_common_volatile_reg(reg);
}
}
static bool cs35l63_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS35L63_MAIN_RENDER_USER_MUTE:
case CS35L63_MAIN_RENDER_USER_VOLUME:
case CS35L63_MAIN_POSTURE_NUMBER:
return false;
default:
return cs35l56_common_volatile_reg(reg);
}
}
@ -253,7 +336,8 @@ int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base)
if (ret)
return ret;
ret = regmap_read_poll_timeout(cs35l56_base->regmap, CS35L56_DSP1_PM_CUR_STATE,
ret = regmap_read_poll_timeout(cs35l56_base->regmap,
cs35l56_base->fw_reg->pm_cur_stat,
val, (val == CS35L56_HALO_STATE_SHUTDOWN),
CS35L56_HALO_STATE_POLL_US,
CS35L56_HALO_STATE_TIMEOUT_US);
@ -278,7 +362,9 @@ int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base)
CS35L56_HALO_STATE_POLL_US,
CS35L56_HALO_STATE_TIMEOUT_US,
false,
cs35l56_base->regmap, CS35L56_DSP1_HALO_STATE, &val);
cs35l56_base->regmap,
cs35l56_base->fw_reg->halo_state,
&val);
if (poll_ret) {
dev_err(cs35l56_base->dev, "Firmware boot timed out(%d): HALO_STATE=%#x\n",
@ -382,6 +468,11 @@ static const struct reg_sequence cs35l56_system_reset_seq[] = {
REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
};
static const struct reg_sequence cs35l63_system_reset_seq[] = {
REG_SEQ0(CS35L63_DSP1_HALO_STATE, 0),
REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
};
void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire)
{
/*
@ -395,9 +486,22 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire)
return;
}
regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
cs35l56_system_reset_seq,
ARRAY_SIZE(cs35l56_system_reset_seq));
switch (cs35l56_base->type) {
case 0x54:
case 0x56:
case 0x57:
regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
cs35l56_system_reset_seq,
ARRAY_SIZE(cs35l56_system_reset_seq));
break;
case 0x63:
regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
cs35l63_system_reset_seq,
ARRAY_SIZE(cs35l63_system_reset_seq));
break;
default:
break;
}
/* On SoundWire the registers won't be accessible until it re-enumerates. */
if (is_soundwire)
@ -514,7 +618,9 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base)
return ret;
}
ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &val);
ret = regmap_read(cs35l56_base->regmap,
cs35l56_base->fw_reg->prot_sts,
&val);
if (ret)
dev_err(cs35l56_base->dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
else
@ -562,7 +668,7 @@ int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base)
/* Firmware must have entered a power-save state */
ret = regmap_read_poll_timeout(cs35l56_base->regmap,
CS35L56_TRANSDUCER_ACTUAL_PS,
cs35l56_base->fw_reg->transducer_actual_ps,
val, (val >= CS35L56_PS3),
CS35L56_PS3_POLL_US,
CS35L56_PS3_TIMEOUT_US);
@ -698,13 +804,29 @@ static int cs35l56_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid)
unique_id |= (u32)pte.x | ((u32)pte.y << 8) | ((u32)pte.wafer_id << 16) |
((u32)pte.dvs << 24);
dev_dbg(cs35l56_base->dev, "UniqueID = %#llx\n", unique_id);
*uid = unique_id;
return 0;
}
static int cs35l63_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid)
{
u32 tmp[2];
int ret;
ret = regmap_bulk_read(cs35l56_base->regmap, CS35L56_DIE_STS1, tmp, ARRAY_SIZE(tmp));
if (ret) {
dev_err(cs35l56_base->dev, "Cannot obtain CS35L56_DIE_STS: %d\n", ret);
return ret;
}
*uid = tmp[1];
*uid <<= 32;
*uid |= tmp[0];
return 0;
}
/* Firmware calibration controls */
const struct cirrus_amp_cal_controls cs35l56_calibration_controls = {
.alg_id = 0x9f210,
@ -725,10 +847,25 @@ int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base)
if (cs35l56_base->secured)
return 0;
ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid);
switch (cs35l56_base->type) {
case 0x54:
case 0x56:
case 0x57:
ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid);
break;
case 0x63:
ret = cs35l63_read_silicon_uid(cs35l56_base, &silicon_uid);
break;
default:
ret = -ENODEV;
break;
}
if (ret < 0)
return ret;
dev_dbg(cs35l56_base->dev, "UniqueID = %#llx\n", silicon_uid);
ret = cs_amp_get_efi_calibration_data(cs35l56_base->dev, silicon_uid,
cs35l56_base->cal_index,
&cs35l56_base->cal_data);
@ -752,7 +889,8 @@ int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
unsigned int prot_status;
int ret;
ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &prot_status);
ret = regmap_read(cs35l56_base->regmap,
cs35l56_base->fw_reg->prot_sts, &prot_status);
if (ret) {
dev_err(cs35l56_base->dev, "Get PROTECTION_STATUS failed: %d\n", ret);
return ret;
@ -760,7 +898,8 @@ int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
*fw_missing = !!(prot_status & CS35L56_FIRMWARE_MISSING);
ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP1_FW_VER, fw_version);
ret = regmap_read(cs35l56_base->regmap,
cs35l56_base->fw_reg->fw_ver, fw_version);
if (ret) {
dev_err(cs35l56_base->dev, "Get FW VER failed: %d\n", ret);
return ret;
@ -809,6 +948,9 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
case 0x35A56:
case 0x35A57:
break;
case 0x35A630:
devid = devid >> 4;
break;
default:
dev_err(cs35l56_base->dev, "Unknown device %x\n", devid);
return ret;
@ -1045,6 +1187,63 @@ const struct regmap_config cs35l56_regmap_sdw = {
};
EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_sdw, "SND_SOC_CS35L56_SHARED");
const struct regmap_config cs35l63_regmap_i2c = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.reg_base = 0x8000,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.val_format_endian = REGMAP_ENDIAN_BIG,
.max_register = CS35L56_DSP1_PMEM_5114,
.reg_defaults = cs35l63_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs35l63_reg_defaults),
.volatile_reg = cs35l63_volatile_reg,
.readable_reg = cs35l56_readable_reg,
.precious_reg = cs35l56_precious_reg,
.cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_NS_GPL(cs35l63_regmap_i2c, "SND_SOC_CS35L56_SHARED");
const struct regmap_config cs35l63_regmap_sdw = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
.val_format_endian = REGMAP_ENDIAN_BIG,
.max_register = CS35L56_DSP1_PMEM_5114,
.reg_defaults = cs35l63_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs35l63_reg_defaults),
.volatile_reg = cs35l63_volatile_reg,
.readable_reg = cs35l56_readable_reg,
.precious_reg = cs35l56_precious_reg,
.cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_NS_GPL(cs35l63_regmap_sdw, "SND_SOC_CS35L56_SHARED");
const struct cs35l56_fw_reg cs35l56_fw_reg = {
.fw_ver = CS35L56_DSP1_FW_VER,
.halo_state = CS35L56_DSP1_HALO_STATE,
.pm_cur_stat = CS35L56_DSP1_PM_CUR_STATE,
.prot_sts = CS35L56_PROTECTION_STATUS,
.transducer_actual_ps = CS35L56_TRANSDUCER_ACTUAL_PS,
.user_mute = CS35L56_MAIN_RENDER_USER_MUTE,
.user_volume = CS35L56_MAIN_RENDER_USER_VOLUME,
.posture_number = CS35L56_MAIN_POSTURE_NUMBER,
};
EXPORT_SYMBOL_NS_GPL(cs35l56_fw_reg, "SND_SOC_CS35L56_SHARED");
const struct cs35l56_fw_reg cs35l63_fw_reg = {
.fw_ver = CS35L63_DSP1_FW_VER,
.halo_state = CS35L63_DSP1_HALO_STATE,
.pm_cur_stat = CS35L63_DSP1_PM_CUR_STATE,
.prot_sts = CS35L63_PROTECTION_STATUS,
.transducer_actual_ps = CS35L63_TRANSDUCER_ACTUAL_PS,
.user_mute = CS35L63_MAIN_RENDER_USER_MUTE,
.user_volume = CS35L63_MAIN_RENDER_USER_VOLUME,
.posture_number = CS35L63_MAIN_POSTURE_NUMBER,
};
EXPORT_SYMBOL_NS_GPL(cs35l63_fw_reg, "SND_SOC_CS35L56_SHARED");
MODULE_DESCRIPTION("ASoC CS35L56 Shared");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");

View File

@ -25,6 +25,9 @@ static int cs35l56_spi_probe(struct spi_device *spi)
return -ENOMEM;
spi_set_drvdata(spi, cs35l56);
cs35l56->base.fw_reg = &cs35l56_fw_reg;
cs35l56->base.regmap = devm_regmap_init_spi(spi, regmap_config);
if (IS_ERR(cs35l56->base.regmap)) {
ret = PTR_ERR(cs35l56->base.regmap);

View File

@ -84,6 +84,25 @@ static const struct snd_kcontrol_new cs35l56_controls[] = {
cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
};
static const struct snd_kcontrol_new cs35l63_controls[] = {
SOC_SINGLE_EXT("Speaker Switch",
CS35L63_MAIN_RENDER_USER_MUTE, 0, 1, 1,
cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
SOC_SINGLE_S_EXT_TLV("Speaker Volume",
CS35L63_MAIN_RENDER_USER_VOLUME,
CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT,
CS35L56_MAIN_RENDER_USER_VOLUME_MIN,
CS35L56_MAIN_RENDER_USER_VOLUME_MAX,
CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT,
0,
cs35l56_dspwait_get_volsw,
cs35l56_dspwait_put_volsw,
vol_tlv),
SOC_SINGLE_EXT("Posture Number", CS35L63_MAIN_POSTURE_NUMBER,
0, 255, 0,
cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
};
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum,
CS35L56_ASP1TX1_INPUT,
0, CS35L56_ASP_TXn_SRC_MASK,
@ -174,7 +193,7 @@ static int cs35l56_play_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
/* Wait for firmware to enter PS0 power state */
ret = regmap_read_poll_timeout(cs35l56->base.regmap,
CS35L56_TRANSDUCER_ACTUAL_PS,
cs35l56->base.fw_reg->transducer_actual_ps,
val, (val == CS35L56_PS0),
CS35L56_PS0_POLL_US,
CS35L56_PS0_TIMEOUT_US);
@ -760,7 +779,8 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing
goto err_unlock;
}
regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS,
regmap_clear_bits(cs35l56->base.regmap,
cs35l56->base.fw_reg->prot_sts,
CS35L56_FIRMWARE_MISSING);
cs35l56->base.fw_patched = true;
@ -837,6 +857,7 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
struct dentry *debugfs_root = component->debugfs_root;
unsigned short vendor, device;
int ret;
BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
@ -876,6 +897,26 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->base.can_hibernate);
debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->base.fw_patched);
switch (cs35l56->base.type) {
case 0x54:
case 0x56:
case 0x57:
ret = snd_soc_add_component_controls(component, cs35l56_controls,
ARRAY_SIZE(cs35l56_controls));
break;
case 0x63:
ret = snd_soc_add_component_controls(component, cs35l63_controls,
ARRAY_SIZE(cs35l63_controls));
break;
default:
ret = -ENODEV;
break;
}
if (ret)
return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n");
queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
return 0;
@ -931,8 +972,6 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l56 = {
.num_dapm_widgets = ARRAY_SIZE(cs35l56_dapm_widgets),
.dapm_routes = cs35l56_audio_map,
.num_dapm_routes = ARRAY_SIZE(cs35l56_audio_map),
.controls = cs35l56_controls,
.num_controls = ARRAY_SIZE(cs35l56_controls),
.set_bias_level = cs35l56_set_bias_level,

View File

@ -51,6 +51,7 @@ struct cs35l56_private {
u8 asp_slot_count;
bool tdm_mode;
bool sysclk_set;
u8 old_sdw_clock_scale;
};
extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi;