aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
authorTakashi Iwai <tiwai@suse.de>2026-06-26 07:33:15 +0200
committerTakashi Iwai <tiwai@suse.de>2026-06-26 07:33:15 +0200
commit677b16108a7457c1fa1cd1b39301e46dfc3aed06 (patch)
tree7fb2eeffe0f8036ad239f41c73fc7b41d5455baa /sound
parent29b9667982e4df2ed7744f86b1144f8bb58eb698 (diff)
parentcf6f56990ea21172e085f0588e5bbf2089ce8f58 (diff)
downloadath-677b16108a7457c1fa1cd1b39301e46dfc3aed06.tar.gz
Merge tag 'asoc-fix-v7.2-merge-window' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Fixes for v7.2 We've got a good collection of device specific fix here, plus a couple of stand out things: - Richard fixed some special cases with the new device_link creation by more gracefully handling any errors during creation. - Charles did some light refactoring of the SoundWire interfaces to fix some persistent randconfig issues that people kept running into.
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/cs530x.c29
-rw-r--r--sound/soc/codecs/cs530x.h6
-rw-r--r--sound/soc/codecs/es9356.c4
-rw-r--r--sound/soc/codecs/max98373-sdw.c4
-rw-r--r--sound/soc/codecs/pcm512x.c2
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.c4
-rw-r--r--sound/soc/codecs/rt1308-sdw.c4
-rw-r--r--sound/soc/codecs/rt1316-sdw.c4
-rw-r--r--sound/soc/codecs/rt5575-spi.c2
-rw-r--r--sound/soc/codecs/rt5645.c6
-rw-r--r--sound/soc/codecs/rt5645.h1
-rw-r--r--sound/soc/codecs/rt5682-sdw.c4
-rw-r--r--sound/soc/codecs/rt700-sdw.c4
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.c4
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c4
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.c4
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c4
-rw-r--r--sound/soc/codecs/rt715-sdw.c4
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.c4
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c4
-rw-r--r--sound/soc/codecs/tac5xx2-sdw.c4
-rw-r--r--sound/soc/codecs/tas2783-sdw.c77
-rw-r--r--sound/soc/codecs/tlv320aic3x.c25
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c20
-rw-r--r--sound/soc/generic/audio-graph-card2.c12
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c8
-rw-r--r--sound/soc/rockchip/rockchip_sai.c259
-rw-r--r--sound/soc/sdca/sdca_asoc.c3
-rw-r--r--sound/soc/soc-core.c35
29 files changed, 324 insertions, 221 deletions
diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c
index 18b5ff75feec4..2c7e331359117 100644
--- a/sound/soc/codecs/cs530x.c
+++ b/sound/soc/codecs/cs530x.c
@@ -1093,6 +1093,29 @@ static int cs530x_component_probe(struct snd_soc_component *component)
return 0;
}
+static bool cs530x_mclk_freq_is_valid(struct cs530x_priv *cs530x,
+ unsigned int freq)
+{
+ /*
+ * All these chips support 48 kHz- and 44.1 kHz-related sample rates,
+ * but they differ in what MCLK frequency is required for achieving
+ * the sample rate.
+ */
+ switch (cs530x->devtype) {
+ case CS4282:
+ case CS4302:
+ case CS4304:
+ case CS4308:
+ return freq == 49152000 || freq == 45158400;
+ case CS5302:
+ case CS5304:
+ case CS5308:
+ return freq == 24576000 || freq == 22579200;
+ }
+
+ return false;
+}
+
static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
@@ -1101,11 +1124,7 @@ static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id,
switch (source) {
case CS530X_SYSCLK_SRC_MCLK:
- switch (freq) {
- case CS530X_SYSCLK_REF_45_1MHZ:
- case CS530X_SYSCLK_REF_49_1MHZ:
- break;
- default:
+ if (!cs530x_mclk_freq_is_valid(cs530x, freq)) {
dev_err(component->dev, "Invalid MCLK source rate %d\n", freq);
return -EINVAL;
}
diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h
index 1e2f6a7a589c1..18aa4dfd0c860 100644
--- a/sound/soc/codecs/cs530x.h
+++ b/sound/soc/codecs/cs530x.h
@@ -200,12 +200,6 @@
/* IN_VOL_CTL5 and OUT_VOL_CTL5 */
#define CS530X_INOUT_VU BIT(0)
-/* MCLK Reference Source Frequency */
-/* 41KHz related */
-#define CS530X_SYSCLK_REF_45_1MHZ 45158400
-/* 48KHz related */
-#define CS530X_SYSCLK_REF_49_1MHZ 49152000
-
/* System Clock Source */
#define CS530X_SYSCLK_SRC_MCLK 0
#define CS530X_SYSCLK_SRC_PLL 1
diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c
index 670e918b56a46..8db81d5746240 100644
--- a/sound/soc/codecs/es9356.c
+++ b/sound/soc/codecs/es9356.c
@@ -1111,8 +1111,10 @@ static int es9356_sdca_dev_resume(struct device *dev)
es9356->disable_irq = false;
ret = sdw_slave_wait_for_init(slave, es9356_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(es9356->regmap, false);
regcache_sync(es9356->regmap);
diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c
index 6829fa07c9ecb..7a42052dc0516 100644
--- a/sound/soc/codecs/max98373-sdw.c
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -272,8 +272,10 @@ static int max98373_resume(struct device *dev)
return 0;
ret = sdw_slave_wait_for_init(slave, MAX98373_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(max98373->regmap, false);
regcache_sync(max98373->regmap);
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
index fdef98ce52f19..fe3b5011fa167 100644
--- a/sound/soc/codecs/pcm512x.c
+++ b/sound/soc/codecs/pcm512x.c
@@ -633,7 +633,7 @@ static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream,
struct regmap *regmap = pcm512x->regmap;
if (IS_ERR(pcm512x->sclk)) {
- dev_info(dev, "No SCLK, using BCLK: %ld\n",
+ dev_info_once(dev, "No SCLK, using BCLK: %ld\n",
PTR_ERR(pcm512x->sclk));
/* Disable reporting of missing SCLK as an error */
diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c
index d62e8a2536767..91d3d43cd9988 100644
--- a/sound/soc/codecs/rt1017-sdca-sdw.c
+++ b/sound/soc/codecs/rt1017-sdca-sdw.c
@@ -779,8 +779,10 @@ static int rt1017_sdca_dev_resume(struct device *dev)
return 0;
ret = sdw_slave_wait_for_init(slave, RT1017_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(rt1017->regmap, false);
regcache_sync(rt1017->regmap);
diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c
index 39e06a3a75609..60e5040b6dd9d 100644
--- a/sound/soc/codecs/rt1308-sdw.c
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -774,8 +774,10 @@ static int rt1308_dev_resume(struct device *dev)
return 0;
ret = sdw_slave_wait_for_init(slave, RT1308_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(rt1308->regmap, false);
regcache_sync_region(rt1308->regmap, 0xc000, 0xcfff);
diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c
index 1828fd9d5af6a..5e8eda6a5f7f8 100644
--- a/sound/soc/codecs/rt1316-sdw.c
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -751,8 +751,10 @@ static int rt1316_dev_resume(struct device *dev)
return 0;
ret = sdw_slave_wait_for_init(slave, RT1316_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(rt1316->regmap, false);
regcache_sync(rt1316->regmap);
diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c
index 9a349965435b6..d5b3a57c88666 100644
--- a/sound/soc/codecs/rt5575-spi.c
+++ b/sound/soc/codecs/rt5575-spi.c
@@ -17,7 +17,7 @@
struct rt5575_spi_burst_write {
u8 cmd;
- u32 addr;
+ __le32 addr;
u8 data[RT5575_SPI_BUF_LEN];
u8 dummy;
} __packed;
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 8a9af260e5f7f..e9819653b30d6 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -83,6 +83,8 @@ static const struct reg_sequence rt5650_init_list[] = {
{RT5645_PWR_ANLG1, 0x02},
{RT5645_IL_CMD3, 0x6728},
{RT5645_PR_BASE + 0x3a, 0x0000},
+ {RT5645_CLSD_OUT_CTRL1, 0x4059},
+ {RT5645_GEN_CTRL3, 0x0200},
};
static const struct reg_default rt5645_reg[] = {
@@ -1855,13 +1857,9 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
RT5645_PWR_CLS_D_L,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L);
- snd_soc_component_update_bits(component, RT5645_GEN_CTRL3,
- RT5645_DET_CLK_MASK, RT5645_DET_CLK_MODE1);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_component_update_bits(component, RT5645_GEN_CTRL3,
- RT5645_DET_CLK_MASK, RT5645_DET_CLK_DIS);
snd_soc_component_write(component, RT5645_EQ_CTRL2, 0);
snd_soc_component_update_bits(component, RT5645_PWR_DIG1,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index bef74b29fd541..a5bfe9861b8ba 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -118,6 +118,7 @@
#define RT5645_A_JD_CTRL1 0x94
#define RT5645_VAD_CTRL4 0x9d
#define RT5645_CLSD_OUT_CTRL 0xa0
+#define RT5645_CLSD_OUT_CTRL1 0xa1
/* Function - Digital */
#define RT5645_ADC_EQ_CTRL1 0xae
#define RT5645_ADC_EQ_CTRL2 0xaf
diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c
index ec2a35a0cacde..dec8c2147d684 100644
--- a/sound/soc/codecs/rt5682-sdw.c
+++ b/sound/soc/codecs/rt5682-sdw.c
@@ -769,8 +769,10 @@ static int rt5682_dev_resume(struct device *dev)
}
ret = sdw_slave_wait_for_init(slave, RT5682_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(rt5682->sdw_regmap, false);
regcache_cache_only(rt5682->regmap, false);
diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c
index 30fcca210f051..6bc636c86f427 100644
--- a/sound/soc/codecs/rt700-sdw.c
+++ b/sound/soc/codecs/rt700-sdw.c
@@ -528,8 +528,10 @@ static int rt700_dev_resume(struct device *dev)
return 0;
ret = sdw_slave_wait_for_init(slave, RT700_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(rt700->regmap, false);
regcache_sync_region(rt700->regmap, 0x3000, 0x8fff);
diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c
index a8164fc3979ab..461315844ba99 100644
--- a/sound/soc/codecs/rt711-sdca-sdw.c
+++ b/sound/soc/codecs/rt711-sdca-sdw.c
@@ -454,8 +454,10 @@ static int rt711_sdca_dev_resume(struct device *dev)
}
ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(rt711->regmap, false);
regcache_sync(rt711->regmap);
diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c
index 4c5c2f5ba5edf..8b7d50a80ff98 100644
--- a/sound/soc/codecs/rt712-sdca-dmic.c
+++ b/sound/soc/codecs/rt712-sdca-dmic.c
@@ -911,8 +911,10 @@ static int rt712_sdca_dmic_dev_resume(struct device *dev)
return 0;
ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(rt712->regmap, false);
regcache_sync(rt712->regmap);
diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c
index 5817321804736..2787524c796e8 100644
--- a/sound/soc/codecs/rt712-sdca-sdw.c
+++ b/sound/soc/codecs/rt712-sdca-sdw.c
@@ -467,8 +467,10 @@ static int rt712_sdca_dev_resume(struct device *dev)
}
ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(rt712->regmap, false);
regcache_sync(rt712->regmap);
diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c
index 4b9815b5628db..fabd21bbbe5b9 100644
--- a/sound/soc/codecs/rt715-sdca-sdw.c
+++ b/sound/soc/codecs/rt715-sdca-sdw.c
@@ -230,8 +230,10 @@ static int rt715_dev_resume(struct device *dev)
return 0;
ret = sdw_slave_wait_for_init(slave, RT715_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(rt715->regmap, false);
regcache_sync_region(rt715->regmap,
diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c
index 7f83a8f1a06e9..a4a3945522e81 100644
--- a/sound/soc/codecs/rt715-sdw.c
+++ b/sound/soc/codecs/rt715-sdw.c
@@ -507,8 +507,10 @@ static int rt715_dev_resume(struct device *dev)
return 0;
ret = sdw_slave_wait_for_init(slave, RT715_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(rt715->regmap, false);
regcache_sync_region(rt715->regmap, 0x3000, 0x8fff);
diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c
index 58606209316a4..02df04a0ddad4 100644
--- a/sound/soc/codecs/rt721-sdca-sdw.c
+++ b/sound/soc/codecs/rt721-sdca-sdw.c
@@ -505,8 +505,10 @@ static int rt721_sdca_dev_resume(struct device *dev)
}
ret = sdw_slave_wait_for_init(slave, RT721_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(rt721->regmap, false);
regcache_sync(rt721->regmap);
diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c
index 0f76492ff915c..284900933ebf4 100644
--- a/sound/soc/codecs/rt722-sdca-sdw.c
+++ b/sound/soc/codecs/rt722-sdca-sdw.c
@@ -552,8 +552,10 @@ static int rt722_sdca_dev_resume(struct device *dev)
}
ret = sdw_slave_wait_for_init(slave, RT722_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(rt722->regmap, false);
regcache_sync(rt722->regmap);
diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c
index bb12cfb6da12b..ace06f5ab58c1 100644
--- a/sound/soc/codecs/tac5xx2-sdw.c
+++ b/sound/soc/codecs/tac5xx2-sdw.c
@@ -1445,8 +1445,10 @@ static s32 tac5xx2_sdca_dev_resume(struct device *dev)
}
ret = sdw_slave_wait_for_init(slave, TAC5XX2_PROBE_TIMEOUT_MS);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(tac_dev->regmap, false);
regcache_mark_dirty(tac_dev->regmap);
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
index 7d70e7e3f24f4..3d0b116544cc4 100644
--- a/sound/soc/codecs/tas2783-sdw.c
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -100,6 +100,8 @@ struct tas2783_prv {
wait_queue_head_t fw_wait;
bool fw_dl_task_done;
bool fw_dl_success;
+ /* use fallback fw name */
+ bool fw_use_fallback;
};
static const struct reg_default tas2783_reg_default[] = {
@@ -740,11 +742,19 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
goto out;
}
+ /* firmware binary not found*/
if (!fmw || !fmw->data) {
- /* firmware binary not found*/
- dev_err(tas_dev->dev,
- "Failed to read fw binary %s\n",
- tas_dev->rca_binaryname);
+ if (!tas_dev->fw_use_fallback) {
+ tas_dev->fw_use_fallback = true;
+ dev_info(tas_dev->dev,
+ "Failed to read preferred fw binary: %s, attempting fallback binary load\n",
+ tas_dev->rca_binaryname);
+ } else {
+ dev_err(tas_dev->dev,
+ "Failed to read fallback fw binary %s\n",
+ tas_dev->rca_binaryname);
+ }
+
ret = -EINVAL;
goto out;
}
@@ -1083,8 +1093,10 @@ static s32 tas2783_sdca_dev_resume(struct device *dev)
int ret;
ret = sdw_slave_wait_for_init(slave, TAS2783_PROBE_TIMEOUT);
- if (ret)
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
return ret;
+ }
regcache_cache_only(tas_dev->regmap, false);
regcache_sync(tas_dev->regmap);
@@ -1103,13 +1115,16 @@ static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t siz
bool pci_found = false;
#if IS_ENABLED(CONFIG_PCI)
struct device *dev = &slave->dev;
+ struct tas2783_prv *tas_dev = dev_get_drvdata(&slave->dev);
struct pci_dev *pci = NULL;
+ const char *fw_uid_prefix = tas_dev->fw_use_fallback ? "" : "0x";
for (; dev; dev = dev->parent) {
if (dev->bus == &pci_bus_type) {
pci = to_pci_dev(dev);
- scnprintf(name, size, "%04X-%1X-%1X.bin",
- pci->subsystem_device, bus->link_id, unique_id);
+ scnprintf(name, size, "%04X-%1X-%s%1X.bin",
+ pci->subsystem_device, bus->link_id,
+ fw_uid_prefix, unique_id);
pci_found = true;
break;
}
@@ -1121,28 +1136,15 @@ static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t siz
bus->link_id, unique_id);
}
-static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
+static s32 tas_fw_load(struct tas2783_prv *tas_dev, struct sdw_slave *slave)
{
- struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
s32 ret;
u8 unique_id = tas_dev->sdw_peripheral->id.unique_id;
- if (tas_dev->hw_init)
- return 0;
-
- tas_dev->fw_dl_task_done = false;
- tas_dev->fw_dl_success = false;
-
- ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1);
- if (ret) {
- dev_err(dev, "sw reset failed, err=%d", ret);
- return ret;
- }
- usleep_range(2000, 2200);
-
tas_generate_fw_name(slave, tas_dev->rca_binaryname,
sizeof(tas_dev->rca_binaryname));
+ tas_dev->fw_dl_task_done = false;
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
tas_dev->rca_binaryname, tas_dev->dev,
GFP_KERNEL, tas_dev, tas2783_fw_ready);
@@ -1157,8 +1159,35 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
msecs_to_jiffies(TIMEOUT_FW_DL_MS));
if (!ret) {
dev_err(tas_dev->dev, "fw request, wait_event timeout\n");
- ret = -EAGAIN;
- } else {
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
+ s32 ret;
+
+ if (tas_dev->hw_init)
+ return 0;
+
+ tas_dev->fw_dl_success = false;
+
+ ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1);
+ if (ret) {
+ dev_err(dev, "sw reset failed, err=%d", ret);
+ return ret;
+ }
+ usleep_range(2000, 2200);
+
+ tas_dev->fw_use_fallback = false;
+ ret = tas_fw_load(tas_dev, slave);
+ if (!ret && tas_dev->fw_use_fallback)
+ ret = tas_fw_load(tas_dev, slave);
+
+ if (!ret) {
if (tas_dev->sa_func_data)
ret = sdca_regmap_write_init(dev, tas_dev->regmap,
tas_dev->sa_func_data);
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index b12c1952823ba..b38393a8130ff 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1049,11 +1049,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ static const u8 dual_rate_q[] = {4, 8, 9, 12, 16};
struct snd_soc_component *component = dai->component;
struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
u16 d, pll_d = 1;
+ bool dual_rate;
int clk;
int width = aic3x->slot_width;
@@ -1079,14 +1081,25 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
/* Fsref can be 44100 or 48000 */
fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
+ dual_rate = params_rate(params) >= 64000;
/* Try to find a value for Q which allows us to bypass the PLL and
* generate CODEC_CLK directly. */
- for (pll_q = 2; pll_q < 18; pll_q++)
- if (aic3x->sysclk / (128 * pll_q) == fsref) {
- bypass_pll = 1;
- break;
+ if (dual_rate) {
+ for (int i = 0; i < ARRAY_SIZE(dual_rate_q); i++) {
+ pll_q = dual_rate_q[i];
+ if (aic3x->sysclk / (128 * pll_q) == fsref) {
+ bypass_pll = 1;
+ break;
+ }
}
+ } else {
+ for (pll_q = 2; pll_q < 18; pll_q++)
+ if (aic3x->sysclk / (128 * pll_q) == fsref) {
+ bypass_pll = 1;
+ break;
+ }
+ }
if (bypass_pll) {
pll_q &= 0xf;
@@ -1106,13 +1119,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
* right DAC to right channel input */
data = (LDAC2LCH | RDAC2RCH);
data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
- if (params_rate(params) >= 64000)
+ if (dual_rate)
data |= DUAL_RATE_MODE;
snd_soc_component_write(component, AIC3X_CODEC_DATAPATH_REG, data);
/* codec sample rate select */
data = (fsref * 20) / params_rate(params);
- if (params_rate(params) < 64000)
+ if (!dual_rate)
data /= 2;
data /= 5;
data -= 2;
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index 5aa96af994c41..38f2b7c63133a 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -288,6 +288,26 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
config_be.dst_addr_width = buswidth;
config_be.dst_maxburst = dma_params_be->maxburst;
+ /*
+ * For eDMA, the back-end may report a maxburst size that is not evenly
+ * divisible by the channel count. This causes the DMA transfer length
+ * to misalign with the FIFO boundary, resulting in wrong data and
+ * audible noise. Align maxburst to the nearest valid boundary:
+ * - If maxburst >= channel count, override to the channel count so
+ * each transfer equals exactly one audio frame.
+ * - If maxburst < channel count, override to 1 to avoid partial-frame
+ * transfers.
+ */
+ if (asrc->use_edma && (dma_params_be->maxburst % params_channels(params))) {
+ if (dma_params_be->maxburst >= params_channels(params)) {
+ config_be.src_maxburst = params_channels(params);
+ config_be.dst_maxburst = params_channels(params);
+ } else {
+ config_be.src_maxburst = 1;
+ config_be.dst_maxburst = 1;
+ }
+ }
+
memset(&audio_config, 0, sizeof(audio_config));
config_be.peripheral_config = &audio_config;
config_be.peripheral_size = sizeof(audio_config);
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
index 6894bb936cfd2..0202ed0ee78e8 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -778,18 +778,6 @@ static void graph_link_init(struct simple_util_priv *priv,
graph_parse_daifmt(ports_cpu, &daifmt);
graph_parse_daifmt(ports_codec, &daifmt);
graph_parse_daifmt(lnk, &daifmt);
- if (daifmt) {
- struct device *dev = simple_priv_to_dev(priv);
-
- /*
- * Recommend to use Auto Select by using .auto_selectable_formats.
- * linux/sound/soc/renesas/rcar/core.c can be good sample for it.
- *
- * One note is that Audio Graph Card2 still keeps compatible to set
- * DAI format via DT.
- */
- dev_warn_once(dev, "use .auto_selectable_formats on each corresponding CPU/Codec driver");
- }
graph_util_parse_link_direction(lnk, &playback_only, &capture_only);
graph_util_parse_link_direction(ports_cpu, &playback_only, &capture_only);
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index 2e5b25b8d00fd..641d6d2432299 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -587,6 +587,10 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
token = hdr->token & APM_WRITE_TOKEN_MASK;
done = data->payload;
+ if (!graph->rx_data.buf) {
+ mutex_unlock(&graph->lock);
+ break;
+ }
phys = graph->rx_data.buf[token].phys;
mutex_unlock(&graph->lock);
/* token numbering starts at 0 */
@@ -609,6 +613,10 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
client_event = APM_CLIENT_EVENT_DATA_READ_DONE;
mutex_lock(&graph->lock);
rd_done = data->payload;
+ if (!graph->tx_data.buf) {
+ mutex_unlock(&graph->lock);
+ break;
+ }
phys = graph->tx_data.buf[hdr->token].phys;
mutex_unlock(&graph->lock);
/* token numbering starts at 0 */
diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c
index a195e96fed0ac..585e89f61f0d9 100644
--- a/sound/soc/rockchip/rockchip_sai.c
+++ b/sound/soc/rockchip/rockchip_sai.c
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/clk.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
@@ -18,6 +19,7 @@
#include <sound/pcm_params.h>
#include <sound/dmaengine_pcm.h>
#include <sound/tlv.h>
+
#include "rockchip_sai.h"
#define DRV_NAME "rockchip-sai"
@@ -215,12 +217,14 @@ wait_for_idle:
static int rockchip_sai_runtime_suspend(struct device *dev)
{
struct rk_sai_dev *sai = dev_get_drvdata(dev);
+ unsigned long flags;
rockchip_sai_fsync_lost_detect(sai, 0);
rockchip_sai_fsync_err_detect(sai, 0);
- scoped_guard(spinlock_irqsave, &sai->xfer_lock)
- rockchip_sai_xfer_clk_stop_and_wait(sai, NULL);
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+ rockchip_sai_xfer_clk_stop_and_wait(sai, NULL);
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
regcache_cache_only(sai->regmap, true);
/*
@@ -480,6 +484,7 @@ static int rockchip_sai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
unsigned int mask = 0, val = 0;
unsigned int clk_gates;
+ unsigned long flags;
int ret = 0;
pm_runtime_get_sync(dai->dev);
@@ -495,56 +500,56 @@ static int rockchip_sai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
sai->is_master_mode = false;
break;
default:
- pm_runtime_put(dai->dev);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_pm_put;
}
- scoped_guard(spinlock_irqsave, &sai->xfer_lock) {
- rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates);
- if (sai->initialized) {
- if (sai->has_capture && sai->has_playback)
- rockchip_sai_xfer_stop(sai, -1);
- else if (sai->has_capture)
- rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_CAPTURE);
- else
- rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_PLAYBACK);
- } else {
- rockchip_sai_clear(sai, 0);
- sai->initialized = true;
- }
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+ rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates);
+ if (sai->initialized) {
+ if (sai->has_capture && sai->has_playback)
+ rockchip_sai_xfer_stop(sai, -1);
+ else if (sai->has_capture)
+ rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_CAPTURE);
+ else
+ rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_PLAYBACK);
+ } else {
+ rockchip_sai_clear(sai, 0);
+ sai->initialized = true;
+ }
- regmap_update_bits(sai->regmap, SAI_CKR, mask, val);
+ regmap_update_bits(sai->regmap, SAI_CKR, mask, val);
- mask = SAI_CKR_CKP_MASK | SAI_CKR_FSP_MASK;
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_NORMAL;
- break;
- case SND_SOC_DAIFMT_NB_IF:
- val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_INVERTED;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_NORMAL;
- break;
- case SND_SOC_DAIFMT_IB_IF:
- val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_INVERTED;
- break;
- default:
- ret = -EINVAL;
- break;
- }
+ mask = SAI_CKR_CKP_MASK | SAI_CKR_FSP_MASK;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_INVERTED;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_xfer_unlock;
+ }
- if (ret == 0) {
- regmap_update_bits(sai->regmap, SAI_CKR, mask, val);
- rockchip_sai_fmt_create(sai, fmt);
- }
+ regmap_update_bits(sai->regmap, SAI_CKR, mask, val);
- if (clk_gates)
- regmap_update_bits(sai->regmap, SAI_XFER,
- SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK,
- clk_gates);
- }
+ rockchip_sai_fmt_create(sai, fmt);
+err_xfer_unlock:
+ if (clk_gates)
+ regmap_update_bits(sai->regmap, SAI_XFER,
+ SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK,
+ clk_gates);
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
+err_pm_put:
pm_runtime_put(dai->dev);
return ret;
@@ -560,6 +565,7 @@ static int rockchip_sai_hw_params(struct snd_pcm_substream *substream,
unsigned int ch_per_lane, slot_width;
unsigned int val, fscr, reg;
unsigned int lanes, req_lanes;
+ unsigned long flags;
int ret = 0;
if (!rockchip_sai_stream_valid(substream, dai))
@@ -586,8 +592,8 @@ static int rockchip_sai_hw_params(struct snd_pcm_substream *substream,
dev_err(sai->dev, "not enough lanes (%d) for requested number of %s channels (%d)\n",
lanes, reg == SAI_TXCR ? "playback" : "capture",
params_channels(params));
- pm_runtime_put(sai->dev);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_pm_put;
} else {
lanes = req_lanes;
}
@@ -613,88 +619,84 @@ static int rockchip_sai_hw_params(struct snd_pcm_substream *substream,
val = SAI_XCR_VDW(32);
break;
default:
- pm_runtime_put(sai->dev);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_pm_put;
}
val |= SAI_XCR_CSR(lanes);
- scoped_guard(spinlock_irqsave, &sai->xfer_lock) {
+ spin_lock_irqsave(&sai->xfer_lock, flags);
- regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK | SAI_XCR_CSR_MASK, val);
+ regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK | SAI_XCR_CSR_MASK, val);
- if (!sai->is_tdm)
- regmap_update_bits(sai->regmap, reg, SAI_XCR_SBW_MASK,
- SAI_XCR_SBW(params_physical_width(params)));
+ if (!sai->is_tdm)
+ regmap_update_bits(sai->regmap, reg, SAI_XCR_SBW_MASK,
+ SAI_XCR_SBW(params_physical_width(params)));
- regmap_read(sai->regmap, reg, &val);
+ regmap_read(sai->regmap, reg, &val);
- slot_width = SAI_XCR_SBW_V(val);
- ch_per_lane = params_channels(params) / lanes;
+ slot_width = SAI_XCR_SBW_V(val);
+ ch_per_lane = params_channels(params) / lanes;
- regmap_update_bits(sai->regmap, reg, SAI_XCR_SNB_MASK,
- SAI_XCR_SNB(ch_per_lane));
+ regmap_update_bits(sai->regmap, reg, SAI_XCR_SNB_MASK,
+ SAI_XCR_SNB(ch_per_lane));
- fscr = SAI_FSCR_FW(sai->fw_ratio * slot_width * ch_per_lane);
+ fscr = SAI_FSCR_FW(sai->fw_ratio * slot_width * ch_per_lane);
- switch (sai->fpw) {
- case FPW_ONE_BCLK_WIDTH:
- fscr |= SAI_FSCR_FPW(1);
- break;
- case FPW_ONE_SLOT_WIDTH:
- fscr |= SAI_FSCR_FPW(slot_width);
- break;
- case FPW_HALF_FRAME_WIDTH:
- fscr |= SAI_FSCR_FPW(sai->fw_ratio * slot_width * ch_per_lane / 2);
- break;
- default:
- dev_err(sai->dev, "Invalid Frame Pulse Width %d\n", sai->fpw);
- ret = -EINVAL;
- break;
- }
+ switch (sai->fpw) {
+ case FPW_ONE_BCLK_WIDTH:
+ fscr |= SAI_FSCR_FPW(1);
+ break;
+ case FPW_ONE_SLOT_WIDTH:
+ fscr |= SAI_FSCR_FPW(slot_width);
+ break;
+ case FPW_HALF_FRAME_WIDTH:
+ fscr |= SAI_FSCR_FPW(sai->fw_ratio * slot_width * ch_per_lane / 2);
+ break;
+ default:
+ dev_err(sai->dev, "Invalid Frame Pulse Width %d\n", sai->fpw);
+ ret = -EINVAL;
+ goto err_xfer_unlock;
+ }
- if (ret == 0) {
- regmap_update_bits(sai->regmap, SAI_FSCR,
- SAI_FSCR_FW_MASK | SAI_FSCR_FPW_MASK, fscr);
+ regmap_update_bits(sai->regmap, SAI_FSCR,
+ SAI_FSCR_FW_MASK | SAI_FSCR_FPW_MASK, fscr);
- if (sai->is_master_mode) {
- bclk_rate = sai->fw_ratio * slot_width *
- ch_per_lane * params_rate(params);
- ret = clk_set_rate(sai->mclk, sai->mclk_rate);
- if (ret)
- dev_err(sai->dev, "Failed to set mclk to %u: %pe\n",
- sai->mclk_rate, ERR_PTR(ret));
- else {
- mclk_rate = clk_get_rate(sai->mclk);
- if (mclk_rate < bclk_rate) {
- dev_err(sai->dev, "Mismatch mclk: %u, at least %u\n",
- mclk_rate, bclk_rate);
- ret = -EINVAL;
- } else {
+ if (sai->is_master_mode) {
+ bclk_rate = sai->fw_ratio * slot_width * ch_per_lane * params_rate(params);
+ ret = clk_set_rate(sai->mclk, sai->mclk_rate);
+ if (ret) {
+ dev_err(sai->dev, "Failed to set mclk to %u: %pe\n",
+ sai->mclk_rate, ERR_PTR(ret));
+ goto err_xfer_unlock;
+ }
+
+ mclk_rate = clk_get_rate(sai->mclk);
+ if (mclk_rate < bclk_rate) {
+ dev_err(sai->dev, "Mismatch mclk: %u, at least %u\n",
+ mclk_rate, bclk_rate);
+ ret = -EINVAL;
+ goto err_xfer_unlock;
+ }
- div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
- mclk_req_rate = bclk_rate * div_bclk;
+ div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
+ mclk_req_rate = bclk_rate * div_bclk;
- if (mclk_rate <
- mclk_req_rate - CLK_SHIFT_RATE_HZ_MAX ||
- mclk_rate >
- mclk_req_rate + CLK_SHIFT_RATE_HZ_MAX) {
- dev_err(sai->dev,
- "Mismatch mclk: %u, expected %u (+/- %dHz)\n",
- mclk_rate, mclk_req_rate,
- CLK_SHIFT_RATE_HZ_MAX);
- ret = -EINVAL;
- } else
- regmap_update_bits(sai->regmap,
- SAI_CKR,
- SAI_CKR_MDIV_MASK,
- SAI_CKR_MDIV(div_bclk));
- }
- }
- }
+ if (mclk_rate < mclk_req_rate - CLK_SHIFT_RATE_HZ_MAX ||
+ mclk_rate > mclk_req_rate + CLK_SHIFT_RATE_HZ_MAX) {
+ dev_err(sai->dev, "Mismatch mclk: %u, expected %u (+/- %dHz)\n",
+ mclk_rate, mclk_req_rate, CLK_SHIFT_RATE_HZ_MAX);
+ ret = -EINVAL;
+ goto err_xfer_unlock;
}
+
+ regmap_update_bits(sai->regmap, SAI_CKR, SAI_CKR_MDIV_MASK,
+ SAI_CKR_MDIV(div_bclk));
}
+err_xfer_unlock:
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
+err_pm_put:
pm_runtime_put(sai->dev);
return ret;
@@ -704,6 +706,7 @@ static int rockchip_sai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ unsigned long flags;
if (!rockchip_sai_stream_valid(substream, dai))
return 0;
@@ -724,12 +727,13 @@ static int rockchip_sai_prepare(struct snd_pcm_substream *substream,
* udelay falls short.
*/
udelay(20);
- scoped_guard(spinlock_irqsave, &sai->xfer_lock)
- regmap_update_bits(sai->regmap, SAI_XFER,
- SAI_XFER_CLK_MASK |
- SAI_XFER_FSS_MASK,
- SAI_XFER_CLK_EN |
- SAI_XFER_FSS_EN);
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+ regmap_update_bits(sai->regmap, SAI_XFER,
+ SAI_XFER_CLK_MASK |
+ SAI_XFER_FSS_MASK,
+ SAI_XFER_CLK_EN |
+ SAI_XFER_FSS_EN);
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
}
rockchip_sai_fsync_lost_detect(sai, 1);
@@ -912,6 +916,7 @@ static int rockchip_sai_set_tdm_slot(struct snd_soc_dai *dai,
int slots, int slot_width)
{
struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ unsigned long flags;
unsigned int clk_gates;
int sw = slot_width;
@@ -927,16 +932,16 @@ static int rockchip_sai_set_tdm_slot(struct snd_soc_dai *dai,
return -EINVAL;
pm_runtime_get_sync(dai->dev);
- scoped_guard(spinlock_irqsave, &sai->xfer_lock) {
- rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates);
- regmap_update_bits(sai->regmap, SAI_TXCR, SAI_XCR_SBW_MASK,
- SAI_XCR_SBW(sw));
- regmap_update_bits(sai->regmap, SAI_RXCR, SAI_XCR_SBW_MASK,
- SAI_XCR_SBW(sw));
- regmap_update_bits(sai->regmap, SAI_XFER,
- SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK,
- clk_gates);
- }
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+ rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates);
+ regmap_update_bits(sai->regmap, SAI_TXCR, SAI_XCR_SBW_MASK,
+ SAI_XCR_SBW(sw));
+ regmap_update_bits(sai->regmap, SAI_RXCR, SAI_XCR_SBW_MASK,
+ SAI_XCR_SBW(sw));
+ regmap_update_bits(sai->regmap, SAI_XFER,
+ SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK,
+ clk_gates);
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
pm_runtime_put(dai->dev);
return 0;
diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c
index e76afa396b0ab..b4dedba719dc6 100644
--- a/sound/soc/sdca/sdca_asoc.c
+++ b/sound/soc/sdca/sdca_asoc.c
@@ -160,6 +160,9 @@ static int ge_put_enum_double(struct snd_kcontrol *kcontrol,
unsigned int reg = e->reg;
int ret;
+ if (item[0] >= e->items)
+ return -EINVAL;
+
reg &= ~SDW_SDCA_CTL_CSEL(0x3F);
reg |= SDW_SDCA_CTL_CSEL(SDCA_CTL_GE_DETECTED_MODE);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 86b6c752a56b7..7817beea5b3bc 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1980,19 +1980,15 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card)
}
}
-static void snd_soc_remove_device_links(struct snd_soc_card *card,
- struct snd_soc_component *stop_at)
+static void snd_soc_remove_device_links(struct snd_soc_card *card)
{
struct snd_soc_component *component;
for_each_card_components(card, component) {
- if (card->dev == component->dev)
- continue;
-
- device_link_remove(card->dev, component->dev);
-
- if (component == stop_at)
- return;
+ if (component->card_device_link) {
+ device_link_del(component->card_device_link);
+ component->card_device_link = NULL;
+ }
}
}
@@ -2001,7 +1997,7 @@ static void snd_soc_unbind_card(struct snd_soc_card *card)
if (snd_soc_card_is_instantiated(card)) {
card->instantiated = false;
- snd_soc_remove_device_links(card, NULL);
+ snd_soc_remove_device_links(card);
soc_cleanup_card_resources(card);
}
@@ -2012,7 +2008,6 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_component *component;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
- struct snd_soc_component *last_devlinked_component = NULL;
int ret;
snd_soc_card_mutex_lock_root(card);
@@ -2141,19 +2136,21 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
* Add device_link from card to component so that system_suspend
* will be done in the correct order. The card must suspend first
* to stop audio activity before the components suspend.
+ *
+ * If a driver pair already have a link in the opposite direction
+ * they must manage their own suspend order.
*/
for_each_card_components(card, component) {
if (card->dev == component->dev)
continue;
- if (!device_link_add(card->dev, component->dev, DL_FLAG_STATELESS)) {
- dev_warn(card->dev, "Failed to create device link to %s\n",
+ component->card_device_link = device_link_add(card->dev,
+ component->dev,
+ DL_FLAG_STATELESS);
+ if (!component->card_device_link) {
+ dev_warn(card->dev, "Could not create device link to %s\n",
dev_name(component->dev));
- ret = -EINVAL;
- goto probe_end;
}
-
- last_devlinked_component = component;
}
ret = snd_soc_card_late_probe(card);
@@ -2185,9 +2182,7 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
probe_end:
if (ret < 0) {
- if (last_devlinked_component)
- snd_soc_remove_device_links(card, last_devlinked_component);
-
+ snd_soc_remove_device_links(card);
soc_cleanup_card_resources(card);
}