From: Richard Fitzgerald <rf@opensource.cirrus.com>
To: broonie@kernel.org
Cc: linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org,
patches@opensource.cirrus.com
Subject: [PATCH 1/2] ASoC: cs35l56: Use Disco FEID and FEV to qualify firmware filenames
Date: Tue, 10 Nov 2026 16:48:06 +0000 [thread overview]
Message-ID: <20261110164807.1671681-2-rf@opensource.cirrus.com> (raw)
In-Reply-To: <20261110164807.1671681-1-rf@opensource.cirrus.com>
Include the FunctionExtensionID (FEID) and FunctionExtensionVersion (FEV)
values from Disco as qualifiers on firmware names. This mechanism is
required to support new products that use CS35L56/57/62/63.
Some manufacturers are starting to re-use SSIDs, and the time between
re-use is decreasing. The SSID is therefore no longer providing the
unique hardware identifier intended by the PCI spec.
To ensure that the audio hardware can be uniquely identified, the FEID
and FEV will be included in the Disco for new products and must be
used to qualify the firmware filename (unless FEID==0). For products that
require an SSID match, the combination of SSID, FEID, FEV will be unique.
The implementation is backward-compatible. All existing SKUs either don't
have FEID in Disco, or it has a value of 0. For compatibility with Windows
behaviour the value of 0 means "ignore".
Behaviour is:
FEID missing: Legacy behaviour, SSID qualified firmware name.
Firmware system name only created if SSID available.
FEID == 0: Legacy behaviour, SSID qualified firmware name.
Firmware system name only created if SSID available.
FEID > 0 and < 0x8000: Firmware name qualified with SSID, FEID and FEV.
If SSID is missing the system_name is left NULL to
preserve the existing fallback behaviour.
FEID >= 0x8000: Firmware name qualified with FEID and FEV only,
SSID not used.
Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
---
sound/soc/codecs/cs35l56.c | 163 ++++++++++++++++++++++++++++++++-----
sound/soc/codecs/cs35l56.h | 3 +
2 files changed, 145 insertions(+), 21 deletions(-)
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index a4d25c287941..b44964a13111 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -9,6 +9,7 @@
#include <kunit/visibility.h>
#include <linux/acpi.h>
#include <linux/array_size.h>
+#include <linux/cleanup.h>
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
@@ -19,6 +20,7 @@
#include <linux/math.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/overflow.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
@@ -32,6 +34,7 @@
#include <sound/cs-amp-lib.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <sound/sdca_function.h>
#include <sound/sdw.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
@@ -1387,6 +1390,9 @@ VISIBLE_IF_KUNIT int cs35l56_set_fw_name(struct snd_soc_component *component)
{
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
unsigned short vendor, device;
+ char ssid[9] = "";
+ char fe[10] = "";
+ const char *separator = "";
int ret;
if ((cs35l56->speaker_id < 0) && cs35l56->base.num_onchip_spkid_gpios) {
@@ -1406,20 +1412,38 @@ VISIBLE_IF_KUNIT int cs35l56_set_fw_name(struct snd_soc_component *component)
cs35l56->speaker_id = ret;
}
- if (!cs35l56->dsp.system_name &&
- (snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0)) {
+ if (!cs35l56->dsp.system_name) {
+ if (cs35l56->feid < 0x8000) {
+ if (snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0) {
+ snprintf(ssid, sizeof(ssid), "%04x%04x", vendor, device);
+ separator = "-";
+ } else if (cs35l56->feid) {
+ dev_err(cs35l56->base.dev, "SSID required but not available\n");
+
+ /* Don't return error: allow falling back to default firmware */
+ return 0;
+ } else {
+ return 0;
+ }
+ }
+
+ if (cs35l56->feid) {
+ snprintf(fe, sizeof(fe), "%sf%xv%x", separator,
+ cs35l56->feid, cs35l56->fev);
+ }
+
/* Append a speaker qualifier if there is a speaker ID */
if (cs35l56->speaker_id >= 0) {
cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
GFP_KERNEL,
- "%04x%04x-spkid%d",
- vendor, device,
+ "%s%s-spkid%d",
+ ssid, fe,
cs35l56->speaker_id);
} else {
cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
GFP_KERNEL,
- "%04x%04x",
- vendor, device);
+ "%s%s",
+ ssid, fe);
}
if (!cs35l56->dsp.system_name)
return -ENOMEM;
@@ -1868,31 +1892,128 @@ static int cs35l56_process_xu_onchip_speaker_id(struct cs35l56_private *cs35l56,
return 0;
}
+static int cs35l56_read_subproperty_control_dc(struct cs35l56_private *cs35l56,
+ struct fwnode_handle *node,
+ const char *subname,
+ int width)
+{
+ static const char * const control_dc_value_prop_name = "mipi-sdca-control-dc-value";
+ u8 val8 = 0;
+ u16 val16 = 0;
+ u32 val32;
+ int ret;
+
+ struct fwnode_handle *subnode __free(fwnode_handle) =
+ fwnode_get_named_child_node(node, subname);
+ if (!subnode)
+ return -EINVAL;
+
+ switch (width) {
+ case 8:
+ ret = fwnode_property_read_u8(subnode, control_dc_value_prop_name, &val8);
+ val32 = val8;
+ break;
+ case 16:
+ ret = fwnode_property_read_u16(subnode, control_dc_value_prop_name, &val16);
+ val32 = val16;
+ break;
+ default:
+ ret = fwnode_property_read_u32(subnode, control_dc_value_prop_name, &val32);
+ break;
+ }
+
+ if (ret == -EINVAL)
+ return ret;
+
+ if (ret < 0) {
+ dev_warn(cs35l56->base.dev, "Failed to get dc-value for %s: %d\n",
+ subname, ret);
+ return ret;
+ }
+
+ /* None of the values we need are this big */
+ if (overflows_type(val32, int))
+ return -EINVAL;
+
+ return (int)val32;
+}
+
+static int cs35l56_process_feid_fev(struct cs35l56_private *cs35l56,
+ struct fwnode_handle *node)
+{
+ int ret;
+
+ /* Either property overrides default of 0. This matches Windows behavior */
+ ret = cs35l56_read_subproperty_control_dc(cs35l56, node,
+ "mipi-sdca-control-0x9-subproperties",
+ 16);
+ if (ret >= 0) {
+ cs35l56->feid = (u16)ret;
+ dev_dbg(cs35l56->base.dev, "FEID=%#x\n", cs35l56->feid);
+ } else if (ret != -EINVAL) {
+ return ret;
+ }
+
+ ret = cs35l56_read_subproperty_control_dc(cs35l56, node,
+ "mipi-sdca-control-0xA-subproperties",
+ 8);
+ if (ret >= 0) {
+ cs35l56->fev = (u8)ret;
+ dev_dbg(cs35l56->base.dev, "FEV=%#x\n", cs35l56->fev);
+ } else if (ret != -EINVAL) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_process_disco_control5(struct cs35l56_private *cs35l56,
+ struct fwnode_handle *node)
+{
+ int ret;
+
+ /*
+ * Don't fail probe because of a malformed property.
+ * It might be a property we are not interested in anyway.
+ */
+ ret = cs35l56_read_subproperty_control_dc(cs35l56, node,
+ "mipi-sdca-control-0x5-subproperties",
+ 32);
+ if (ret < 0)
+ return 0;
+
+ if (ret != SDCA_FUNCTION_TYPE_SMART_AMP)
+ return 0;
+
+ return cs35l56_process_feid_fev(cs35l56, node);
+}
+
VISIBLE_IF_KUNIT int cs35l56_process_xu_properties(struct cs35l56_private *cs35l56)
{
- struct fwnode_handle *ext_node = NULL;
- struct fwnode_handle *link;
int ret;
if (!cs35l56->sdw_peripheral)
return 0;
- fwnode_for_each_child_node(dev_fwnode(cs35l56->base.dev), link) {
- ext_node = fwnode_get_named_child_node(link,
- "mipi-sdca-function-expansion-subproperties");
- if (ext_node) {
- fwnode_handle_put(link);
- break;
+ fwnode_for_each_child_node_scoped(dev_fwnode(cs35l56->base.dev), link) {
+ ret = cs35l56_process_disco_control5(cs35l56, link);
+ if (ret)
+ return ret;
+
+ if (!cs35l56->base.num_onchip_spkid_gpios) {
+ struct fwnode_handle *ext_node __free(fwnode_handle) =
+ fwnode_get_named_child_node(link,
+ "mipi-sdca-function-expansion-subproperties");
+
+ if (ext_node) {
+ ret = cs35l56_process_xu_onchip_speaker_id(cs35l56, ext_node);
+ if (ret)
+ return ret;
+ }
}
}
- if (!ext_node)
- return 0;
-
- ret = cs35l56_process_xu_onchip_speaker_id(cs35l56, ext_node);
- fwnode_handle_put(ext_node);
-
- return ret;
+ return 0;
}
EXPORT_SYMBOL_IF_KUNIT(cs35l56_process_xu_properties);
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
index b46f410f0e09..f55dab899f9d 100644
--- a/sound/soc/codecs/cs35l56.h
+++ b/sound/soc/codecs/cs35l56.h
@@ -56,6 +56,9 @@ struct cs35l56_private {
u8 sdw_link_num;
u8 sdw_unique_id;
+ u16 feid;
+ u8 fev;
+
u8 ambient_ctl_value;
};
--
2.47.3
next prev parent reply other threads:[~2026-11-10 16:48 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 16:48 [PATCH 0/2] ASoC: cs35l56: Consider SDCA FunctionExtension ID/Version for firmware name Richard Fitzgerald
2026-11-10 16:48 ` Richard Fitzgerald [this message]
2026-11-10 16:48 ` [PATCH 2/2] ASoC: cs35l56: Add KUnit tests for FEID and FEV firmware names Richard Fitzgerald
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261110164807.1671681-2-rf@opensource.cirrus.com \
--to=rf@opensource.cirrus.com \
--cc=broonie@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=patches@opensource.cirrus.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®