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

ASoC: cs35l56: Firmware file must match the version of preloaded firmware

Check during initialization whether the firmware is already patched.
If so, include the firmware version in the wm_adsp fwf_name string.

If the firmware has already been patched by the BIOS the driver
can only replace it if it has control of hard RESET.

If the driver cannot replace the firmware, it can still load a wmfw
(for ALSA control definitions) and/or a bin (for additional tunings).
But these must match the version of firmware that is running on the
CS35L56.

The firmware is pre-patched if FIRMWARE_MISSING == 0.

Including the firmware version in the fwf_name string will
qualify the firmware file name:

Normal (unpatched or replaceable firmware):
  cs35l56-rev-dsp1-misc[-system_name].[wmfw|bin]

Preloaded firmware:
  cs35l56-rev[-s]-VVVVVV-dsp1-misc[-system_name].[wmfw|bin]

Where:
   [-s] is an optional -s added into the name for a secured CS35L56
   VVVVVV is the 24-bit firmware version in hexadecimal.
Signed-off-by: default avatarRichard Fitzgerald <rf@opensource.cirrus.com>
Fixes: 608f1b0d ("ASoC: cs35l56: Move DSP part string generation so that it is done only once")
Link: https://msgid.link/r/20240129162737.497-13-rf@opensource.cirrus.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent f6c96794
...@@ -75,6 +75,7 @@ ...@@ -75,6 +75,7 @@
#define CS35L56_DSP1_AHBM_WINDOW_DEBUG_0 0x25E2040 #define CS35L56_DSP1_AHBM_WINDOW_DEBUG_0 0x25E2040
#define CS35L56_DSP1_AHBM_WINDOW_DEBUG_1 0x25E2044 #define CS35L56_DSP1_AHBM_WINDOW_DEBUG_1 0x25E2044
#define CS35L56_DSP1_XMEM_UNPACKED24_0 0x2800000 #define CS35L56_DSP1_XMEM_UNPACKED24_0 0x2800000
#define CS35L56_DSP1_FW_VER 0x2800010
#define CS35L56_DSP1_HALO_STATE_A1 0x2801E58 #define CS35L56_DSP1_HALO_STATE_A1 0x2801E58
#define CS35L56_DSP1_HALO_STATE 0x28021E0 #define CS35L56_DSP1_HALO_STATE 0x28021E0
#define CS35L56_DSP1_PM_CUR_STATE_A1 0x2804000 #define CS35L56_DSP1_PM_CUR_STATE_A1 0x2804000
...@@ -285,6 +286,8 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base); ...@@ -285,6 +286,8 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base);
int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base); int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base);
int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire); int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire);
void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp); void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
bool *fw_missing, unsigned int *fw_version);
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base); int cs35l56_hw_init(struct cs35l56_base *cs35l56_base);
int cs35l56_get_bclk_freq_id(unsigned int freq); int cs35l56_get_bclk_freq_id(unsigned int freq);
void cs35l56_fill_supply_names(struct regulator_bulk_data *data); void cs35l56_fill_supply_names(struct regulator_bulk_data *data);
......
...@@ -628,10 +628,35 @@ void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_ds ...@@ -628,10 +628,35 @@ void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_ds
} }
EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, SND_SOC_CS35L56_SHARED); EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, SND_SOC_CS35L56_SHARED);
int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
bool *fw_missing, unsigned int *fw_version)
{
unsigned int prot_status;
int ret;
ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &prot_status);
if (ret) {
dev_err(cs35l56_base->dev, "Get PROTECTION_STATUS failed: %d\n", ret);
return ret;
}
*fw_missing = !!(prot_status & CS35L56_FIRMWARE_MISSING);
ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP1_FW_VER, fw_version);
if (ret) {
dev_err(cs35l56_base->dev, "Get FW VER failed: %d\n", ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_NS_GPL(cs35l56_read_prot_status, SND_SOC_CS35L56_SHARED);
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
{ {
int ret; int ret;
unsigned int devid, revid, otpid, secured; unsigned int devid, revid, otpid, secured, fw_ver;
bool fw_missing;
/* /*
* When the system is not using a reset_gpio ensure the device is * When the system is not using a reset_gpio ensure the device is
...@@ -690,8 +715,13 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) ...@@ -690,8 +715,13 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
return ret; return ret;
} }
dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n", ret = cs35l56_read_prot_status(cs35l56_base, &fw_missing, &fw_ver);
cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid); if (ret)
return ret;
dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n",
cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid,
fw_ver >> 16, (fw_ver >> 8) & 0xff, fw_ver & 0xff, !fw_missing);
/* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */ /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */
regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
......
...@@ -804,7 +804,7 @@ static struct snd_soc_dai_driver cs35l56_dai[] = { ...@@ -804,7 +804,7 @@ static struct snd_soc_dai_driver cs35l56_dai[] = {
} }
}; };
static void cs35l56_secure_patch(struct cs35l56_private *cs35l56) static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56)
{ {
int ret; int ret;
...@@ -816,19 +816,10 @@ static void cs35l56_secure_patch(struct cs35l56_private *cs35l56) ...@@ -816,19 +816,10 @@ static void cs35l56_secure_patch(struct cs35l56_private *cs35l56)
cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
} }
static void cs35l56_patch(struct cs35l56_private *cs35l56) static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing)
{ {
unsigned int firmware_missing;
int ret; int ret;
ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing);
if (ret) {
dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
return;
}
firmware_missing &= CS35L56_FIRMWARE_MISSING;
/* /*
* Disable SoundWire interrupts to prevent race with IRQ work. * Disable SoundWire interrupts to prevent race with IRQ work.
* Setting sdw_irq_no_unmask prevents the handler re-enabling * Setting sdw_irq_no_unmask prevents the handler re-enabling
...@@ -901,34 +892,49 @@ static void cs35l56_dsp_work(struct work_struct *work) ...@@ -901,34 +892,49 @@ static void cs35l56_dsp_work(struct work_struct *work)
struct cs35l56_private *cs35l56 = container_of(work, struct cs35l56_private *cs35l56 = container_of(work,
struct cs35l56_private, struct cs35l56_private,
dsp_work); dsp_work);
unsigned int firmware_version;
bool firmware_missing;
int ret;
if (!cs35l56->base.init_done) if (!cs35l56->base.init_done)
return; return;
pm_runtime_get_sync(cs35l56->base.dev); pm_runtime_get_sync(cs35l56->base.dev);
ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &firmware_version);
if (ret)
goto err;
/* Populate fw file qualifier with the revision and security state */ /* Populate fw file qualifier with the revision and security state */
if (!cs35l56->dsp.fwf_name) { kfree(cs35l56->dsp.fwf_name);
cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL, "%02x%s-dsp1", if (firmware_missing) {
cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL, "%02x-dsp1", cs35l56->base.rev);
} else {
/* Firmware files must match the running firmware version */
cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL,
"%02x%s-%06x-dsp1",
cs35l56->base.rev, cs35l56->base.rev,
cs35l56->base.secured ? "-s" : ""); cs35l56->base.secured ? "-s" : "",
if (!cs35l56->dsp.fwf_name) firmware_version);
goto err;
} }
if (!cs35l56->dsp.fwf_name)
goto err;
dev_dbg(cs35l56->base.dev, "DSP fwf name: '%s' system name: '%s'\n", dev_dbg(cs35l56->base.dev, "DSP fwf name: '%s' system name: '%s'\n",
cs35l56->dsp.fwf_name, cs35l56->dsp.system_name); cs35l56->dsp.fwf_name, cs35l56->dsp.system_name);
/* /*
* When the device is running in secure mode the firmware files can * The firmware cannot be patched if it is already running from
* only contain insecure tunings and therefore we do not need to * patch RAM. In this case the firmware files are versioned to
* shutdown the firmware to apply them and can use the lower cost * match the running firmware version and will only contain
* reinit sequence instead. * tunings. We do not need to shutdown the firmware to apply
* tunings so can use the lower cost reinit sequence instead.
*/ */
if (cs35l56->base.secured) if (!firmware_missing)
cs35l56_secure_patch(cs35l56); cs35l56_reinit_patch(cs35l56);
else else
cs35l56_patch(cs35l56); cs35l56_patch(cs35l56, firmware_missing);
/* /*
......
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