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.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmo9XtYACgkQJNaLcl1U
 h9CvHAf/asLvScBxUkGKP76NoX7+czVbpfxH+yfJ6qavcOzvi+hqHlIGh/9vpFSW
 GENKI/z/g/9x1WRfwODuBdvBvfkvMGfdUKZoly1zpEICML9cDwvTHwh35TkrwVTf
 /cSawS/CByv2ppdeoNe4bBTTSqf4PC0tWo3tRHR2f/LTuSAyac+eJp2PGpRoofBn
 te//kJBNSOGLBxJuikOcsf6xfHzB5Rnl5QMXEk1wts5Y51NEpx0PixNbQqJ7Zvee
 4jGbytul9RlS26QNM6O5qLGiQy3SpECadwvtMUiYW3jn8/YtSbl760sCeGQXjaRE
 7AXiqTVbFRktJ0ffg25eeD4twOE2dQ==
 =NKjz
 -----END PGP SIGNATURE-----

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.
This commit is contained in:
Takashi Iwai 2026-06-26 07:33:15 +02:00
commit 677b16108a
33 changed files with 368 additions and 267 deletions

View File

@ -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;

View File

@ -11,6 +11,7 @@
#include <linux/idr.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/jiffies.h>
#include <linux/lockdep_types.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
@ -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 */

View File

@ -10,6 +10,8 @@
#include <sound/soc.h>
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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -17,7 +17,7 @@
struct rt5575_spi_burst_write {
u8 cmd;
u32 addr;
__le32 addr;
u8 data[RT5575_SPI_BUF_LEN];
u8 dummy;
} __packed;

View File

@ -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 |

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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 @@ 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;

View File

@ -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);

View File

@ -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);
}