lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260205164838.1611295-2-rf@opensource.cirrus.com>
Date: Thu,  5 Feb 2026 16:48:36 +0000
From: Richard Fitzgerald <rf@...nsource.cirrus.com>
To: broonie@...nel.org
Cc: linux-sound@...r.kernel.org, linux-kernel@...r.kernel.org,
        patches@...nsource.cirrus.com
Subject: [PATCH 1/3] ASoC: cs35l56: Support for reading speaker ID from on-chip GPIOs

Add support for using the state of pins on the amplifier to indicate
the type of speaker fitted.

Previously, where there were alternate speaker vendors, this was
indicated using host CPU GPIOs.

Some new Dell models use spare pins on the CS35L63 as GPIOs for the
speaker ID detection.

Cirrus-specific SDCA Disco properties provide a list of the pins to be
used, and pull-up/down settings for the pads. This list is ordered,
MSbit to LSbit.

The code to set the firmware filename has been modified to check for
using chip pins for speaker ID. The entire block of code to set
firmware name has been moved out of cs35l56_component_probe() into
its own function to make it easier to KUnit test.

Signed-off-by: Richard Fitzgerald <rf@...nsource.cirrus.com>
---
 include/sound/cs35l56.h           |  37 +++++++
 sound/soc/codecs/cs35l56-shared.c | 167 ++++++++++++++++++++++++++++++
 sound/soc/codecs/cs35l56.c        | 141 ++++++++++++++++++++++++-
 sound/soc/codecs/cs35l56.h        |   2 +
 4 files changed, 343 insertions(+), 4 deletions(-)

diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h
index 5928af539c46..ae1e1489b671 100644
--- a/include/sound/cs35l56.h
+++ b/include/sound/cs35l56.h
@@ -9,6 +9,7 @@
 #ifndef __CS35L56_H
 #define __CS35L56_H
 
+#include <linux/bits.h>
 #include <linux/debugfs.h>
 #include <linux/firmware/cirrus/cs_dsp.h>
 #include <linux/regulator/consumer.h>
@@ -26,6 +27,9 @@ struct snd_ctl_elem_value;
 #define CS35L56_GLOBAL_ENABLES				0x0002014
 #define CS35L56_BLOCK_ENABLES				0x0002018
 #define CS35L56_BLOCK_ENABLES2				0x000201C
+#define CS35L56_SYNC_GPIO1_CFG				0x0002410
+#define CS35L56_ASP2_DIO_GPIO13_CFG			0x0002440
+#define CS35L56_UPDATE_REGS				0x0002A0C
 #define CS35L56_REFCLK_INPUT				0x0002C04
 #define CS35L56_GLOBAL_SAMPLE_RATE			0x0002C0C
 #define CS35L56_OTP_MEM_53				0x00300D4
@@ -65,6 +69,9 @@ struct snd_ctl_elem_value;
 #define CS35L56_IRQ1_MASK_8				0x000E0AC
 #define CS35L56_IRQ1_MASK_18				0x000E0D4
 #define CS35L56_IRQ1_MASK_20				0x000E0DC
+#define CS35L56_GPIO_STATUS1				0x000F000
+#define CS35L56_GPIO1_CTRL1				0x000F008
+#define CS35L56_GPIO13_CTRL1				0x000F038
 #define CS35L56_MIXER_NGATE_CH1_CFG			0x0010004
 #define CS35L56_MIXER_NGATE_CH2_CFG			0x0010008
 #define CS35L56_DSP_MBOX_1_RAW				0x0011000
@@ -130,6 +137,17 @@ struct snd_ctl_elem_value;
 #define CS35L56_MTLREVID_MASK				0x0000000F
 #define CS35L56_REVID_B0				0x000000B0
 
+/* PAD_INTF */
+#define CS35L56_PAD_GPIO_PULL_MASK			GENMASK(3, 2)
+#define CS35L56_PAD_GPIO_IE				BIT(0)
+
+#define CS35L56_PAD_PULL_NONE				0
+#define CS35L56_PAD_PULL_UP				1
+#define CS35L56_PAD_PULL_DOWN				2
+
+/* UPDATE_REGS */
+#define CS35L56_UPDT_GPIO_PRES				BIT(6)
+
 /* ASP_ENABLES1 */
 #define CS35L56_ASP_RX2_EN_SHIFT			17
 #define CS35L56_ASP_RX1_EN_SHIFT			16
@@ -185,6 +203,12 @@ struct snd_ctl_elem_value;
 /* MIXER_NGATE_CHn_CFG */
 #define CS35L56_AUX_NGATE_CHn_EN			0x00000001
 
+/* GPIOn_CTRL1 */
+#define CS35L56_GPIO_DIR_MASK				BIT(31)
+#define CS35L56_GPIO_FN_MASK				GENMASK(2, 0)
+
+#define CS35L56_GPIO_FN_GPIO				0x00000001
+
 /* Mixer input sources */
 #define CS35L56_INPUT_SRC_NONE				0x00
 #define CS35L56_INPUT_SRC_ASP1RX1			0x08
@@ -279,6 +303,7 @@ struct snd_ctl_elem_value;
 #define CS35L56_HALO_STATE_TIMEOUT_US			250000
 #define CS35L56_RESET_PULSE_MIN_US			1100
 #define CS35L56_WAKE_HOLD_TIME_US			1000
+#define CS35L56_PAD_PULL_SETTLE_US			10
 
 #define CS35L56_CALIBRATION_POLL_US			(100 * USEC_PER_MSEC)
 #define CS35L56_CALIBRATION_TIMEOUT_US			(5 * USEC_PER_SEC)
@@ -289,6 +314,9 @@ struct snd_ctl_elem_value;
 #define CS35L56_NUM_BULK_SUPPLIES			3
 #define CS35L56_NUM_DSP_REGIONS				5
 
+#define CS35L56_MAX_GPIO				13
+#define CS35L63_MAX_GPIO				9
+
 /* Additional margin for SYSTEM_RESET to control port ready on SPI */
 #define CS35L56_SPI_RESET_TO_PORT_READY_US (CS35L56_CONTROL_PORT_READY_US + 2500)
 
@@ -338,6 +366,10 @@ struct cs35l56_base {
 	const struct cirrus_amp_cal_controls *calibration_controls;
 	struct dentry *debugfs;
 	u64 silicon_uid;
+	u8 onchip_spkid_gpios[5];
+	u8 num_onchip_spkid_gpios;
+	u8 onchip_spkid_pulls[5];
+	u8 num_onchip_spkid_pulls;
 };
 
 static inline bool cs35l56_is_otp_register(unsigned int reg)
@@ -413,6 +445,11 @@ void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base);
 void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
 int cs35l56_hw_init(struct cs35l56_base *cs35l56_base);
 int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base);
+int cs35l56_check_and_save_onchip_spkid_gpios(struct cs35l56_base *cs35l56_base,
+					      const u32 *gpios, int num_gpios,
+					      const u32 *pulls, int num_pulls);
+int cs35l56_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base);
+int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base);
 int cs35l56_get_bclk_freq_id(unsigned int freq);
 void cs35l56_fill_supply_names(struct regulator_bulk_data *data);
 
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
index 60100c8f8c95..55c75b9e4172 100644
--- a/sound/soc/codecs/cs35l56-shared.c
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -6,12 +6,14 @@
 //                    Cirrus Logic International Semiconductor Ltd.
 
 #include <linux/array_size.h>
+#include <linux/bitfield.h>
 #include <linux/cleanup.h>
 #include <linux/debugfs.h>
 #include <linux/firmware/cirrus/wmfw.h>
 #include <linux/fs.h>
 #include <linux/gpio/consumer.h>
 #include <linux/kstrtox.h>
+#include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
@@ -182,6 +184,8 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
 	case CS35L56_OTP_MEM_53:
 	case CS35L56_OTP_MEM_54:
 	case CS35L56_OTP_MEM_55:
+	case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
+	case CS35L56_UPDATE_REGS:
 	case CS35L56_ASP1_ENABLES1:
 	case CS35L56_ASP1_CONTROL1:
 	case CS35L56_ASP1_CONTROL2:
@@ -213,6 +217,7 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
 	case CS35L56_IRQ1_MASK_8:
 	case CS35L56_IRQ1_MASK_18:
 	case CS35L56_IRQ1_MASK_20:
+	case CS35L56_GPIO_STATUS1 ... CS35L56_GPIO13_CTRL1:
 	case CS35L56_MIXER_NGATE_CH1_CFG:
 	case CS35L56_MIXER_NGATE_CH2_CFG:
 	case CS35L56_DSP_VIRTUAL1_MBOX_1:
@@ -262,6 +267,8 @@ static bool cs35l56_common_volatile_reg(unsigned int reg)
 	case CS35L56_GLOBAL_ENABLES:		   /* owned by firmware */
 	case CS35L56_BLOCK_ENABLES:		   /* owned by firmware */
 	case CS35L56_BLOCK_ENABLES2:		   /* owned by firmware */
+	case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
+	case CS35L56_UPDATE_REGS:
 	case CS35L56_REFCLK_INPUT:		   /* owned by firmware */
 	case CS35L56_GLOBAL_SAMPLE_RATE:	   /* owned by firmware */
 	case CS35L56_DACPCM1_INPUT:		   /* owned by firmware */
@@ -272,6 +279,7 @@ static bool cs35l56_common_volatile_reg(unsigned int reg)
 	case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8:
 	case CS35L56_IRQ1_EINT_18:
 	case CS35L56_IRQ1_EINT_20:
+	case CS35L56_GPIO_STATUS1 ... CS35L56_GPIO13_CTRL1:
 	case CS35L56_MIXER_NGATE_CH1_CFG:
 	case CS35L56_MIXER_NGATE_CH2_CFG:
 	case CS35L56_DSP_VIRTUAL1_MBOX_1:
@@ -1552,6 +1560,165 @@ int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base)
 }
 EXPORT_SYMBOL_NS_GPL(cs35l56_get_speaker_id, "SND_SOC_CS35L56_SHARED");
 
+int cs35l56_check_and_save_onchip_spkid_gpios(struct cs35l56_base *cs35l56_base,
+					      const u32 *gpios, int num_gpios,
+					      const u32 *pulls, int num_pulls)
+{
+	int max_gpio;
+	int ret = 0;
+	int i;
+
+	if ((num_gpios > ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios)) ||
+	    (num_pulls > ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls)))
+		return -EOVERFLOW;
+
+	switch (cs35l56_base->type) {
+	case 0x54:
+	case 0x56:
+	case 0x57:
+		max_gpio = CS35L56_MAX_GPIO;
+		break;
+	default:
+		max_gpio = CS35L63_MAX_GPIO;
+		break;
+	}
+
+	for (i = 0; i < num_gpios; i++) {
+		if (gpios[i] < 1 || gpios[i] > max_gpio) {
+			dev_err(cs35l56_base->dev, "Invalid spkid GPIO %d\n", gpios[i]);
+			/* Keep going so we log all bad values */
+			ret = -EINVAL;
+		}
+
+		/* Change to zero-based */
+		cs35l56_base->onchip_spkid_gpios[i] = gpios[i] - 1;
+	}
+
+	for (i = 0; i < num_pulls; i++) {
+		switch (pulls[i]) {
+		case 0:
+			cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_NONE;
+			break;
+		case 1:
+			cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_UP;
+			break;
+		case 2:
+			cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_DOWN;
+			break;
+		default:
+			dev_err(cs35l56_base->dev, "Invalid spkid pull %d\n", pulls[i]);
+			/* Keep going so we log all bad values */
+			ret = -EINVAL;
+			break;
+		}
+	}
+	if (ret)
+		return ret;
+
+	cs35l56_base->num_onchip_spkid_gpios = num_gpios;
+	cs35l56_base->num_onchip_spkid_pulls = num_pulls;
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_check_and_save_onchip_spkid_gpios, "SND_SOC_CS35L56_SHARED");
+
+/* Caller must pm_runtime resume before calling this function */
+int cs35l56_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base)
+{
+	struct regmap *regmap = cs35l56_base->regmap;
+	unsigned int addr_offset, val;
+	int num_gpios, num_pulls;
+	int i, ret;
+
+	if (cs35l56_base->num_onchip_spkid_gpios == 0)
+		return 0;
+
+	num_gpios = min(cs35l56_base->num_onchip_spkid_gpios,
+			ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
+	num_pulls = min(cs35l56_base->num_onchip_spkid_pulls,
+			ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls));
+
+	for (i = 0; i < num_gpios; i++) {
+		addr_offset = cs35l56_base->onchip_spkid_gpios[i] * sizeof(u32);
+
+		/* Set unspecified pulls to NONE */
+		if (i < num_pulls) {
+			val = FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK,
+					 cs35l56_base->onchip_spkid_pulls[i]);
+		} else {
+			val = FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK, CS35L56_PAD_PULL_NONE);
+		}
+
+		ret = regmap_update_bits(regmap, CS35L56_SYNC_GPIO1_CFG + addr_offset,
+					 CS35L56_PAD_GPIO_PULL_MASK | CS35L56_PAD_GPIO_IE,
+					 val | CS35L56_PAD_GPIO_IE);
+		if (ret) {
+			dev_err(cs35l56_base->dev, "GPIO%d set pad fail: %d\n",
+				cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
+			return ret;
+		}
+	}
+
+	ret = regmap_write(regmap, CS35L56_UPDATE_REGS, CS35L56_UPDT_GPIO_PRES);
+	if (ret) {
+		dev_err(cs35l56_base->dev, "UPDT_GPIO_PRES failed:%d\n", ret);
+		return ret;
+	}
+
+	usleep_range(CS35L56_PAD_PULL_SETTLE_US, CS35L56_PAD_PULL_SETTLE_US * 2);
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_configure_onchip_spkid_pads, "SND_SOC_CS35L56_SHARED");
+
+/* Caller must pm_runtime resume before calling this function */
+int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base)
+{
+	struct regmap *regmap = cs35l56_base->regmap;
+	unsigned int addr_offset, val;
+	int num_gpios;
+	int speaker_id = 0;
+	int i, ret;
+
+	if (cs35l56_base->num_onchip_spkid_gpios == 0)
+		return -ENOENT;
+
+	num_gpios = min(cs35l56_base->num_onchip_spkid_gpios,
+			ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
+
+	for (i = 0; i < num_gpios; i++) {
+		addr_offset = cs35l56_base->onchip_spkid_gpios[i] * sizeof(u32);
+
+		ret = regmap_update_bits(regmap, CS35L56_GPIO1_CTRL1 + addr_offset,
+					 CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_MASK,
+					 CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO);
+		if (ret) {
+			dev_err(cs35l56_base->dev, "GPIO%u set func fail: %d\n",
+				cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
+			return ret;
+		}
+	}
+
+	ret = regmap_read(regmap, CS35L56_GPIO_STATUS1, &val);
+	if (ret) {
+		dev_err(cs35l56_base->dev, "GPIO%d status read failed: %d\n",
+			cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
+		return ret;
+	}
+
+	for (i = 0; i < num_gpios; i++) {
+		speaker_id <<= 1;
+
+		if (val & BIT(cs35l56_base->onchip_spkid_gpios[i]))
+			speaker_id |= 1;
+	}
+
+	dev_dbg(cs35l56_base->dev, "Onchip GPIO Speaker ID = %d\n", speaker_id);
+
+	return speaker_id;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_read_onchip_spkid, "SND_SOC_CS35L56_SHARED");
+
 static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = {
 	[0x0C] = 128000,
 	[0x0F] = 256000,
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index 31dd2f7b2858..2ff8b172b76e 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -1179,15 +1179,28 @@ VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
 }
 EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_suffix);
 
-static int cs35l56_component_probe(struct snd_soc_component *component)
+VISIBLE_IF_KUNIT int cs35l56_set_fw_name(struct snd_soc_component *component)
 {
-	struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(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));
+	if ((cs35l56->speaker_id < 0) && cs35l56->base.num_onchip_spkid_gpios) {
+		PM_RUNTIME_ACQUIRE(cs35l56->base.dev, pm);
+		ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+		if (ret)
+			return ret;
+
+		ret = cs35l56_configure_onchip_spkid_pads(&cs35l56->base);
+		if (ret)
+			return ret;
+
+		ret = cs35l56_read_onchip_spkid(&cs35l56->base);
+		if (ret < 0)
+			return ret;
+
+		cs35l56->speaker_id = ret;
+	}
 
 	if (!cs35l56->dsp.system_name &&
 	    (snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0)) {
@@ -1208,6 +1221,19 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
 			return -ENOMEM;
 	}
 
+	return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_name);
+
+static int cs35l56_component_probe(struct snd_soc_component *component)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+	struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+	struct dentry *debugfs_root = component->debugfs_root;
+	int ret;
+
+	BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
+
 	if (!wait_for_completion_timeout(&cs35l56->init_completion,
 					 msecs_to_jiffies(5000))) {
 		dev_err(cs35l56->base.dev, "%s: init_completion timed out\n", __func__);
@@ -1219,6 +1245,10 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
 		return -ENOMEM;
 
 	cs35l56->component = component;
+	ret = cs35l56_set_fw_name(component);
+	if (ret)
+		return ret;
+
 	ret = cs35l56_set_fw_suffix(cs35l56);
 	if (ret)
 		return ret;
@@ -1532,6 +1562,105 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56)
 	return 0;
 }
 
+static int cs35l56_read_fwnode_u32_array(struct device *dev,
+					struct fwnode_handle *parent_node,
+					const char *prop_name,
+					int max_count,
+					u32 *dest)
+{
+	int count, ret;
+
+	count = fwnode_property_count_u32(parent_node, prop_name);
+	if ((count == 0) || (count == -EINVAL) || (count == -ENODATA)) {
+		dev_dbg(dev, "%s not found in %s\n", prop_name, fwnode_get_name(parent_node));
+		return 0;
+	}
+
+	if (count < 0) {
+		dev_err(dev, "Get %s error:%d\n", prop_name, count);
+		return count;
+	}
+
+	if (count > max_count) {
+		dev_err(dev, "%s too many entries (%d)\n", prop_name, count);
+		return -EOVERFLOW;
+	}
+
+	ret = fwnode_property_read_u32_array(parent_node, prop_name, dest, count);
+	if (ret) {
+		dev_err(dev, "Error reading %s: %d\n", prop_name, ret);
+		return ret;
+	}
+
+	return count;
+}
+
+static int cs35l56_process_xu_onchip_speaker_id(struct cs35l56_private *cs35l56,
+						struct fwnode_handle *ext_node)
+{
+	static const char * const gpio_name = "01fa-spk-id-gpios-onchip";
+	static const char * const pull_name = "01fa-spk-id-gpios-onchip-pull";
+	u32 gpios[5], pulls[5];
+	int num_gpios, num_pulls;
+	int ret;
+
+	static_assert(ARRAY_SIZE(gpios) == ARRAY_SIZE(cs35l56->base.onchip_spkid_gpios));
+	static_assert(ARRAY_SIZE(pulls) == ARRAY_SIZE(cs35l56->base.onchip_spkid_pulls));
+
+	num_gpios = cs35l56_read_fwnode_u32_array(cs35l56->base.dev, ext_node, gpio_name,
+						  ARRAY_SIZE(gpios), gpios);
+	if (num_gpios < 1)
+		return num_gpios;
+
+	num_pulls = cs35l56_read_fwnode_u32_array(cs35l56->base.dev, ext_node, pull_name,
+						  ARRAY_SIZE(pulls), pulls);
+	if (num_pulls < 0)
+		return num_pulls;
+
+	if (num_pulls != num_gpios) {
+		dev_warn(cs35l56->base.dev, "%s count(%d) != %s count(%d)\n",
+			pull_name, num_pulls, gpio_name, num_gpios);
+	}
+
+	ret = cs35l56_check_and_save_onchip_spkid_gpios(&cs35l56->base,
+							gpios, num_gpios,
+							pulls, num_pulls);
+	if (ret) {
+		return dev_err_probe(cs35l56->base.dev, ret, "Error in %s/%s\n",
+				     gpio_name, pull_name);
+	}
+
+	return 0;
+}
+
+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;
+		}
+	}
+
+	if (!ext_node)
+		return 0;
+
+	ret = cs35l56_process_xu_onchip_speaker_id(cs35l56, ext_node);
+	fwnode_handle_put(ext_node);
+
+	return ret;
+}
+EXPORT_SYMBOL_IF_KUNIT(cs35l56_process_xu_properties);
+
 static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56)
 {
 	struct device *dev = cs35l56->base.dev;
@@ -1712,6 +1841,10 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
 	if (ret != 0)
 		goto err;
 
+	ret = cs35l56_process_xu_properties(cs35l56);
+	if (ret)
+		goto err;
+
 	ret = cs35l56_dsp_init(cs35l56);
 	if (ret < 0) {
 		dev_err_probe(cs35l56->base.dev, ret, "DSP init failed\n");
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
index 7187885a13c1..691f857d0bd8 100644
--- a/sound/soc/codecs/cs35l56.h
+++ b/sound/soc/codecs/cs35l56.h
@@ -76,6 +76,8 @@ void cs35l56_remove(struct cs35l56_private *cs35l56);
 
 #if IS_ENABLED(CONFIG_KUNIT)
 int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56);
+int cs35l56_set_fw_name(struct snd_soc_component *component);
+int cs35l56_process_xu_properties(struct cs35l56_private *cs35l56);
 #endif
 
 #endif /* ifndef CS35L56_H */
-- 
2.47.3


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ