Commit 6f8ad048 authored by Richard Fitzgerald's avatar Richard Fitzgerald Committed by Mark Brown

ALSA: hda: cs35l56: Firmware file must match the version of preloaded firmware

Check whether the firmware is already patched. If so, include the
firmware version in the firmware file name.

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 either:
 - FIRMWARE_MISSING == 0, or
 - it is a secured CS35L56 (which implies that is was already patched),

cs35l56_hw_init() will set preloaded_fw_ver to the (non-zero)
firmware version if either of these conditions is true.

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.

Backport note:
This won't apply to kernel versions older than v6.6.
Signed-off-by: default avatarRichard Fitzgerald <rf@opensource.cirrus.com>
Fixes: 73cfbfa9 ("ALSA: hda/cs35l56: Add driver for Cirrus Logic CS35L56 amplifier")
Link: https://msgid.link/r/20240129162737.497-18-rf@opensource.cirrus.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent e82bc517
...@@ -397,7 +397,7 @@ static const struct cs_dsp_client_ops cs35l56_hda_client_ops = { ...@@ -397,7 +397,7 @@ static const struct cs_dsp_client_ops cs35l56_hda_client_ops = {
static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56, static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56,
const struct firmware **firmware, char **filename, const struct firmware **firmware, char **filename,
const char *dir, const char *system_name, const char *base_name, const char *system_name,
const char *amp_name, const char *amp_name,
const char *filetype) const char *filetype)
{ {
...@@ -405,20 +405,13 @@ static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56, ...@@ -405,20 +405,13 @@ static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56,
int ret = 0; int ret = 0;
if (system_name && amp_name) if (system_name && amp_name)
*filename = kasprintf(GFP_KERNEL, "%scs35l56-%02x%s-dsp1-misc-%s-%s.%s", dir, *filename = kasprintf(GFP_KERNEL, "%s-%s-%s.%s", base_name,
cs35l56->base.rev,
cs35l56->base.secured ? "-s" : "",
system_name, amp_name, filetype); system_name, amp_name, filetype);
else if (system_name) else if (system_name)
*filename = kasprintf(GFP_KERNEL, "%scs35l56-%02x%s-dsp1-misc-%s.%s", dir, *filename = kasprintf(GFP_KERNEL, "%s-%s.%s", base_name,
cs35l56->base.rev,
cs35l56->base.secured ? "-s" : "",
system_name, filetype); system_name, filetype);
else else
*filename = kasprintf(GFP_KERNEL, "%scs35l56-%02x%s-dsp1-misc.%s", dir, *filename = kasprintf(GFP_KERNEL, "%s.%s", base_name, filetype);
cs35l56->base.rev,
cs35l56->base.secured ? "-s" : "",
filetype);
if (!*filename) if (!*filename)
return -ENOMEM; return -ENOMEM;
...@@ -451,8 +444,8 @@ static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56, ...@@ -451,8 +444,8 @@ static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56,
return 0; return 0;
} }
static const char cirrus_dir[] = "cirrus/";
static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56, static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
unsigned int preloaded_fw_ver,
const struct firmware **wmfw_firmware, const struct firmware **wmfw_firmware,
char **wmfw_filename, char **wmfw_filename,
const struct firmware **coeff_firmware, const struct firmware **coeff_firmware,
...@@ -460,29 +453,43 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56, ...@@ -460,29 +453,43 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
{ {
const char *system_name = cs35l56->system_name; const char *system_name = cs35l56->system_name;
const char *amp_name = cs35l56->amp_name; const char *amp_name = cs35l56->amp_name;
char base_name[37];
int ret; int ret;
if (preloaded_fw_ver) {
snprintf(base_name, sizeof(base_name),
"cirrus/cs35l56-%02x%s-%06x-dsp1-misc",
cs35l56->base.rev,
cs35l56->base.secured ? "-s" : "",
preloaded_fw_ver & 0xffffff);
} else {
snprintf(base_name, sizeof(base_name),
"cirrus/cs35l56-%02x%s-dsp1-misc",
cs35l56->base.rev,
cs35l56->base.secured ? "-s" : "");
}
if (system_name && amp_name) { if (system_name && amp_name) {
if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
cirrus_dir, system_name, amp_name, "wmfw")) { base_name, system_name, amp_name, "wmfw")) {
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
cirrus_dir, system_name, amp_name, "bin"); base_name, system_name, amp_name, "bin");
return; return;
} }
} }
if (system_name) { if (system_name) {
if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
cirrus_dir, system_name, NULL, "wmfw")) { base_name, system_name, NULL, "wmfw")) {
if (amp_name) if (amp_name)
cs35l56_hda_request_firmware_file(cs35l56, cs35l56_hda_request_firmware_file(cs35l56,
coeff_firmware, coeff_filename, coeff_firmware, coeff_filename,
cirrus_dir, system_name, base_name, system_name,
amp_name, "bin"); amp_name, "bin");
if (!*coeff_firmware) if (!*coeff_firmware)
cs35l56_hda_request_firmware_file(cs35l56, cs35l56_hda_request_firmware_file(cs35l56,
coeff_firmware, coeff_filename, coeff_firmware, coeff_filename,
cirrus_dir, system_name, base_name, system_name,
NULL, "bin"); NULL, "bin");
return; return;
} }
...@@ -493,26 +500,26 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56, ...@@ -493,26 +500,26 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
*/ */
if (amp_name) if (amp_name)
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
cirrus_dir, system_name, amp_name, "bin"); base_name, system_name, amp_name, "bin");
if (!*coeff_firmware) if (!*coeff_firmware)
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
cirrus_dir, system_name, NULL, "bin"); base_name, system_name, NULL, "bin");
if (*coeff_firmware) if (*coeff_firmware)
return; return;
} }
ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
cirrus_dir, NULL, NULL, "wmfw"); base_name, NULL, NULL, "wmfw");
if (!ret) { if (!ret) {
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
cirrus_dir, NULL, NULL, "bin"); base_name, NULL, NULL, "bin");
return; return;
} }
if (!*coeff_firmware) if (!*coeff_firmware)
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
cirrus_dir, NULL, NULL, "bin"); base_name, NULL, NULL, "bin");
} }
static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware, static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware,
...@@ -546,7 +553,8 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) ...@@ -546,7 +553,8 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
const struct firmware *wmfw_firmware = NULL; const struct firmware *wmfw_firmware = NULL;
char *coeff_filename = NULL; char *coeff_filename = NULL;
char *wmfw_filename = NULL; char *wmfw_filename = NULL;
unsigned int firmware_missing; unsigned int preloaded_fw_ver;
bool firmware_missing;
int ret = 0; int ret = 0;
/* Prepare for a new DSP power-up */ /* Prepare for a new DSP power-up */
...@@ -557,24 +565,21 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) ...@@ -557,24 +565,21 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
pm_runtime_get_sync(cs35l56->base.dev); pm_runtime_get_sync(cs35l56->base.dev);
ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing); /*
if (ret) { * The firmware can only be upgraded if it is currently running
dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret); * from the built-in ROM. If not, the wmfw/bin must be for the
* version of firmware that is running on the chip.
*/
ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &preloaded_fw_ver);
if (ret)
goto err_pm_put; goto err_pm_put;
}
firmware_missing &= CS35L56_FIRMWARE_MISSING; if (firmware_missing)
preloaded_fw_ver = 0;
/* cs35l56_hda_request_firmware_files(cs35l56, preloaded_fw_ver,
* Firmware can only be downloaded if the CS35L56 is secured or is &wmfw_firmware, &wmfw_filename,
* running from the built-in ROM. If it is secured the BIOS will have
* downloaded firmware, and the wmfw/bin files will only contain
* tunings that are safe to download with the firmware running.
*/
if (cs35l56->base.secured || firmware_missing) {
cs35l56_hda_request_firmware_files(cs35l56, &wmfw_firmware, &wmfw_filename,
&coeff_firmware, &coeff_filename); &coeff_firmware, &coeff_filename);
}
/* /*
* If the BIOS didn't patch the firmware a bin file is mandatory to * If the BIOS didn't patch the firmware a bin file is mandatory to
...@@ -589,12 +594,12 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) ...@@ -589,12 +594,12 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
mutex_lock(&cs35l56->base.irq_lock); mutex_lock(&cs35l56->base.irq_lock);
/* /*
* When the device is running in secure mode the firmware files can * If the firmware hasn't been patched it must be shutdown before
* only contain insecure tunings and therefore we do not need to * doing a full patch and reset afterwards. If it is already
* shutdown the firmware to apply them and can use the lower cost * running a patched version the firmware files only contain
* reinit sequence instead. * tunings and we can use the lower cost reinit sequence instead.
*/ */
if (!cs35l56->base.secured && (wmfw_firmware || coeff_firmware)) { if (firmware_missing && (wmfw_firmware || coeff_firmware)) {
ret = cs35l56_firmware_shutdown(&cs35l56->base); ret = cs35l56_firmware_shutdown(&cs35l56->base);
if (ret) if (ret)
goto err; goto err;
...@@ -613,7 +618,7 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) ...@@ -613,7 +618,7 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
if (coeff_filename) if (coeff_filename)
dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename); dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename);
if (cs35l56->base.secured) { if (!firmware_missing) {
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
if (ret) if (ret)
goto err_powered_up; goto err_powered_up;
......
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