diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index b7bdf19ebb42..fe5316d93fef 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1372,34 +1372,6 @@ int sdw_slave_get_current_bank(struct sdw_slave *slave) } EXPORT_SYMBOL_GPL(sdw_slave_get_current_bank); -/** - * sdw_slave_wait_for_init - Wait for device initialisation - * @slave: Pointer to the SoundWire peripheral. - * @timeout_ms: Timeout in milliseconds. - * - * Wait for a peripheral device to enumerate and be initialised by the - * SoundWire core. - * - * Return: Zero on success, and a negative error code on failure. - */ -int sdw_slave_wait_for_init(struct sdw_slave *slave, int timeout_ms) -{ - unsigned long time; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(timeout_ms)); - if (!time) { - dev_err(&slave->dev, "Initialization not complete\n"); - sdw_show_ping_status(slave->bus, true); - return -ETIMEDOUT; - } - - slave->unattach_request = 0; - - return 0; -} -EXPORT_SYMBOL_GPL(sdw_slave_wait_for_init); - static int sdw_slave_set_frequency(struct sdw_slave *slave) { int scale_index; diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index a46cbaec5949..b484784e2690 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -1093,8 +1094,6 @@ int sdw_slave_get_current_bank(struct sdw_slave *sdev); int sdw_slave_get_scale_index(struct sdw_slave *slave, u8 *base); -int sdw_slave_wait_for_init(struct sdw_slave *slave, int timeout_ms); - /* messaging and data APIs */ int sdw_read(struct sdw_slave *slave, u32 addr); int sdw_write(struct sdw_slave *slave, u32 addr, u8 value); @@ -1138,12 +1137,6 @@ static inline int sdw_slave_get_current_bank(struct sdw_slave *sdev) return -EINVAL; } -static inline int sdw_slave_wait_for_init(struct sdw_slave *slave, int timeout_ms) -{ - WARN_ONCE(1, "SoundWire API is disabled"); - return -EINVAL; -} - /* messaging and data APIs */ static inline int sdw_read(struct sdw_slave *slave, u32 addr) { @@ -1207,4 +1200,33 @@ static inline int sdw_update_no_pm(struct sdw_slave *slave, u32 addr, u8 mask, u #endif /* CONFIG_SOUNDWIRE */ +/** + * sdw_slave_wait_for_init - Wait for device initialisation + * @slave: Pointer to the SoundWire peripheral. + * @timeout_ms: Timeout in milliseconds. + * + * Wait for a peripheral device to enumerate and be initialised by the + * SoundWire core. + * + * Return: Zero on success, and a negative error code on failure. + */ +static inline int sdw_slave_wait_for_init(struct sdw_slave *slave, int timeout_ms) +{ + unsigned long time; + + if (!slave) + return 0; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(timeout_ms)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete\n"); + return -ETIMEDOUT; + } + + slave->unattach_request = 0; + + return 0; +} + #endif /* __SOUNDWIRE_H */ diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 11bc9527653f..aa423865dbe7 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -10,6 +10,8 @@ #include +struct device_link; + /* * Component probe and remove ordering levels for components with runtime * dependencies. @@ -216,6 +218,8 @@ struct snd_soc_component { struct list_head card_aux_list; /* for auxiliary bound components */ struct list_head card_list; + struct device_link *card_device_link; + const struct snd_soc_component_driver *driver; struct list_head dai_list; diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index d45f805b5d18..b763da613769 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -59,9 +59,9 @@ #define TASDEVICE_CHECKSUM_REG TASDEVICE_REG(0x0, 0x0, 0x7e) /* XM_340 */ -#define TASDEVICE_XM_A1_REG TASDEVICE_REG(0x64, 0x63, 0x3c) +#define TASDEVICE_XM_A1_REG TASDEVICE_REG(0x64, 0x02, 0x4c) /* XM_341 */ -#define TASDEVICE_XM_A2_REG TASDEVICE_REG(0x64, 0x63, 0x38) +#define TASDEVICE_XM_A2_REG TASDEVICE_REG(0x64, 0x02, 0x64) /* Volume control */ #define TAS2563_DVC_LVL TASDEVICE_REG(0x00, 0x02, 0x0c) diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c index 18b5ff75feec..2c7e33135911 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 1e2f6a7a589c..18aa4dfd0c86 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 670e918b56a4..8db81d574624 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 6829fa07c9ec..7a42052dc051 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 fdef98ce52f1..fe3b5011fa16 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 d62e8a253676..91d3d43cd998 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 39e06a3a7560..60e5040b6dd9 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 1828fd9d5af6..5e8eda6a5f7f 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 9a349965435b..d5b3a57c8866 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 8a9af260e5f7..e9819653b30d 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 bef74b29fd54..a5bfe9861b8b 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 ec2a35a0cacd..dec8c2147d68 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 30fcca210f05..6bc636c86f42 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 a8164fc3979a..461315844ba9 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 4c5c2f5ba5ed..8b7d50a80ff9 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 581732180473..2787524c796e 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 4b9815b5628d..fabd21bbbe5b 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 7f83a8f1a06e..a4a3945522e8 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 58606209316a..02df04a0ddad 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 0f76492ff915..284900933ebf 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 bb12cfb6da12..ace06f5ab58c 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 7d70e7e3f24f..3d0b116544cc 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 b12c1952823b..b38393a8130f 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 5aa96af994c4..38f2b7c63133 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 6894bb936cfd..0202ed0ee78e 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 2e5b25b8d00f..641d6d243229 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 a195e96fed0a..585e89f61f0d 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include + #include "rockchip_sai.h" #define DRV_NAME "rockchip-sai" @@ -215,12 +217,14 @@ static void rockchip_sai_xfer_clk_stop_and_wait(struct rk_sai_dev *sai, unsigned 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; - } - - 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; - } - - if (ret == 0) { - regmap_update_bits(sai->regmap, SAI_CKR, mask, val); - rockchip_sai_fmt_create(sai, fmt); - } - - if (clk_gates) - 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); + 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); + + 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; + } + + regmap_update_bits(sai->regmap, SAI_CKR, mask, val); + + 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; - } - - if (ret == 0) { - 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 { - - 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)); - } - } - } - } + 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; } + 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)); + 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; + + 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 e76afa396b0a..b4dedba719dc 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 86b6c752a56b..7817beea5b3b 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); }