summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tas2781.yaml31
-rw-r--r--drivers/firmware/cirrus/cs_dsp.c56
-rw-r--r--include/linux/firmware/cirrus/cs_dsp.h4
-rw-r--r--include/linux/soundwire/sdw_registers.h2
-rw-r--r--include/sound/sdca.h14
-rw-r--r--include/sound/sdca_fdl.h30
-rw-r--r--include/sound/sdca_regmap.h2
-rw-r--r--include/sound/tas2781.h5
-rw-r--r--sound/soc/codecs/arizona.c18
-rw-r--r--sound/soc/codecs/tas2781-i2c.c29
-rw-r--r--sound/soc/intel/avs/debugfs.c9
-rw-r--r--sound/soc/sdca/Kconfig18
-rw-r--r--sound/soc/sdca/Makefile10
-rw-r--r--sound/soc/sdca/sdca_class.c304
-rw-r--r--sound/soc/sdca/sdca_class.h37
-rw-r--r--sound/soc/sdca/sdca_class_function.c460
-rw-r--r--sound/soc/sdca/sdca_fdl.c93
-rw-r--r--sound/soc/sdca/sdca_function_device.c117
-rw-r--r--sound/soc/sdca/sdca_function_device.h15
-rw-r--r--sound/soc/sdca/sdca_functions.c52
-rw-r--r--sound/soc/sdca/sdca_hid.c3
-rw-r--r--sound/soc/sdca/sdca_interrupts.c39
-rw-r--r--sound/soc/sdca/sdca_regmap.c89
23 files changed, 1263 insertions, 174 deletions
diff --git a/Documentation/devicetree/bindings/sound/ti,tas2781.yaml b/Documentation/devicetree/bindings/sound/ti,tas2781.yaml
index f0bb5faf55c8..f3a5638f4239 100644
--- a/Documentation/devicetree/bindings/sound/ti,tas2781.yaml
+++ b/Documentation/devicetree/bindings/sound/ti,tas2781.yaml
@@ -34,12 +34,16 @@ description: |
https://www.ti.com/lit/gpn/tas2320
https://www.ti.com/lit/gpn/tas2563
https://www.ti.com/lit/gpn/tas2572
+ https://www.ti.com/lit/gpn/tas2574
https://www.ti.com/lit/gpn/tas2781
+ https://www.ti.com/lit/gpn/tas5806m
+ https://www.ti.com/lit/gpn/tas5806md
https://www.ti.com/lit/gpn/tas5815
https://www.ti.com/lit/gpn/tas5822m
https://www.ti.com/lit/gpn/tas5825m
https://www.ti.com/lit/gpn/tas5827
https://www.ti.com/lit/gpn/tas5828m
+ https://www.ti.com/lit/gpn/tas5830
properties:
compatible:
@@ -58,12 +62,18 @@ properties:
ti,tas2563: 6.1-W Boosted Class-D Audio Amplifier With Integrated
DSP and IV Sense, 16/20/24/32bit stereo I2S or multichannel TDM.
+ ti,tas2568: 5.3-W Digital Input Smart Amp with I/V Sense and Integrated
+ 10.75-V Class-H Boost
+
ti,tas2570: 5.8-W Digital Input smart amp with I/V sense and integrated
11-V Class-H Boost
ti,tas2572: 6.6-W Digital Input smart amp with I/V sense and integrated
13-V Class-H Boost
+ ti,tas2574: 8.5-W Digital Input smart amp with I/V sense and integrated
+ 15-V Class-H Boost
+
ti,tas2781: 24-V Class-D Amplifier with Real Time Integrated Speaker
Protection and Audio Processing, 16/20/24/32bit stereo I2S or
multichannel TDM.
@@ -72,6 +82,14 @@ properties:
Audio Amplifier with 96-Khz Extended Processing and Low Idle Power
Dissipation.
+ ti,tas5806m: 23-W, Inductor-Less, Digital Input, Stereo, Closed-Loop
+ Class-D Audio Amplifier with Enhanced Processing and Low Power
+ Dissipation.
+
+ ti,tas5806md: 23-W, Inductor-Less, Digital Input, Stereo, Closed-Loop
+ Class-D Audio Amplifier with Enhanced Processing and DirectPath(TM)
+ HP Driver
+
ti,tas5815: 30-W, Digital Input, Stereo, Closed-loop Class-D Audio
Amplifier with 96 kHz Enhanced Processing
@@ -86,6 +104,9 @@ properties:
ti,tas5828: 50-W Stereo, Digital Input, High Efficiency Closed-Loop
Class-D Amplifier with Hybrid-Pro Algorithm
+
+ ti,tas5830: 65-W Stereo, Digital Input, High Efficiency Closed-Loop
+ Class-D Amplifier with Class-H Algorithm
oneOf:
- items:
- enum:
@@ -94,14 +115,19 @@ properties:
- ti,tas2120
- ti,tas2320
- ti,tas2563
+ - ti,tas2568
- ti,tas2570
- ti,tas2572
+ - ti,tas2574
- ti,tas5802
+ - ti,tas5806m
+ - ti,tas5806md
- ti,tas5815
- ti,tas5822
- ti,tas5825
- ti,tas5827
- ti,tas5828
+ - ti,tas5830
- const: ti,tas2781
- enum:
- ti,tas2781
@@ -137,6 +163,8 @@ allOf:
- ti,tas2118
- ti,tas2120
- ti,tas2320
+ - ti,tas2568
+ - ti,tas2574
then:
properties:
reg:
@@ -217,6 +245,8 @@ allOf:
compatible:
contains:
enum:
+ - ti,tas5806m
+ - ti,tas5806md
- ti,tas5822
then:
properties:
@@ -233,6 +263,7 @@ allOf:
enum:
- ti,tas5827
- ti,tas5828
+ - ti,tas5830
then:
properties:
reg:
diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c
index f51047d8ea64..9acdcd75928a 100644
--- a/drivers/firmware/cirrus/cs_dsp.c
+++ b/drivers/firmware/cirrus/cs_dsp.c
@@ -9,6 +9,7 @@
* Cirrus Logic International Semiconductor Ltd.
*/
+#include <linux/cleanup.h>
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
@@ -388,18 +389,14 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_mem_region_name, "FW_CS_DSP");
#ifdef CONFIG_DEBUG_FS
static void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp, const char *s)
{
- char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
-
kfree(dsp->wmfw_file_name);
- dsp->wmfw_file_name = tmp;
+ dsp->wmfw_file_name = kstrdup(s, GFP_KERNEL);
}
static void cs_dsp_debugfs_save_binname(struct cs_dsp *dsp, const char *s)
{
- char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
-
kfree(dsp->bin_file_name);
- dsp->bin_file_name = tmp;
+ dsp->bin_file_name = kstrdup(s, GFP_KERNEL);
}
static void cs_dsp_debugfs_clear(struct cs_dsp *dsp)
@@ -410,24 +407,33 @@ static void cs_dsp_debugfs_clear(struct cs_dsp *dsp)
dsp->bin_file_name = NULL;
}
+static ssize_t cs_dsp_debugfs_string_read(struct cs_dsp *dsp,
+ char __user *user_buf,
+ size_t count, loff_t *ppos,
+ const char **pstr)
+{
+ const char *str __free(kfree) = NULL;
+
+ scoped_guard(mutex, &dsp->pwr_lock) {
+ if (!*pstr)
+ return 0;
+
+ str = kasprintf(GFP_KERNEL, "%s\n", *pstr);
+ if (!str)
+ return -ENOMEM;
+
+ return simple_read_from_buffer(user_buf, count, ppos, str, strlen(str));
+ }
+}
+
static ssize_t cs_dsp_debugfs_wmfw_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct cs_dsp *dsp = file->private_data;
- ssize_t ret;
- mutex_lock(&dsp->pwr_lock);
-
- if (!dsp->wmfw_file_name || !dsp->booted)
- ret = 0;
- else
- ret = simple_read_from_buffer(user_buf, count, ppos,
- dsp->wmfw_file_name,
- strlen(dsp->wmfw_file_name));
-
- mutex_unlock(&dsp->pwr_lock);
- return ret;
+ return cs_dsp_debugfs_string_read(dsp, user_buf, count, ppos,
+ &dsp->wmfw_file_name);
}
static ssize_t cs_dsp_debugfs_bin_read(struct file *file,
@@ -435,19 +441,9 @@ static ssize_t cs_dsp_debugfs_bin_read(struct file *file,
size_t count, loff_t *ppos)
{
struct cs_dsp *dsp = file->private_data;
- ssize_t ret;
- mutex_lock(&dsp->pwr_lock);
-
- if (!dsp->bin_file_name || !dsp->booted)
- ret = 0;
- else
- ret = simple_read_from_buffer(user_buf, count, ppos,
- dsp->bin_file_name,
- strlen(dsp->bin_file_name));
-
- mutex_unlock(&dsp->pwr_lock);
- return ret;
+ return cs_dsp_debugfs_string_read(dsp, user_buf, count, ppos,
+ &dsp->bin_file_name);
}
static const struct {
diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h
index a66eb7624730..69959032f8f5 100644
--- a/include/linux/firmware/cirrus/cs_dsp.h
+++ b/include/linux/firmware/cirrus/cs_dsp.h
@@ -188,8 +188,8 @@ struct cs_dsp {
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_root;
- char *wmfw_file_name;
- char *bin_file_name;
+ const char *wmfw_file_name;
+ const char *bin_file_name;
#endif
};
diff --git a/include/linux/soundwire/sdw_registers.h b/include/linux/soundwire/sdw_registers.h
index 0a5939285583..cae8a0a5a9b0 100644
--- a/include/linux/soundwire/sdw_registers.h
+++ b/include/linux/soundwire/sdw_registers.h
@@ -355,4 +355,6 @@
/* Check the reserved and fixed bits in address */
#define SDW_SDCA_VALID_CTL(reg) (((reg) & (GENMASK(31, 25) | BIT(18) | BIT(13))) == BIT(30))
+#define SDW_SDCA_MAX_REGISTER 0x47FFFFFF
+
#endif /* __SDW_REGISTERS_H */
diff --git a/include/sound/sdca.h b/include/sound/sdca.h
index d38cdbfeb35f..67ff3c88705d 100644
--- a/include/sound/sdca.h
+++ b/include/sound/sdca.h
@@ -13,19 +13,23 @@
#include <linux/kconfig.h>
struct acpi_table_swft;
+struct fwnode_handle;
struct sdw_slave;
+struct sdca_dev;
#define SDCA_MAX_FUNCTION_COUNT 8
/**
* struct sdca_function_desc - short descriptor for an SDCA Function
* @node: firmware node for the Function.
+ * @func_dev: pointer to SDCA function device.
* @name: Human-readable string.
* @type: Function topology type.
* @adr: ACPI address (used for SDCA register access).
*/
struct sdca_function_desc {
struct fwnode_handle *node;
+ struct sdca_dev *func_dev;
const char *name;
u32 type;
u8 adr;
@@ -58,6 +62,8 @@ void sdca_lookup_functions(struct sdw_slave *slave);
void sdca_lookup_swft(struct sdw_slave *slave);
void sdca_lookup_interface_revision(struct sdw_slave *slave);
bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk);
+int sdca_dev_register_functions(struct sdw_slave *slave);
+void sdca_dev_unregister_functions(struct sdw_slave *slave);
#else
@@ -68,6 +74,14 @@ static inline bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_qu
{
return false;
}
+
+static inline int sdca_dev_register_functions(struct sdw_slave *slave)
+{
+ return 0;
+}
+
+static inline void sdca_dev_unregister_functions(struct sdw_slave *slave) {}
+
#endif
#endif
diff --git a/include/sound/sdca_fdl.h b/include/sound/sdca_fdl.h
index f4ba809cb203..fbaf4b384c8a 100644
--- a/include/sound/sdca_fdl.h
+++ b/include/sound/sdca_fdl.h
@@ -64,6 +64,8 @@ struct fdl_state {
SDCA_CTL_XU_FDLD_ACK_TRANSFER | \
SDCA_CTL_XU_FDLD_NEEDS_SET)
+#if IS_ENABLED(CONFIG_SND_SOC_SDCA_FDL)
+
int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt);
int sdca_fdl_process(struct sdca_interrupt *interrupt);
int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function,
@@ -72,4 +74,32 @@ int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function,
int sdca_reset_function(struct device *dev, struct sdca_function_data *function,
struct regmap *regmap);
+#else
+
+static inline int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt)
+{
+ return 0;
+}
+
+static inline int sdca_fdl_process(struct sdca_interrupt *interrupt)
+{
+ return 0;
+}
+
+static inline int sdca_fdl_sync(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ return 0;
+}
+
+static inline int sdca_reset_function(struct device *dev,
+ struct sdca_function_data *function,
+ struct regmap *regmap)
+{
+ return 0;
+}
+
+#endif // CONFIG_SND_SOC_SDCA_FDL
+
#endif // __SDCA_FDL_H__
diff --git a/include/sound/sdca_regmap.h b/include/sound/sdca_regmap.h
index b2e3c2ad2bb8..792540a530fc 100644
--- a/include/sound/sdca_regmap.h
+++ b/include/sound/sdca_regmap.h
@@ -27,5 +27,7 @@ int sdca_regmap_populate_constants(struct device *dev, struct sdca_function_data
int sdca_regmap_write_defaults(struct device *dev, struct regmap *regmap,
struct sdca_function_data *function);
+int sdca_regmap_write_init(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function);
#endif // __SDCA_REGMAP_H__
diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h
index c3b4c43dd2bf..711142cb9918 100644
--- a/include/sound/tas2781.h
+++ b/include/sound/tas2781.h
@@ -117,15 +117,20 @@ enum audio_device {
TAS2120,
TAS2320,
TAS2563,
+ TAS2568,
TAS2570,
TAS2572,
+ TAS2574,
TAS2781,
TAS5802,
+ TAS5806M,
+ TAS5806MD,
TAS5815,
TAS5822,
TAS5825,
TAS5827,
TAS5828,
+ TAS5830,
TAS_OTHERS,
};
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 99884dc6ff86..8c683b0bb74c 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -169,7 +169,7 @@ static const struct snd_soc_dapm_widget arizona_spkr =
int arizona_init_spk(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int ret;
@@ -238,7 +238,7 @@ static const struct snd_soc_dapm_route arizona_mono_routes[] = {
int arizona_init_mono(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int i;
@@ -255,6 +255,7 @@ EXPORT_SYMBOL_GPL(arizona_init_mono);
int arizona_init_gpio(struct snd_soc_component *component)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int i;
@@ -262,24 +263,21 @@ int arizona_init_gpio(struct snd_soc_component *component)
switch (arizona->type) {
case WM5110:
case WM8280:
- snd_soc_component_disable_pin(component,
- "DRC2 Signal Activity");
+ snd_soc_dapm_disable_pin(dapm, "DRC2 Signal Activity");
break;
default:
break;
}
- snd_soc_component_disable_pin(component, "DRC1 Signal Activity");
+ snd_soc_dapm_disable_pin(dapm, "DRC1 Signal Activity");
for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) {
case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
- snd_soc_component_enable_pin(component,
- "DRC1 Signal Activity");
+ snd_soc_dapm_enable_pin(dapm, "DRC1 Signal Activity");
break;
case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
- snd_soc_component_enable_pin(component,
- "DRC2 Signal Activity");
+ snd_soc_dapm_enable_pin(dapm, "DRC2 Signal Activity");
break;
default:
break;
@@ -1911,7 +1909,7 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_component *component = dai->component;
- struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
struct snd_soc_dapm_route routes[2];
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index 7bd98ff5a250..0a86964dfcfd 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -105,15 +105,20 @@ static const struct i2c_device_id tasdevice_id[] = {
{ "tas2120", TAS2120 },
{ "tas2320", TAS2320 },
{ "tas2563", TAS2563 },
+ { "tas2568", TAS2568 },
{ "tas2570", TAS2570 },
{ "tas2572", TAS2572 },
+ { "tas2574", TAS2574 },
{ "tas2781", TAS2781 },
{ "tas5802", TAS5802 },
+ { "tas5806m", TAS5806M },
+ { "tas5806md", TAS5806MD },
{ "tas5815", TAS5815 },
{ "tas5822", TAS5822 },
{ "tas5825", TAS5825 },
{ "tas5827", TAS5827 },
{ "tas5828", TAS5828 },
+ { "tas5830", TAS5830 },
{}
};
MODULE_DEVICE_TABLE(i2c, tasdevice_id);
@@ -125,15 +130,20 @@ static const struct of_device_id tasdevice_of_match[] = {
{ .compatible = "ti,tas2120" },
{ .compatible = "ti,tas2320" },
{ .compatible = "ti,tas2563" },
+ { .compatible = "ti,tas2568" },
{ .compatible = "ti,tas2570" },
{ .compatible = "ti,tas2572" },
+ { .compatible = "ti,tas2574" },
{ .compatible = "ti,tas2781" },
{ .compatible = "ti,tas5802" },
+ { .compatible = "ti,tas5806m" },
+ { .compatible = "ti,tas5806md" },
{ .compatible = "ti,tas5815" },
{ .compatible = "ti,tas5822" },
{ .compatible = "ti,tas5825" },
{ .compatible = "ti,tas5827" },
{ .compatible = "ti,tas5828" },
+ { .compatible = "ti,tas5830" },
{},
};
MODULE_DEVICE_TABLE(of, tasdevice_of_match);
@@ -1643,8 +1653,10 @@ static void tasdevice_fw_ready(const struct firmware *fmw,
case TAS2118:
case TAS2120:
case TAS2320:
+ case TAS2568:
case TAS2570:
case TAS2572:
+ case TAS2574:
goto out;
}
if (tas_priv->name_prefix)
@@ -1670,9 +1682,7 @@ static void tasdevice_fw_ready(const struct firmware *fmw,
}
tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
- /* There is no calibration required for
- * TAS5802/TAS5815/TAS5822/TAS5825/TAS5827/TAS5828.
- */
+ /* There is no calibration required for TAS58XX. */
if (tas_priv->chip_id < TAS5802) {
ret = tasdevice_create_cali_ctrls(tas_priv);
if (ret) {
@@ -1728,11 +1738,14 @@ out:
case TAS2563:
case TAS2781:
case TAS5802:
+ case TAS5806M:
+ case TAS5806MD:
case TAS5815:
case TAS5822:
case TAS5825:
case TAS5827:
case TAS5828:
+ case TAS5830:
/* If DSP FW fail, DSP kcontrol won't be created. */
tasdevice_dsp_remove(tas_priv);
}
@@ -1883,8 +1896,10 @@ static int tasdevice_codec_probe(struct snd_soc_component *codec)
case TAS2118:
case TAS2120:
case TAS2320:
+ case TAS2568:
case TAS2570:
case TAS2572:
+ case TAS2574:
p = (struct snd_kcontrol_new *)tas2x20_snd_controls;
size = ARRAY_SIZE(tas2x20_snd_controls);
tas_priv->dvc_tlv_table = tas2x20_dvc_table;
@@ -1894,11 +1909,14 @@ static int tasdevice_codec_probe(struct snd_soc_component *codec)
size = ARRAY_SIZE(tas2781_snd_controls);
break;
case TAS5802:
+ case TAS5806M:
+ case TAS5806MD:
case TAS5815:
case TAS5822:
case TAS5825:
case TAS5827:
case TAS5828:
+ case TAS5830:
p = (struct snd_kcontrol_new *)tas5825_snd_controls;
size = ARRAY_SIZE(tas5825_snd_controls);
break;
@@ -2072,15 +2090,20 @@ static const struct acpi_device_id tasdevice_acpi_match[] = {
{ "TXNW2120", TAS2120 },
{ "TXNW2320", TAS2320 },
{ "TXNW2563", TAS2563 },
+ { "TXNW2568", TAS2568 },
{ "TXNW2570", TAS2570 },
{ "TXNW2572", TAS2572 },
+ { "TXNW2574", TAS2574 },
{ "TXNW2781", TAS2781 },
{ "TXNW5802", TAS5802 },
+ { "TXNW806M", TAS5806M },
+ { "TXNW806D", TAS5806MD },
{ "TXNW5815", TAS5815 },
{ "TXNW5822", TAS5822 },
{ "TXNW5825", TAS5825 },
{ "TXNW5827", TAS5827 },
{ "TXNW5828", TAS5828 },
+ { "TXNW5830", TAS5830 },
{},
};
diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c
index 3534de46f9e4..701c247227bf 100644
--- a/sound/soc/intel/avs/debugfs.c
+++ b/sound/soc/intel/avs/debugfs.c
@@ -119,16 +119,13 @@ static ssize_t probe_points_read(struct file *file, char __user *to, size_t coun
}
for (i = 0; i < num_desc; i++) {
- ret = snprintf(buf + len, PAGE_SIZE - len,
- "Id: %#010x Purpose: %d Node id: %#x\n",
- desc[i].id.value, desc[i].purpose, desc[i].node_id.val);
- if (ret < 0)
- goto free_desc;
+ ret = scnprintf(buf + len, PAGE_SIZE - len,
+ "Id: %#010x Purpose: %d Node id: %#x\n",
+ desc[i].id.value, desc[i].purpose, desc[i].node_id.val);
len += ret;
}
ret = simple_read_from_buffer(to, count, ppos, buf, len);
-free_desc:
kfree(desc);
exit:
kfree(buf);
diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig
index a73920d07073..3c6bf0e2c2f0 100644
--- a/sound/soc/sdca/Kconfig
+++ b/sound/soc/sdca/Kconfig
@@ -4,6 +4,7 @@ menu "SoundWire (SDCA)"
config SND_SOC_SDCA
tristate
depends on ACPI
+ select AUXILIARY_BUS
help
This option enables support for the MIPI SoundWire Device
Class for Audio (SDCA).
@@ -36,4 +37,21 @@ config SND_SOC_SDCA_FDL
config SND_SOC_SDCA_OPTIONAL
def_tristate SND_SOC_SDCA || !SND_SOC_SDCA
+config SND_SOC_SDCA_CLASS
+ tristate "SDCA Class Driver"
+ depends on SND_SOC_SDCA
+ select SND_SOC_SDCA_CLASS_FUNCTION
+ select SND_SOC_SDCA_FDL
+ select SND_SOC_SDCA_HID
+ select SND_SOC_SDCA_IRQ
+ help
+ This option enables support for the SDCA Class driver which should
+ support any class compliant SDCA part.
+
+config SND_SOC_SDCA_CLASS_FUNCTION
+ tristate
+ help
+ This option enables support for the SDCA Class Function drivers,
+ these implement the individual functions of the SDCA Class driver.
+
endmenu
diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile
index be911c399bbd..f6b73275d964 100644
--- a/sound/soc/sdca/Makefile
+++ b/sound/soc/sdca/Makefile
@@ -1,9 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o sdca_asoc.o \
- sdca_ump.o
+snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_function_device.o \
+ sdca_regmap.o sdca_asoc.o sdca_ump.o
snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_HID) += sdca_hid.o
snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_IRQ) += sdca_interrupts.o
snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_FDL) += sdca_fdl.o
+snd-soc-sdca-class-y := sdca_class.o
+snd-soc-sdca-class-function-y := sdca_class_function.o
+
obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o
+
+obj-$(CONFIG_SND_SOC_SDCA_CLASS) += snd-soc-sdca-class.o
+obj-$(CONFIG_SND_SOC_SDCA_CLASS_FUNCTION) += snd-soc-sdca-class-function.o
diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c
new file mode 100644
index 000000000000..349d32933ba8
--- /dev/null
+++ b/sound/soc/sdca/sdca_class.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_interrupts.h>
+#include <sound/sdca_regmap.h>
+#include "sdca_class.h"
+
+#define CLASS_SDW_ATTACH_TIMEOUT_MS 5000
+
+static int class_read_prop(struct sdw_slave *sdw)
+{
+ struct sdw_slave_prop *prop = &sdw->prop;
+
+ sdw_slave_read_prop(sdw);
+
+ prop->use_domain_irq = true;
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY |
+ SDW_SCP_INT1_IMPL_DEF;
+
+ return 0;
+}
+
+static int class_sdw_update_status(struct sdw_slave *sdw, enum sdw_slave_status status)
+{
+ struct sdca_class_drv *drv = dev_get_drvdata(&sdw->dev);
+
+ switch (status) {
+ case SDW_SLAVE_ATTACHED:
+ dev_dbg(drv->dev, "device attach\n");
+
+ drv->attached = true;
+
+ complete(&drv->device_attach);
+ break;
+ case SDW_SLAVE_UNATTACHED:
+ dev_dbg(drv->dev, "device detach\n");
+
+ drv->attached = false;
+
+ reinit_completion(&drv->device_attach);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct sdw_slave_ops class_sdw_ops = {
+ .read_prop = class_read_prop,
+ .update_status = class_sdw_update_status,
+};
+
+static void class_regmap_lock(void *data)
+{
+ struct mutex *lock = data;
+
+ mutex_lock(lock);
+}
+
+static void class_regmap_unlock(void *data)
+{
+ struct mutex *lock = data;
+
+ mutex_unlock(lock);
+}
+
+static int class_wait_for_attach(struct sdca_class_drv *drv)
+{
+ if (!drv->attached) {
+ unsigned long timeout = msecs_to_jiffies(CLASS_SDW_ATTACH_TIMEOUT_MS);
+ unsigned long time;
+
+ time = wait_for_completion_timeout(&drv->device_attach, timeout);
+ if (!time) {
+ dev_err(drv->dev, "timed out waiting for device re-attach\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ regcache_cache_only(drv->dev_regmap, false);
+
+ return 0;
+}
+
+static bool class_dev_regmap_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SDW_SCP_SDCA_INTMASK1 ... SDW_SCP_SDCA_INTMASK4:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool class_dev_regmap_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INT4:
+ case SDW_SCP_SDCA_INTMASK1 ... SDW_SCP_SDCA_INTMASK4:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config class_dev_regmap_config = {
+ .name = "sdca-device",
+ .reg_bits = 32,
+ .val_bits = 8,
+
+ .max_register = SDW_SDCA_MAX_REGISTER,
+ .volatile_reg = class_dev_regmap_volatile,
+ .precious_reg = class_dev_regmap_precious,
+
+ .cache_type = REGCACHE_MAPLE,
+
+ .lock = class_regmap_lock,
+ .unlock = class_regmap_unlock,
+};
+
+static void class_boot_work(struct work_struct *work)
+{
+ struct sdca_class_drv *drv = container_of(work,
+ struct sdca_class_drv,
+ boot_work);
+ int ret;
+
+ ret = class_wait_for_attach(drv);
+ if (ret)
+ goto err;
+
+ drv->irq_info = sdca_irq_allocate(drv->dev, drv->dev_regmap,
+ drv->sdw->irq);
+ if (IS_ERR(drv->irq_info))
+ goto err;
+
+ ret = sdca_dev_register_functions(drv->sdw);
+ if (ret)
+ goto err;
+
+ dev_dbg(drv->dev, "boot work complete\n");
+
+ pm_runtime_mark_last_busy(drv->dev);
+ pm_runtime_put_autosuspend(drv->dev);
+
+ return;
+
+err:
+ pm_runtime_put_sync(drv->dev);
+}
+
+static void class_dev_remove(void *data)
+{
+ struct sdca_class_drv *drv = data;
+
+ cancel_work_sync(&drv->boot_work);
+
+ sdca_dev_unregister_functions(drv->sdw);
+}
+
+static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id)
+{
+ struct device *dev = &sdw->dev;
+ struct sdca_device_data *data = &sdw->sdca_data;
+ struct regmap_config *dev_config;
+ struct sdca_class_drv *drv;
+ int ret;
+
+ sdca_lookup_swft(sdw);
+
+ drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ dev_config = devm_kmemdup(dev, &class_dev_regmap_config,
+ sizeof(*dev_config), GFP_KERNEL);
+ if (!dev_config)
+ return -ENOMEM;
+
+ drv->functions = devm_kcalloc(dev, data->num_functions,
+ sizeof(*drv->functions),
+ GFP_KERNEL);
+ if (!drv->functions)
+ return -ENOMEM;
+
+ drv->dev = dev;
+ drv->sdw = sdw;
+ mutex_init(&drv->regmap_lock);
+
+ dev_set_drvdata(drv->dev, drv);
+
+ INIT_WORK(&drv->boot_work, class_boot_work);
+ init_completion(&drv->device_attach);
+
+ dev_config->lock_arg = &drv->regmap_lock;
+
+ drv->dev_regmap = devm_regmap_init_sdw(sdw, dev_config);
+ if (IS_ERR(drv->dev_regmap))
+ return dev_err_probe(drv->dev, PTR_ERR(drv->dev_regmap),
+ "failed to create device regmap\n");
+
+ regcache_cache_only(drv->dev_regmap, true);
+
+ pm_runtime_set_autosuspend_delay(dev, 250);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_get_noresume(dev);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, class_dev_remove, drv);
+ if (ret)
+ return ret;
+
+ queue_work(system_long_wq, &drv->boot_work);
+
+ return 0;
+}
+
+static int class_runtime_suspend(struct device *dev)
+{
+ struct sdca_class_drv *drv = dev_get_drvdata(dev);
+
+ /*
+ * Whilst the driver doesn't power the chip down here, going into runtime
+ * suspend lets the SoundWire bus power down, which means the driver
+ * can't communicate with the device any more.
+ */
+ regcache_cache_only(drv->dev_regmap, true);
+
+ return 0;
+}
+
+static int class_runtime_resume(struct device *dev)
+{
+ struct sdca_class_drv *drv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = class_wait_for_attach(drv);
+ if (ret)
+ goto err;
+
+ regcache_mark_dirty(drv->dev_regmap);
+
+ ret = regcache_sync(drv->dev_regmap);
+ if (ret) {
+ dev_err(drv->dev, "failed to restore cache: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(drv->dev_regmap, true);
+
+ return ret;
+}
+
+static const struct dev_pm_ops class_pm_ops = {
+ RUNTIME_PM_OPS(class_runtime_suspend, class_runtime_resume, NULL)
+};
+
+static const struct sdw_device_id class_sdw_id[] = {
+ SDW_SLAVE_ENTRY(0x01FA, 0x4245, 0),
+ {}
+};
+MODULE_DEVICE_TABLE(sdw, class_sdw_id);
+
+static struct sdw_driver class_sdw_driver = {
+ .driver = {
+ .name = "sdca_class",
+ .pm = pm_ptr(&class_pm_ops),
+ },
+
+ .probe = class_sdw_probe,
+ .id_table = class_sdw_id,
+ .ops = &class_sdw_ops,
+};
+module_sdw_driver(class_sdw_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SDCA Class Driver");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_class.h b/sound/soc/sdca/sdca_class.h
new file mode 100644
index 000000000000..bb4c9dd12429
--- /dev/null
+++ b/sound/soc/sdca/sdca_class.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ *
+ * Copyright (C) 2025 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __SDCA_CLASS_H__
+#define __SDCA_CLASS_H__
+
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+struct device;
+struct regmap;
+struct sdw_slave;
+struct sdca_function_data;
+
+struct sdca_class_drv {
+ struct device *dev;
+ struct regmap *dev_regmap;
+ struct sdw_slave *sdw;
+
+ struct sdca_function_data *functions;
+ struct sdca_interrupt_info *irq_info;
+
+ struct mutex regmap_lock;
+ struct work_struct boot_work;
+ struct completion device_attach;
+
+ bool attached;
+};
+
+#endif /* __SDCA_CLASS_H__ */
diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c
new file mode 100644
index 000000000000..0028482a1e75
--- /dev/null
+++ b/sound/soc/sdca/sdca_class_function.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/pcm.h>
+#include <sound/sdca_asoc.h>
+#include <sound/sdca_fdl.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_interrupts.h>
+#include <sound/sdca_regmap.h>
+#include <sound/sdw.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc.h>
+#include "sdca_class.h"
+
+struct class_function_drv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct sdca_class_drv *core;
+
+ struct sdca_function_data *function;
+};
+
+static void class_function_regmap_lock(void *data)
+{
+ struct mutex *lock = data;
+
+ mutex_lock(lock);
+}
+
+static void class_function_regmap_unlock(void *data)
+{
+ struct mutex *lock = data;
+
+ mutex_unlock(lock);
+}
+
+static bool class_function_regmap_writeable(struct device *dev, unsigned int reg)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+
+ return sdca_regmap_writeable(drv->function, reg);
+}
+
+static bool class_function_regmap_readable(struct device *dev, unsigned int reg)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+
+ return sdca_regmap_readable(drv->function, reg);
+}
+
+static bool class_function_regmap_volatile(struct device *dev, unsigned int reg)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+
+ return sdca_regmap_volatile(drv->function, reg);
+}
+
+static const struct regmap_config class_function_regmap_config = {
+ .name = "sdca",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+
+ .max_register = SDW_SDCA_MAX_REGISTER,
+ .readable_reg = class_function_regmap_readable,
+ .writeable_reg = class_function_regmap_writeable,
+ .volatile_reg = class_function_regmap_volatile,
+
+ .cache_type = REGCACHE_MAPLE,
+
+ .lock = class_function_regmap_lock,
+ .unlock = class_function_regmap_unlock,
+};
+
+static int class_function_regmap_mbq_size(struct device *dev, unsigned int reg)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+
+ return sdca_regmap_mbq_size(drv->function, reg);
+}
+
+static bool class_function_regmap_deferrable(struct device *dev, unsigned int reg)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+
+ return sdca_regmap_deferrable(drv->function, reg);
+}
+
+static const struct regmap_sdw_mbq_cfg class_function_mbq_config = {
+ .mbq_size = class_function_regmap_mbq_size,
+ .deferrable = class_function_regmap_deferrable,
+ .retry_us = 1000,
+ .timeout_us = 10000,
+};
+
+static int class_function_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct class_function_drv *drv = snd_soc_component_get_drvdata(dai->component);
+
+ return sdca_asoc_set_constraints(drv->dev, drv->regmap, drv->function,
+ substream, dai);
+}
+
+static int class_function_sdw_add_peripheral(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct class_function_drv *drv = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_slave *sdw = dev_to_sdw_dev(drv->dev->parent);
+ struct sdw_stream_config sconfig = {0};
+ struct sdw_port_config pconfig = {0};
+ int ret;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ snd_sdw_params_to_config(substream, params, &sconfig, &pconfig);
+
+ /*
+ * FIXME: As also noted in sdca_asoc_get_port(), currently only
+ * a single unshared port is supported for each DAI.
+ */
+ ret = sdca_asoc_get_port(drv->dev, drv->regmap, drv->function, dai);
+ if (ret < 0)
+ return ret;
+
+ pconfig.num = ret;
+
+ ret = sdw_stream_add_slave(sdw, &sconfig, &pconfig, 1, sdw_stream);
+ if (ret) {
+ dev_err(drv->dev, "failed to add sdw stream: %d\n", ret);
+ return ret;
+ }
+
+ return sdca_asoc_hw_params(drv->dev, drv->regmap, drv->function,
+ substream, params, dai);
+}
+
+static int class_function_sdw_remove_peripheral(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct class_function_drv *drv = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_slave *sdw = dev_to_sdw_dev(drv->dev->parent);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ return sdw_stream_remove_slave(sdw, sdw_stream);
+}
+
+static int class_function_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops class_function_sdw_ops = {
+ .startup = class_function_startup,
+ .shutdown = sdca_asoc_free_constraints,
+ .set_stream = class_function_sdw_set_stream,
+ .hw_params = class_function_sdw_add_peripheral,
+ .hw_free = class_function_sdw_remove_peripheral,
+};
+
+static int class_function_component_probe(struct snd_soc_component *component)
+{
+ struct class_function_drv *drv = snd_soc_component_get_drvdata(component);
+ struct sdca_class_drv *core = drv->core;
+
+ return sdca_irq_populate(drv->function, component, core->irq_info);
+}
+
+static const struct snd_soc_component_driver class_function_component_drv = {
+ .probe = class_function_component_probe,
+ .endianness = 1,
+};
+
+static int class_function_boot(struct class_function_drv *drv)
+{
+ unsigned int reg = SDW_SDCA_CTL(drv->function->desc->adr,
+ SDCA_ENTITY_TYPE_ENTITY_0,
+ SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(drv->regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(drv->dev, "failed to read function status: %d\n", ret);
+ return ret;
+ }
+
+ if (!(val & SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET)) {
+ dev_dbg(drv->dev, "reset function device\n");
+
+ ret = sdca_reset_function(drv->dev, drv->function, drv->regmap);
+ if (ret)
+ return ret;
+ }
+
+ if (val & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) {
+ dev_dbg(drv->dev, "write initialisation\n");
+
+ ret = sdca_regmap_write_init(drv->dev, drv->core->dev_regmap,
+ drv->function);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(drv->regmap, reg,
+ SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION);
+ if (ret < 0) {
+ dev_err(drv->dev,
+ "failed to clear function init status: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ /* Start FDL process */
+ ret = sdca_irq_populate_early(drv->dev, drv->regmap, drv->function,
+ drv->core->irq_info);
+ if (ret)
+ return ret;
+
+ ret = sdca_fdl_sync(drv->dev, drv->function, drv->core->irq_info);
+ if (ret)
+ return ret;
+
+ ret = sdca_regmap_write_defaults(drv->dev, drv->regmap, drv->function);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(drv->regmap, reg, 0xFF);
+ if (ret < 0) {
+ dev_err(drv->dev, "failed to clear function status: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int class_function_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *aux_dev_id)
+{
+ struct device *dev = &auxdev->dev;
+ struct sdca_class_drv *core = dev_get_drvdata(dev->parent);
+ struct sdca_device_data *data = &core->sdw->sdca_data;
+ struct sdca_function_desc *desc;
+ struct snd_soc_component_driver *cmp_drv;
+ struct snd_soc_dai_driver *dais;
+ struct class_function_drv *drv;
+ struct regmap_sdw_mbq_cfg *mbq_config;
+ struct regmap_config *config;
+ struct reg_default *defaults;
+ int ndefaults;
+ int num_dais;
+ int ret;
+ int i;
+
+ drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ cmp_drv = devm_kmemdup(dev, &class_function_component_drv, sizeof(*cmp_drv),
+ GFP_KERNEL);
+ if (!cmp_drv)
+ return -ENOMEM;
+
+ config = devm_kmemdup(dev, &class_function_regmap_config, sizeof(*config),
+ GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+
+ mbq_config = devm_kmemdup(dev, &class_function_mbq_config, sizeof(*mbq_config),
+ GFP_KERNEL);
+ if (!mbq_config)
+ return -ENOMEM;
+
+ drv->dev = dev;
+ drv->core = core;
+
+ for (i = 0; i < data->num_functions; i++) {
+ desc = &data->function[i];
+
+ if (desc->type == aux_dev_id->driver_data)
+ break;
+ }
+ if (i == core->sdw->sdca_data.num_functions) {
+ dev_err(dev, "failed to locate function\n");
+ return -EINVAL;
+ }
+
+ drv->function = &core->functions[i];
+
+ ret = sdca_parse_function(dev, core->sdw, desc, drv->function);
+ if (ret)
+ return ret;
+
+ ndefaults = sdca_regmap_count_constants(dev, drv->function);
+ if (ndefaults < 0)
+ return ndefaults;
+
+ defaults = devm_kcalloc(dev, ndefaults, sizeof(*defaults), GFP_KERNEL);
+ if (!defaults)
+ return -ENOMEM;
+
+ ret = sdca_regmap_populate_constants(dev, drv->function, defaults);
+ if (ret < 0)
+ return ret;
+
+ regcache_sort_defaults(defaults, ndefaults);
+
+ auxiliary_set_drvdata(auxdev, drv);
+
+ config->reg_defaults = defaults;
+ config->num_reg_defaults = ndefaults;
+ config->lock_arg = &core->regmap_lock;
+
+ if (drv->function->busy_max_delay) {
+ mbq_config->timeout_us = drv->function->busy_max_delay;
+ mbq_config->retry_us = umax(drv->function->busy_max_delay / 10,
+ mbq_config->retry_us);
+ }
+
+ drv->regmap = devm_regmap_init_sdw_mbq_cfg(dev, core->sdw, config, mbq_config);
+ if (IS_ERR(drv->regmap))
+ return dev_err_probe(dev, PTR_ERR(drv->regmap),
+ "failed to create regmap");
+
+ ret = sdca_asoc_populate_component(dev, drv->function, cmp_drv,
+ &dais, &num_dais,
+ &class_function_sdw_ops);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 200);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_get_noresume(dev);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = class_function_boot(drv);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(dev, cmp_drv, dais, num_dais);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register component\n");
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static int class_function_runtime_suspend(struct device *dev)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+
+ /*
+ * Whilst the driver doesn't power the chip down here, going into
+ * runtime suspend means the driver can't be sure the bus won't
+ * power down which would prevent communication with the device.
+ */
+ regcache_cache_only(drv->regmap, true);
+
+ return 0;
+}
+
+static int class_function_runtime_resume(struct device *dev)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+ int ret;
+
+ regcache_mark_dirty(drv->regmap);
+ regcache_cache_only(drv->regmap, false);
+
+ ret = regcache_sync(drv->regmap);
+ if (ret) {
+ dev_err(drv->dev, "failed to restore register cache: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(drv->regmap, true);
+
+ return ret;
+}
+
+static const struct dev_pm_ops class_function_pm_ops = {
+ RUNTIME_PM_OPS(class_function_runtime_suspend,
+ class_function_runtime_resume, NULL)
+};
+
+static const struct auxiliary_device_id class_function_id_table[] = {
+ {
+ .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_SMART_AMP_NAME,
+ .driver_data = SDCA_FUNCTION_TYPE_SMART_AMP,
+ },
+ {
+ .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_SMART_MIC_NAME,
+ .driver_data = SDCA_FUNCTION_TYPE_SMART_MIC,
+ },
+ {
+ .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_UAJ_NAME,
+ .driver_data = SDCA_FUNCTION_TYPE_UAJ,
+ },
+ {
+ .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_HID_NAME,
+ .driver_data = SDCA_FUNCTION_TYPE_HID,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(auxiliary, class_function_id_table);
+
+static struct auxiliary_driver class_function_drv = {
+ .driver = {
+ .name = "sdca_function",
+ .pm = pm_ptr(&class_function_pm_ops),
+ },
+
+ .probe = class_function_probe,
+ .id_table = class_function_id_table
+};
+module_auxiliary_driver(class_function_drv);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SDCA Class Function Driver");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c
index cb79dc3131b8..3180ebd07c40 100644
--- a/sound/soc/sdca/sdca_fdl.c
+++ b/sound/soc/sdca/sdca_fdl.c
@@ -51,6 +51,10 @@ int sdca_reset_function(struct device *dev, struct sdca_function_data *function,
return -EINVAL;
}
+ /*
+ * Poll up to 16 times but no more than once per ms, these are just
+ * arbitrarily selected values, so may be fine tuned in future.
+ */
poll_us = umin(function->reset_max_delay >> 4, 1000);
ret = regmap_read_poll_timeout(regmap, reg, val, !val, poll_us,
@@ -402,8 +406,6 @@ int sdca_fdl_process(struct sdca_interrupt *interrupt)
unsigned int reg, status;
int response, ret;
- guard(mutex)(&fdl_state->lock);
-
ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap,
interrupt->function, interrupt->entity,
interrupt->control);
@@ -412,56 +414,59 @@ int sdca_fdl_process(struct sdca_interrupt *interrupt)
sdca_ump_cancel_timeout(&fdl_state->timeout);
- reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
- SDCA_CTL_XU_FDL_STATUS, 0);
- ret = regmap_read(interrupt->function_regmap, reg, &status);
- if (ret < 0) {
- dev_err(dev, "failed to read FDL status: %d\n", ret);
- return ret;
- }
-
- dev_dbg(dev, "FDL status: %#x\n", status);
+ scoped_guard(mutex, &fdl_state->lock) {
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr,
+ interrupt->entity->id, SDCA_CTL_XU_FDL_STATUS, 0);
+ ret = regmap_read(interrupt->function_regmap, reg, &status);
+ if (ret < 0) {
+ dev_err(dev, "failed to read FDL status: %d\n", ret);
+ return ret;
+ }
- ret = fdl_status_process(interrupt, status);
- if (ret < 0)
- goto reset_function;
+ dev_dbg(dev, "FDL status: %#x\n", status);
- response = ret;
+ ret = fdl_status_process(interrupt, status);
+ if (ret < 0)
+ goto reset_function;
- dev_dbg(dev, "FDL response: %#x\n", response);
+ response = ret;
- ret = regmap_write(interrupt->function_regmap, reg,
- response | (status & ~SDCA_CTL_XU_FDLH_MASK));
- if (ret < 0) {
- dev_err(dev, "failed to set FDL status signal: %d\n", ret);
- return ret;
- }
+ dev_dbg(dev, "FDL response: %#x\n", response);
- ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap,
- interrupt->function, interrupt->entity,
- interrupt->control);
- if (ret)
- return ret;
-
- switch (response) {
- case SDCA_CTL_XU_FDLH_RESET_ACK:
- dev_dbg(dev, "FDL request reset\n");
+ ret = regmap_write(interrupt->function_regmap, reg,
+ response | (status & ~SDCA_CTL_XU_FDLH_MASK));
+ if (ret < 0) {
+ dev_err(dev, "failed to set FDL status signal: %d\n", ret);
+ return ret;
+ }
- switch (xu->reset_mechanism) {
- default:
- dev_warn(dev, "Requested reset mechanism not implemented\n");
+ ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap,
+ interrupt->function,
+ interrupt->entity,
+ interrupt->control);
+ if (ret)
+ return ret;
+
+ switch (response) {
+ case SDCA_CTL_XU_FDLH_RESET_ACK:
+ dev_dbg(dev, "FDL request reset\n");
+
+ switch (xu->reset_mechanism) {
+ default:
+ dev_warn(dev, "Requested reset mechanism not implemented\n");
+ fallthrough;
+ case SDCA_XU_RESET_FUNCTION:
+ goto reset_function;
+ }
+ case SDCA_CTL_XU_FDLH_COMPLETE:
+ if (status & SDCA_CTL_XU_FDLD_REQ_ABORT ||
+ status == SDCA_CTL_XU_FDLD_COMPLETE)
+ return 0;
fallthrough;
- case SDCA_XU_RESET_FUNCTION:
- goto reset_function;
- }
- case SDCA_CTL_XU_FDLH_COMPLETE:
- if (status & SDCA_CTL_XU_FDLD_REQ_ABORT ||
- status == SDCA_CTL_XU_FDLD_COMPLETE)
+ default:
+ sdca_ump_schedule_timeout(&fdl_state->timeout, xu->max_delay);
return 0;
- fallthrough;
- default:
- sdca_ump_schedule_timeout(&fdl_state->timeout, xu->max_delay);
- return 0;
+ }
}
reset_function:
diff --git a/sound/soc/sdca/sdca_function_device.c b/sound/soc/sdca/sdca_function_device.c
new file mode 100644
index 000000000000..91c49d7389db
--- /dev/null
+++ b/sound/soc/sdca/sdca_function_device.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2024 Intel Corporation.
+
+/*
+ * SDCA Function Device management
+ */
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+#include "sdca_function_device.h"
+
+/*
+ * A SoundWire device can have multiple SDCA functions identified by
+ * their type and ADR. there can be multiple SoundWire devices per
+ * link, or multiple devices spread across multiple links. An IDA is
+ * required to identify each instance.
+ */
+static DEFINE_IDA(sdca_function_ida);
+
+static void sdca_dev_release(struct device *dev)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct sdca_dev *sdev = auxiliary_dev_to_sdca_dev(auxdev);
+
+ ida_free(&sdca_function_ida, auxdev->id);
+ kfree(sdev);
+}
+
+/* alloc, init and add link devices */
+static struct sdca_dev *sdca_dev_register(struct device *parent,
+ struct sdca_function_desc *function_desc)
+{
+ struct sdca_dev *sdev;
+ struct auxiliary_device *auxdev;
+ int ret;
+ int rc;
+
+ sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
+ if (!sdev)
+ return ERR_PTR(-ENOMEM);
+
+ auxdev = &sdev->auxdev;
+ auxdev->name = function_desc->name;
+ auxdev->dev.parent = parent;
+ auxdev->dev.fwnode = function_desc->node;
+ auxdev->dev.release = sdca_dev_release;
+
+ sdev->function.desc = function_desc;
+
+ rc = ida_alloc(&sdca_function_ida, GFP_KERNEL);
+ if (rc < 0) {
+ kfree(sdev);
+ return ERR_PTR(rc);
+ }
+ auxdev->id = rc;
+
+ /* now follow the two-step init/add sequence */
+ ret = auxiliary_device_init(auxdev);
+ if (ret < 0) {
+ dev_err(parent, "failed to initialize SDCA function dev %s\n",
+ function_desc->name);
+ ida_free(&sdca_function_ida, auxdev->id);
+ kfree(sdev);
+ return ERR_PTR(ret);
+ }
+
+ ret = auxiliary_device_add(auxdev);
+ if (ret < 0) {
+ dev_err(parent, "failed to add SDCA function dev %s\n",
+ sdev->auxdev.name);
+ /* sdev will be freed with the put_device() and .release sequence */
+ auxiliary_device_uninit(&sdev->auxdev);
+ return ERR_PTR(ret);
+ }
+
+ return sdev;
+}
+
+static void sdca_dev_unregister(struct sdca_dev *sdev)
+{
+ auxiliary_device_delete(&sdev->auxdev);
+ auxiliary_device_uninit(&sdev->auxdev);
+}
+
+int sdca_dev_register_functions(struct sdw_slave *slave)
+{
+ struct sdca_device_data *sdca_data = &slave->sdca_data;
+ int i;
+
+ for (i = 0; i < sdca_data->num_functions; i++) {
+ struct sdca_dev *func_dev;
+
+ func_dev = sdca_dev_register(&slave->dev,
+ &sdca_data->function[i]);
+ if (!func_dev)
+ return -ENODEV;
+
+ sdca_data->function[i].func_dev = func_dev;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA");
+
+void sdca_dev_unregister_functions(struct sdw_slave *slave)
+{
+ struct sdca_device_data *sdca_data = &slave->sdca_data;
+ int i;
+
+ for (i = 0; i < sdca_data->num_functions; i++)
+ sdca_dev_unregister(sdca_data->function[i].func_dev);
+}
+EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_function_device.h b/sound/soc/sdca/sdca_function_device.h
new file mode 100644
index 000000000000..5adf7551d3a4
--- /dev/null
+++ b/sound/soc/sdca/sdca_function_device.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/* Copyright(c) 2024 Intel Corporation. */
+
+#ifndef __SDCA_FUNCTION_DEVICE_H
+#define __SDCA_FUNCTION_DEVICE_H
+
+struct sdca_dev {
+ struct auxiliary_device auxdev;
+ struct sdca_function_data function;
+};
+
+#define auxiliary_dev_to_sdca_dev(auxiliary_dev) \
+ container_of(auxiliary_dev, struct sdca_dev, auxdev)
+
+#endif
diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c
index 0fcc8e82041e..e4b3efeb30f0 100644
--- a/sound/soc/sdca/sdca_functions.c
+++ b/sound/soc/sdca/sdca_functions.c
@@ -6,8 +6,6 @@
* https://www.mipi.org/mipi-sdca-v1-0-download
*/
-#define dev_fmt(fmt) "%s: " fmt, __func__
-
#include <linux/acpi.h>
#include <linux/byteorder/generic.h>
#include <linux/cleanup.h>
@@ -1013,10 +1011,10 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti
control->type = find_sdca_control_datatype(entity, control);
control->nbits = find_sdca_control_bits(entity, control);
- dev_info(dev, "%s: %s: control %#x mode %#x layers %#x cn %#llx int %d %s\n",
- entity->label, control->label, control->sel,
- control->mode, control->layers, control->cn_list,
- control->interrupt_position, control->deferrable ? "deferrable" : "");
+ dev_dbg(dev, "%s: %s: control %#x mode %#x layers %#x cn %#llx int %d %s\n",
+ entity->label, control->label, control->sel,
+ control->mode, control->layers, control->cn_list,
+ control->interrupt_position, control->deferrable ? "deferrable" : "");
return 0;
}
@@ -1137,9 +1135,9 @@ static int find_sdca_entity_iot(struct device *dev,
if (!ret)
terminal->num_transducer = tmp;
- dev_info(dev, "%s: terminal type %#x ref %#x conn %#x count %d\n",
- entity->label, terminal->type, terminal->reference,
- terminal->connector, terminal->num_transducer);
+ dev_dbg(dev, "%s: terminal type %#x ref %#x conn %#x count %d\n",
+ entity->label, terminal->type, terminal->reference,
+ terminal->connector, terminal->num_transducer);
return 0;
}
@@ -1165,8 +1163,8 @@ static int find_sdca_entity_cs(struct device *dev,
if (!ret)
clock->max_delay = tmp;
- dev_info(dev, "%s: clock type %#x delay %d\n", entity->label,
- clock->type, clock->max_delay);
+ dev_dbg(dev, "%s: clock type %#x delay %d\n", entity->label,
+ clock->type, clock->max_delay);
return 0;
}
@@ -1217,8 +1215,8 @@ static int find_sdca_entity_pde(struct device *dev,
delays[i].to_ps = delay_list[j++];
delays[i].us = delay_list[j++];
- dev_info(dev, "%s: from %#x to %#x delay %dus\n", entity->label,
- delays[i].from_ps, delays[i].to_ps, delays[i].us);
+ dev_dbg(dev, "%s: from %#x to %#x delay %dus\n", entity->label,
+ delays[i].from_ps, delays[i].to_ps, delays[i].us);
}
power->num_max_delay = num_delays;
@@ -1324,7 +1322,7 @@ find_sdca_entity_hide(struct device *dev, struct sdw_slave *sdw,
unsigned char *report_desc = NULL;
ret = fwnode_property_read_u32(entity_node,
- "mipi-sdca-RxUMP-ownership-transition-maxdelay", &delay);
+ "mipi-sdca-RxUMP-ownership-transition-max-delay", &delay);
if (!ret)
hide->max_delay = delay;
@@ -1447,8 +1445,8 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw,
entity->type = tmp;
- dev_info(dev, "%s: entity %#x type %#x\n",
- entity->label, entity->id, entity->type);
+ dev_dbg(dev, "%s: entity %#x type %#x\n",
+ entity->label, entity->id, entity->type);
switch (entity->type) {
case SDCA_ENTITY_TYPE_IT:
@@ -1623,7 +1621,7 @@ static int find_sdca_entity_connection_iot(struct device *dev,
terminal->clock = clock_entity;
- dev_info(dev, "%s -> %s\n", clock_entity->label, entity->label);
+ dev_dbg(dev, "%s -> %s\n", clock_entity->label, entity->label);
fwnode_handle_put(clock_node);
return 0;
@@ -1673,7 +1671,7 @@ static int find_sdca_entity_connection_pde(struct device *dev,
return -EINVAL;
}
- dev_info(dev, "%s -> %s\n", managed[i]->label, entity->label);
+ dev_dbg(dev, "%s -> %s\n", managed[i]->label, entity->label);
}
power->num_managed = num_managed;
@@ -1808,7 +1806,7 @@ static int find_sdca_entity_connection(struct device *dev,
pins[i] = connected_entity;
- dev_info(dev, "%s -> %s\n", connected_entity->label, entity->label);
+ dev_dbg(dev, "%s -> %s\n", connected_entity->label, entity->label);
i++;
fwnode_handle_put(connected_node);
@@ -1893,8 +1891,8 @@ static int find_sdca_cluster_channel(struct device *dev,
channel->relationship = tmp;
- dev_info(dev, "cluster %#x: channel id %#x purpose %#x relationship %#x\n",
- cluster->id, channel->id, channel->purpose, channel->relationship);
+ dev_dbg(dev, "cluster %#x: channel id %#x purpose %#x relationship %#x\n",
+ cluster->id, channel->id, channel->purpose, channel->relationship);
return 0;
}
@@ -2065,7 +2063,7 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw,
return -EINVAL;
}
- dev_info(dev, "fileset: %#x\n", filesets_list[i]);
+ dev_dbg(dev, "fileset: %#x\n", filesets_list[i]);
files = devm_kcalloc(dev, num_entries / mult_fileset,
sizeof(struct sdca_fdl_file), GFP_KERNEL);
@@ -2086,8 +2084,8 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw,
file->file_id = fileset_entries[j++];
file->fdl_offset = fileset_entries[j++];
- dev_info(dev, "file: %#x, vendor: %#x, offset: %#x\n",
- file->file_id, file->vendor_id, file->fdl_offset);
+ dev_dbg(dev, "file: %#x, vendor: %#x, offset: %#x\n",
+ file->file_id, file->vendor_id, file->fdl_offset);
}
set->id = filesets_list[i];
@@ -2130,9 +2128,9 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw,
if (!ret)
function->reset_max_delay = tmp;
- dev_info(dev, "%pfwP: name %s busy delay %dus reset delay %dus\n",
- function->desc->node, function->desc->name,
- function->busy_max_delay, function->reset_max_delay);
+ dev_dbg(dev, "%pfwP: name %s busy delay %dus reset delay %dus\n",
+ function->desc->node, function->desc->name,
+ function->busy_max_delay, function->reset_max_delay);
ret = find_sdca_init_table(dev, function_desc->node, function);
if (ret)
diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c
index ad53207b0d62..abbd56a3d297 100644
--- a/sound/soc/sdca/sdca_hid.c
+++ b/sound/soc/sdca/sdca_hid.c
@@ -166,6 +166,3 @@ int sdca_hid_process_report(struct sdca_interrupt *interrupt)
return 0;
}
EXPORT_SYMBOL_NS(sdca_hid_process_report, "SND_SOC_SDCA");
-
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_DESCRIPTION("SDCA HID library");
diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c
index 5176460416bb..8f6a2adfb6fb 100644
--- a/sound/soc/sdca/sdca_interrupts.c
+++ b/sound/soc/sdca/sdca_interrupts.c
@@ -456,11 +456,8 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap,
else if (!interrupt)
continue;
- switch (entity->type) {
- case SDCA_ENTITY_TYPE_XU:
- if (control->sel != SDCA_CTL_XU_FDL_CURRENTOWNER)
- break;
-
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
ret = sdca_irq_data_populate(dev, regmap, NULL,
function, entity,
control, interrupt);
@@ -534,27 +531,22 @@ int sdca_irq_populate(struct sdca_function_data *function,
handler = base_handler;
- switch (entity->type) {
- case SDCA_ENTITY_TYPE_ENTITY_0:
- if (control->sel == SDCA_CTL_ENTITY_0_FUNCTION_STATUS)
- handler = function_status_handler;
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS):
+ handler = function_status_handler;
break;
- case SDCA_ENTITY_TYPE_GE:
- if (control->sel == SDCA_CTL_GE_DETECTED_MODE)
- handler = detected_mode_handler;
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ handler = detected_mode_handler;
break;
- case SDCA_ENTITY_TYPE_XU:
- if (control->sel == SDCA_CTL_XU_FDL_CURRENTOWNER) {
- ret = sdca_fdl_alloc_state(interrupt);
- if (ret)
- return ret;
+ case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
+ ret = sdca_fdl_alloc_state(interrupt);
+ if (ret)
+ return ret;
- handler = fdl_owner_handler;
- }
+ handler = fdl_owner_handler;
break;
- case SDCA_ENTITY_TYPE_HIDE:
- if (control->sel == SDCA_CTL_HIDE_HIDTX_CURRENTOWNER)
- handler = hid_handler;
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER):
+ handler = hid_handler;
break;
default:
break;
@@ -618,6 +610,3 @@ struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev,
return info;
}
EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SDCA IRQ library");
diff --git a/sound/soc/sdca/sdca_regmap.c b/sound/soc/sdca/sdca_regmap.c
index 8fa138fca00f..2cca9a9c71ea 100644
--- a/sound/soc/sdca/sdca_regmap.c
+++ b/sound/soc/sdca/sdca_regmap.c
@@ -275,6 +275,49 @@ int sdca_regmap_populate_constants(struct device *dev,
}
EXPORT_SYMBOL_NS(sdca_regmap_populate_constants, "SND_SOC_SDCA");
+static int populate_control_defaults(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct sdca_control *control)
+{
+ int i, ret;
+ int cn;
+
+ if (control->mode == SDCA_ACCESS_MODE_DC)
+ return 0;
+
+ if (control->layers & SDCA_ACCESS_LAYER_DEVICE)
+ return 0;
+
+ i = 0;
+ for_each_set_bit(cn, (unsigned long *)&control->cn_list,
+ BITS_PER_TYPE(control->cn_list)) {
+ unsigned int reg, val;
+
+ reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, cn);
+
+ if (control->has_default || control->has_fixed) {
+ ret = regmap_write(regmap, reg, control->values[i]);
+ if (ret) {
+ dev_err(dev, "Failed to write default %#x: %d\n",
+ reg, ret);
+ return ret;
+ }
+
+ i++;
+ } else if (!control->is_volatile) {
+ ret = regmap_read(regmap, reg, &val);
+ if (ret) {
+ dev_err(dev, "Failed to read initial %#x: %d\n",
+ reg, ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
/**
* sdca_regmap_write_defaults - write out DisCo defaults to device
* @dev: Pointer to the device.
@@ -283,14 +326,17 @@ EXPORT_SYMBOL_NS(sdca_regmap_populate_constants, "SND_SOC_SDCA");
*
* This function will write out to the hardware all the DisCo default and
* fixed value controls. This will cause them to be populated into the cache,
- * and subsequent handling can be done through a cache sync.
+ * and subsequent handling can be done through a cache sync. It will also
+ * read any non-volatile registers that don't have defaults/fixed values to
+ * populate those into the cache, this ensures they are available for reads
+ * even when the device is runtime suspended.
*
* Return: Returns zero on success, and a negative error code on failure.
*/
int sdca_regmap_write_defaults(struct device *dev, struct regmap *regmap,
struct sdca_function_data *function)
{
- int i, j, k;
+ int i, j;
int ret;
for (i = 0; i < function->num_entities; i++) {
@@ -298,31 +344,30 @@ int sdca_regmap_write_defaults(struct device *dev, struct regmap *regmap,
for (j = 0; j < entity->num_controls; j++) {
struct sdca_control *control = &entity->controls[j];
- int cn;
- if (control->mode == SDCA_ACCESS_MODE_DC)
- continue;
-
- if (!control->has_default && !control->has_fixed)
- continue;
-
- k = 0;
- for_each_set_bit(cn, (unsigned long *)&control->cn_list,
- BITS_PER_TYPE(control->cn_list)) {
- unsigned int reg;
+ ret = populate_control_defaults(dev, regmap, function,
+ entity, control);
+ if (ret)
+ return ret;
+ }
+ }
- reg = SDW_SDCA_CTL(function->desc->adr, entity->id,
- control->sel, cn);
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_regmap_write_defaults, "SND_SOC_SDCA");
- ret = regmap_write(regmap, reg, control->values[k]);
- if (ret)
- return ret;
+int sdca_regmap_write_init(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function)
+{
+ struct sdca_init_write *init = function->init_table;
+ int ret, i;
- k++;
- }
- }
+ for (i = 0; i < function->num_init_table; i++) {
+ ret = regmap_write(regmap, init[i].addr, init[i].val);
+ if (ret)
+ return ret;
}
return 0;
}
-EXPORT_SYMBOL_NS(sdca_regmap_write_defaults, "SND_SOC_SDCA");
+EXPORT_SYMBOL_NS(sdca_regmap_write_init, "SND_SOC_SDCA");