Commit dfd2ffb3 authored by Richard Fitzgerald's avatar Richard Fitzgerald Committed by Mark Brown

ASoC: cs35l56: Prevent overwriting firmware ASP config

Only populate the ASP1 config registers in the regmap cache if the
ASP DAI is used. This prevents regcache_sync() from overwriting
these registers with their defaults when the firmware owns
control of these registers.

On a SoundWire system the ASP could be owned by the firmware to
share reference audio with the firmware on other cs35l56. Or it
can be used as a normal codec-codec interface owned by the driver.
The driver must not overwrite the registers if the firmware has
control of them.

The original implementation for this in commit 07f7d6e7
("ASoC: cs35l56: Fix for initializing ASP1 mixer registers") was
to still provide defaults for these registers, assuming that if
they were never reconfigured from defaults then regcache_sync()
would not write them out because they are not dirty. Unfortunately
regcache_sync() is not that smart. If the chip has not reset (so
the driver has not called regcache_mark_dirty()) a regcache_sync()
could write out registers that are not dirty.

To avoid accidental overwriting of the ASP registers, they are
removed from the table of defaults and instead are populated with
defaults only if one of the ASP DAI configuration functions is
called. So if the DAI has never been configured, the firmware is
assumed to have ownership of these registers, and the regmap cache
will not contain any entries for them.
Signed-off-by: default avatarRichard Fitzgerald <rf@opensource.cirrus.com>
Fixes: 07f7d6e7 ("ASoC: cs35l56: Fix for initializing ASP1 mixer registers")
Link: https://msgid.link/r/20240408101803.43183-5-rf@opensource.cirrus.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent d4884fd4
...@@ -267,6 +267,7 @@ struct cs35l56_base { ...@@ -267,6 +267,7 @@ struct cs35l56_base {
bool fw_patched; bool fw_patched;
bool secured; bool secured;
bool can_hibernate; bool can_hibernate;
bool fw_owns_asp1;
bool cal_data_valid; bool cal_data_valid;
s8 cal_index; s8 cal_index;
struct cirrus_amp_cal_data cal_data; struct cirrus_amp_cal_data cal_data;
...@@ -283,6 +284,7 @@ extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC]; ...@@ -283,6 +284,7 @@ extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC];
extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC]; extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC];
int cs35l56_set_patch(struct cs35l56_base *cs35l56_base); int cs35l56_set_patch(struct cs35l56_base *cs35l56_base);
int cs35l56_init_asp1_regs_for_driver_control(struct cs35l56_base *cs35l56_base);
int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base); int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base);
int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command); int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command);
int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base); int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base);
......
...@@ -40,16 +40,11 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED); ...@@ -40,16 +40,11 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED);
static const struct reg_default cs35l56_reg_defaults[] = { static const struct reg_default cs35l56_reg_defaults[] = {
/* no defaults for OTP_MEM - first read populates cache */ /* no defaults for OTP_MEM - first read populates cache */
{ CS35L56_ASP1_ENABLES1, 0x00000000 }, /*
{ CS35L56_ASP1_CONTROL1, 0x00000028 }, * No defaults for ASP1 control or ASP1TX mixer. See
{ CS35L56_ASP1_CONTROL2, 0x18180200 }, * cs35l56_populate_asp1_register_defaults() and
{ CS35L56_ASP1_CONTROL3, 0x00000002 }, * cs35l56_sync_asp1_mixer_widgets_with_firmware().
{ CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 }, */
{ CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
{ CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
{ CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
/* no defaults for ASP1TX mixer */
{ CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 }, { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
{ CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
...@@ -210,6 +205,36 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) ...@@ -210,6 +205,36 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
} }
} }
static const struct reg_sequence cs35l56_asp1_defaults[] = {
REG_SEQ0(CS35L56_ASP1_ENABLES1, 0x00000000),
REG_SEQ0(CS35L56_ASP1_CONTROL1, 0x00000028),
REG_SEQ0(CS35L56_ASP1_CONTROL2, 0x18180200),
REG_SEQ0(CS35L56_ASP1_CONTROL3, 0x00000002),
REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL1, 0x03020100),
REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL5, 0x00020100),
REG_SEQ0(CS35L56_ASP1_DATA_CONTROL1, 0x00000018),
REG_SEQ0(CS35L56_ASP1_DATA_CONTROL5, 0x00000018),
};
/*
* The firmware can have control of the ASP so we don't provide regmap
* with defaults for these registers, to prevent a regcache_sync() from
* overwriting the firmware settings. But if the machine driver hooks up
* the ASP it means the driver is taking control of the ASP, so then the
* registers are populated with the defaults.
*/
int cs35l56_init_asp1_regs_for_driver_control(struct cs35l56_base *cs35l56_base)
{
if (!cs35l56_base->fw_owns_asp1)
return 0;
cs35l56_base->fw_owns_asp1 = false;
return regmap_multi_reg_write(cs35l56_base->regmap, cs35l56_asp1_defaults,
ARRAY_SIZE(cs35l56_asp1_defaults));
}
EXPORT_SYMBOL_NS_GPL(cs35l56_init_asp1_regs_for_driver_control, SND_SOC_CS35L56_SHARED);
/* /*
* The firmware boot sequence can overwrite the ASP1 config registers so that * The firmware boot sequence can overwrite the ASP1 config registers so that
* they don't match regmap's view of their values. Rewrite the values from the * they don't match regmap's view of their values. Rewrite the values from the
...@@ -217,19 +242,15 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) ...@@ -217,19 +242,15 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
*/ */
int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base) int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base)
{ {
struct reg_sequence asp1_regs[] = { struct reg_sequence asp1_regs[ARRAY_SIZE(cs35l56_asp1_defaults)];
{ .reg = CS35L56_ASP1_ENABLES1 },
{ .reg = CS35L56_ASP1_CONTROL1 },
{ .reg = CS35L56_ASP1_CONTROL2 },
{ .reg = CS35L56_ASP1_CONTROL3 },
{ .reg = CS35L56_ASP1_FRAME_CONTROL1 },
{ .reg = CS35L56_ASP1_FRAME_CONTROL5 },
{ .reg = CS35L56_ASP1_DATA_CONTROL1 },
{ .reg = CS35L56_ASP1_DATA_CONTROL5 },
};
int i, ret; int i, ret;
/* Read values from regmap cache into a write sequence */ if (cs35l56_base->fw_owns_asp1)
return 0;
memcpy(asp1_regs, cs35l56_asp1_defaults, sizeof(asp1_regs));
/* Read current values from regmap cache into the write sequence */
for (i = 0; i < ARRAY_SIZE(asp1_regs); ++i) { for (i = 0; i < ARRAY_SIZE(asp1_regs); ++i) {
ret = regmap_read(cs35l56_base->regmap, asp1_regs[i].reg, &asp1_regs[i].def); ret = regmap_read(cs35l56_base->regmap, asp1_regs[i].reg, &asp1_regs[i].def);
if (ret) if (ret)
......
...@@ -454,9 +454,14 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f ...@@ -454,9 +454,14 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f
{ {
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component); struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component);
unsigned int val; unsigned int val;
int ret;
dev_dbg(cs35l56->base.dev, "%s: %#x\n", __func__, fmt); dev_dbg(cs35l56->base.dev, "%s: %#x\n", __func__, fmt);
ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base);
if (ret)
return ret;
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
case SND_SOC_DAIFMT_CBC_CFC: case SND_SOC_DAIFMT_CBC_CFC:
break; break;
...@@ -530,6 +535,11 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx ...@@ -530,6 +535,11 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx
unsigned int rx_mask, int slots, int slot_width) unsigned int rx_mask, int slots, int slot_width)
{ {
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
int ret;
ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base);
if (ret)
return ret;
if ((slots == 0) || (slot_width == 0)) { if ((slots == 0) || (slot_width == 0)) {
dev_dbg(cs35l56->base.dev, "tdm config cleared\n"); dev_dbg(cs35l56->base.dev, "tdm config cleared\n");
...@@ -578,6 +588,11 @@ static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream, ...@@ -578,6 +588,11 @@ static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream,
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
unsigned int rate = params_rate(params); unsigned int rate = params_rate(params);
u8 asp_width, asp_wl; u8 asp_width, asp_wl;
int ret;
ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base);
if (ret)
return ret;
asp_wl = params_width(params); asp_wl = params_width(params);
if (cs35l56->asp_slot_width) if (cs35l56->asp_slot_width)
...@@ -634,7 +649,11 @@ static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai, ...@@ -634,7 +649,11 @@ static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir) int clk_id, unsigned int freq, int dir)
{ {
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
int freq_id; int freq_id, ret;
ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base);
if (ret)
return ret;
if (freq == 0) { if (freq == 0) {
cs35l56->sysclk_set = false; cs35l56->sysclk_set = false;
...@@ -1403,6 +1422,9 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) ...@@ -1403,6 +1422,9 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
cs35l56->base.cal_index = -1; cs35l56->base.cal_index = -1;
cs35l56->speaker_id = -ENOENT; cs35l56->speaker_id = -ENOENT;
/* Assume that the firmware owns ASP1 until we know different */
cs35l56->base.fw_owns_asp1 = true;
dev_set_drvdata(cs35l56->base.dev, cs35l56); dev_set_drvdata(cs35l56->base.dev, cs35l56);
cs35l56_fill_supply_names(cs35l56->supplies); cs35l56_fill_supply_names(cs35l56->supplies);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment