diff options
Diffstat (limited to 'sound/soc')
32 files changed, 409 insertions, 112 deletions
diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index 09b475c83c49..e8b6819cc4b4 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -303,13 +303,14 @@ static int create_sdw_dailink(struct snd_soc_card *card, static int create_sdw_dailinks(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, int *be_id, - struct asoc_sdw_dailink *soc_dais, + struct asoc_sdw_dailink *soc_dais, int num_dais, struct snd_soc_codec_conf **codec_conf) { struct device *dev = card->dev; struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private; struct snd_soc_dai_link_component *sdw_platform_component; + int i; int ret; sdw_platform_component = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), @@ -329,7 +330,7 @@ static int create_sdw_dailinks(struct snd_soc_card *card, } /* generate DAI links by each sdw link */ - while (soc_dais->initialised) { + for (i = 0; i < num_dais && soc_dais->initialised; i++) { int current_be_id = 0; ret = create_sdw_dailink(card, soc_dais, dai_links, @@ -463,7 +464,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) /* SDW */ if (sdw_be_num) { ret = create_sdw_dailinks(card, &dai_links, &be_id, - soc_dais, &codec_conf); + soc_dais, num_ends, &codec_conf); if (ret) return ret; } diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index a0fd8a6f9970..a423853f3a97 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -220,13 +220,14 @@ static int create_sdw_dailink(struct snd_soc_card *card, static int create_sdw_dailinks(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, int *be_id, - struct asoc_sdw_dailink *sof_dais, + struct asoc_sdw_dailink *sof_dais, int num_dais, struct snd_soc_codec_conf **codec_conf) { + int i; int ret; /* generate DAI links by each sdw link */ - while (sof_dais->initialised) { + for (i = 0; i < num_dais && sof_dais->initialised; i++) { int current_be_id = 0; ret = create_sdw_dailink(card, sof_dais, dai_links, @@ -334,7 +335,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) /* SDW */ if (sdw_be_num) { ret = create_sdw_dailinks(card, &dai_links, &be_id, - sof_dais, &codec_conf); + sof_dais, num_ends, &codec_conf); if (ret) return ret; } diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c index d7363f9d53bb..879afeb81c42 100644 --- a/sound/soc/codecs/adau1372.c +++ b/sound/soc/codecs/adau1372.c @@ -813,6 +813,11 @@ static int adau1372_set_power(struct adau1372 *adau1372, bool enable) if (adau1372->use_pll) { ret = adau1372_enable_pll(adau1372); if (ret) { + if (!adau1372->pd_gpio) + regmap_update_bits(adau1372->regmap, + ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_PLL_EN, + 0); regcache_cache_only(adau1372->regmap, true); if (adau1372->pd_gpio) gpiod_set_value(adau1372->pd_gpio, 1); diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index a6805d5405cd..a6545f5a8ed0 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -284,22 +284,22 @@ static void aw88261_reg_force_set(struct aw88261 *aw88261) if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) { /* set FORCE_PWM */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG, - AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE); + ~AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE); /* set BOOST_OS_WIDTH */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG, - AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE); + ~AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE); /* set BURST_LOOPR */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG, - AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE); + ~AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE); /* set RSQN_DLY */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG, - AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE); + ~AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE); /* set BURST_SSMODE */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG, - AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE); + ~AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE); /* set BST_BURST */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG, - AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE); + ~AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE); } else { dev_dbg(aw88261->aw_pa->dev, "needn't set reg value"); } diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 795e2764d67e..9928608296d8 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -534,6 +534,7 @@ static void cs35l56_spi_system_reset(struct cs35l56_base *cs35l56_base) * The regmap must remain in cache-only until the chip has * booted, so use a bypassed read. */ + val = 0; ret = read_poll_timeout(regmap_read_bypassed, read_ret, (val > 0) && (val < 0xffffffff), CS35L56_HALO_STATE_POLL_US, @@ -1259,7 +1260,7 @@ ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base, return -EMSGSIZE; ret = simple_write_to_buffer(&cal_data, sizeof(cal_data), ppos, from, count); - if (ret) + if (ret < 0) return ret; ret = cs35l56_stash_calibration(cs35l56_base, &cal_data); @@ -1293,6 +1294,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_create_cal_debugfs, "SND_SOC_CS35L56_SHARED"); void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base) { debugfs_remove_recursive(cs35l56_base->debugfs); + cs35l56_base->debugfs = ERR_PTR(-ENOENT); } EXPORT_SYMBOL_NS_GPL(cs35l56_remove_cal_debugfs, "SND_SOC_CS35L56_SHARED"); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 4fbbdcc87151..033028a4734b 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1326,7 +1326,7 @@ VISIBLE_IF_KUNIT int cs35l56_set_fw_name(struct snd_soc_component *component) } EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_name); -static int cs35l56_component_probe(struct snd_soc_component *component) +static int _cs35l56_component_probe(struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); @@ -1426,6 +1426,17 @@ static void cs35l56_component_remove(struct snd_soc_component *component) cs35l56->component = NULL; } +static int cs35l56_component_probe(struct snd_soc_component *component) +{ + int ret; + + ret = _cs35l56_component_probe(component); + if (ret < 0) + cs35l56_component_remove(component); + + return ret; +} + static int cs35l56_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { @@ -1480,6 +1491,7 @@ static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev) int cs35l56_system_suspend(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; dev_dbg(dev, "system_suspend\n"); @@ -1495,7 +1507,11 @@ int cs35l56_system_suspend(struct device *dev) if (cs35l56->base.irq) disable_irq(cs35l56->base.irq); - return pm_runtime_force_suspend(dev); + ret = pm_runtime_force_suspend(dev); + if ((ret < 0) && cs35l56->base.irq) + enable_irq(cs35l56->base.irq); + + return ret; } EXPORT_SYMBOL_GPL(cs35l56_system_suspend); @@ -1964,11 +1980,14 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); if (ret < 0) { dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); - goto err; + goto err_remove_wm_adsp; } return 0; +err_remove_wm_adsp: + wm_adsp2_remove(&cs35l56->dsp); + err: gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); @@ -2076,6 +2095,8 @@ void cs35l56_remove(struct cs35l56_private *cs35l56) destroy_workqueue(cs35l56->dsp_wq); + wm_adsp2_remove(&cs35l56->dsp); + pm_runtime_dont_use_autosuspend(cs35l56->base.dev); pm_runtime_suspend(cs35l56->base.dev); pm_runtime_disable(cs35l56->base.dev); 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/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 2652fcf2a3a3..3220f9226e0b 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -911,12 +911,14 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm; - const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; + const char *cvt_name; ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); if (ret < 0) return ret; + cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; + if (port == NULL) return -EINVAL; diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 528d5b167ecf..58a5798823d7 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -244,6 +244,11 @@ static const struct va_macro_data sm8250_va_data = { .version = LPASS_CODEC_VERSION_1_0, }; +static const struct va_macro_data sc7280_va_data = { + .has_swr_master = false, + .has_npl_clk = false, +}; + static const struct va_macro_data sm8450_va_data = { .has_swr_master = true, .has_npl_clk = true, @@ -1755,7 +1760,7 @@ static const struct dev_pm_ops va_macro_pm_ops = { }; static const struct of_device_id va_macro_dt_match[] = { - { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data }, + { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sc7280_va_data }, { .compatible = "qcom,sm6115-lpass-va-macro", .data = &sm8450_va_data }, { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data }, { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data }, 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/sma1307.c b/sound/soc/codecs/sma1307.c index 5850bf6e71ca..5cc0834873f0 100644 --- a/sound/soc/codecs/sma1307.c +++ b/sound/soc/codecs/sma1307.c @@ -1592,6 +1592,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) struct sma1307_priv *sma1307 = container_of(work, struct sma1307_priv, check_fault_work.work); unsigned int status1_val, status2_val; + char volume[sizeof("VOLUME=0x12345678")]; char *envp[3] = { NULL, NULL, NULL }; if (sma1307->tsdw_cnt) @@ -1607,7 +1608,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) if (~status1_val & SMA1307_OT1_OK_STATUS) { dev_crit(sma1307->dev, "%s: OT1(Over Temperature Level 1)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1"); + envp[0] = "STATUS=OT1"; if (sma1307->sw_ot1_prot) { /* Volume control (Current Volume -3dB) */ if ((sma1307->cur_vol + 6) <= 0xFA) { @@ -1615,8 +1616,9 @@ static void sma1307_check_fault_worker(struct work_struct *work) regmap_write(sma1307->regmap, SMA1307_0A_SPK_VOL, sma1307->cur_vol); - envp[1] = kasprintf(GFP_KERNEL, - "VOLUME=0x%02X", sma1307->cur_vol); + snprintf(volume, sizeof(volume), + "VOLUME=0x%02X", sma1307->cur_vol); + envp[1] = volume; } } sma1307->tsdw_cnt++; @@ -1625,48 +1627,53 @@ static void sma1307_check_fault_worker(struct work_struct *work) SMA1307_0A_SPK_VOL, sma1307->init_vol); sma1307->tsdw_cnt = 0; sma1307->cur_vol = sma1307->init_vol; - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1_CLEAR"); - envp[1] = kasprintf(GFP_KERNEL, - "VOLUME=0x%02X", sma1307->cur_vol); + envp[0] = "STATUS=OT1_CLEAR"; + snprintf(volume, sizeof(volume), "VOLUME=0x%02X", + sma1307->cur_vol); + envp[1] = volume; } if (~status1_val & SMA1307_OT2_OK_STATUS) { dev_crit(sma1307->dev, "%s: OT2(Over Temperature Level 2)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT2"); + envp[0] = "STATUS=OT2"; + envp[1] = NULL; } if (status1_val & SMA1307_UVLO_STATUS) { dev_crit(sma1307->dev, "%s: UVLO(Under Voltage Lock Out)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=UVLO"); + envp[0] = "STATUS=UVLO"; + envp[1] = NULL; } if (status1_val & SMA1307_OVP_BST_STATUS) { dev_crit(sma1307->dev, "%s: OVP_BST(Over Voltage Protection)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OVP_BST"); + envp[0] = "STATUS=OVP_BST"; + envp[1] = NULL; } if (status2_val & SMA1307_OCP_SPK_STATUS) { dev_crit(sma1307->dev, "%s: OCP_SPK(Over Current Protect SPK)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_SPK"); + envp[0] = "STATUS=OCP_SPK"; + envp[1] = NULL; } if (status2_val & SMA1307_OCP_BST_STATUS) { dev_crit(sma1307->dev, "%s: OCP_BST(Over Current Protect Boost)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_BST"); + envp[0] = "STATUS=OCP_BST"; + envp[1] = NULL; } if (status2_val & SMA1307_CLK_MON_STATUS) { dev_crit(sma1307->dev, "%s: CLK_FAULT(No clock input)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=CLK_FAULT"); + envp[0] = "STATUS=CLK_FAULT"; + envp[1] = NULL; } if (envp[0] != NULL) { if (kobject_uevent_env(sma1307->kobj, KOBJ_CHANGE, envp)) dev_err(sma1307->dev, "%s: Error sending uevent\n", __func__); - kfree(envp[0]); - kfree(envp[1]); } if (sma1307->check_fault_status) { diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index ce22298b43ef..f8e847d28258 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/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index ca630c9948e4..baa75e7ff53b 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1170,7 +1170,14 @@ EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) { + if (!dsp) + return 0; + + if (!dsp->component) + return 0; + cs_dsp_cleanup_debugfs(&dsp->cs_dsp); + dsp->component = NULL; return 0; } 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/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index 40a3b7432174..f819f33ec46b 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -117,6 +117,9 @@ static int fsl_audmix_put_mix_clk_src(struct snd_kcontrol *kcontrol, unsigned int *item = ucontrol->value.enumerated.item; unsigned int reg_val, val, mix_clk; + if (item[0] >= e->items) + return -EINVAL; + /* Get current state */ reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR); mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK) @@ -157,6 +160,9 @@ static int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol, unsigned int reg_val, val, mask = 0, ctr = 0; int ret; + if (item[0] >= e->items) + return -EINVAL; + /* Get current state */ reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR); diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index a7fef772760a..2634699534db 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -766,6 +766,11 @@ static const dai_register_cb dai_register_cbs[] = { mt8183_dai_memif_register, }; +static void mt8183_afe_release_reserved_mem(void *data) +{ + of_reserved_mem_device_release(data); +} + static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; @@ -794,6 +799,12 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) { dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); afe->preallocate_buffers = true; + } else { + ret = devm_add_action_or_reset(dev, + mt8183_afe_release_reserved_mem, + dev); + if (ret) + return ret; } /* initial audio related clock */ @@ -833,17 +844,21 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) /* enable clock for regcache get default value from hw */ afe_priv->pm_runtime_bypass_reg_ctl = true; - pm_runtime_get_sync(dev); - - ret = regmap_reinit_cache(afe->regmap, &mt8183_afe_regmap_config); + ret = pm_runtime_resume_and_get(dev); if (ret) { - dev_err(dev, "regmap_reinit_cache fail, ret %d\n", ret); + afe_priv->pm_runtime_bypass_reg_ctl = false; goto err_pm_disable; } + ret = regmap_reinit_cache(afe->regmap, &mt8183_afe_regmap_config); pm_runtime_put_sync(dev); afe_priv->pm_runtime_bypass_reg_ctl = false; + if (ret) { + dev_err(dev, "regmap_reinit_cache fail, ret %d\n", ret); + goto err_pm_disable; + } + regcache_cache_only(afe->regmap, true); regcache_mark_dirty(afe->regmap); diff --git a/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c b/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c index 24b0c78815f6..77cf2b604f6c 100644 --- a/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c +++ b/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c @@ -2351,9 +2351,13 @@ static int mt8189_afe_runtime_resume(struct device *dev) static int mt8189_afe_component_probe(struct snd_soc_component *component) { struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + int ret; /* enable clock for regcache get default value from hw */ - pm_runtime_get_sync(afe->dev); + ret = pm_runtime_resume_and_get(afe->dev); + if (ret) + return dev_err_probe(afe->dev, ret, "failed to resume device\n"); + mtk_afe_add_sub_dai_control(component); pm_runtime_put_sync(afe->dev); @@ -2417,6 +2421,11 @@ static const struct reg_sequence mt8189_cg_patch[] = { { AUDIO_TOP_CON4, 0x361c }, }; +static void mt8189_afe_release_reserved_mem(void *data) +{ + of_reserved_mem_device_release(data); +} + static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev) { int ret, i; @@ -2431,8 +2440,15 @@ static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev) return ret; ret = of_reserved_mem_device_init(dev); - if (ret) + if (ret) { dev_warn(dev, "failed to assign memory region: %d\n", ret); + } else { + ret = devm_add_action_or_reset(dev, + mt8189_afe_release_reserved_mem, + dev); + if (ret) + return ret; + } afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL); if (!afe) @@ -2533,18 +2549,22 @@ static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev) dev_pm_syscore_device(dev, true); /* enable clock for regcache get default value from hw */ - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return dev_err_probe(dev, ret, "failed to resume device\n"); afe->regmap = devm_regmap_init_mmio(dev, afe->base_addr, &mt8189_afe_regmap_config); - if (IS_ERR(afe->regmap)) - return PTR_ERR(afe->regmap); + if (IS_ERR(afe->regmap)) { + ret = PTR_ERR(afe->regmap); + goto err_pm_put; + } ret = regmap_register_patch(afe->regmap, mt8189_cg_patch, ARRAY_SIZE(mt8189_cg_patch)); if (ret < 0) { dev_err(dev, "Failed to apply cg patch\n"); - goto err_pm_disable; + goto err_pm_put; } regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &tmp_reg); @@ -2563,12 +2583,12 @@ static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev) afe->num_dai_drivers); if (ret) { dev_err(dev, "afe component err: %d\n", ret); - goto err_pm_disable; + return ret; } return 0; -err_pm_disable: +err_pm_put: pm_runtime_put_sync(dev); return ret; } @@ -2578,14 +2598,12 @@ static void mt8189_afe_pcm_dev_remove(struct platform_device *pdev) struct mtk_base_afe *afe = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; - pm_runtime_put_sync(dev); if (!pm_runtime_status_suspended(dev)) mt8189_afe_runtime_suspend(dev); mt8189_afe_disable_main_clock(afe); /* disable afe clock */ mt8189_afe_disable_reg_rw_clk(afe); - of_reserved_mem_device_release(dev); } static const struct of_device_id mt8189_afe_pcm_dt_match[] = { diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c index 3d32fe46118e..db0ae44a86af 100644 --- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c @@ -2155,6 +2155,11 @@ static const dai_register_cb dai_register_cbs[] = { mt8192_dai_memif_register, }; +static void mt8192_afe_release_reserved_mem(void *data) +{ + of_reserved_mem_device_release(data); +} + static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; @@ -2184,6 +2189,10 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) { dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); afe->preallocate_buffers = true; + } else { + ret = devm_add_action_or_reset(dev, mt8192_afe_release_reserved_mem, dev); + if (ret) + return ret; } /* init audio related clock */ @@ -2218,15 +2227,19 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) /* enable clock for regcache get default value from hw */ afe_priv->pm_runtime_bypass_reg_ctl = true; - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) { + afe_priv->pm_runtime_bypass_reg_ctl = false; + return dev_err_probe(dev, ret, "failed to resume device\n"); + } ret = regmap_reinit_cache(afe->regmap, &mt8192_afe_regmap_config); - if (ret) - return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n"); - pm_runtime_put_sync(dev); afe_priv->pm_runtime_bypass_reg_ctl = false; + if (ret) + return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n"); + regcache_cache_only(afe->regmap, true); regcache_mark_dirty(afe->regmap); diff --git a/sound/soc/meson/aiu-acodec-ctrl.c b/sound/soc/meson/aiu-acodec-ctrl.c index 483772ba69cd..94c5d6533523 100644 --- a/sound/soc/meson/aiu-acodec-ctrl.c +++ b/sound/soc/meson/aiu-acodec-ctrl.c @@ -36,6 +36,9 @@ static int aiu_acodec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int mux, changed; + if (ucontrol->value.enumerated.item[0] >= e->items) + return -EINVAL; + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); changed = snd_soc_component_test_bits(component, e->reg, CTRL_DIN_LRCLK_SRC, diff --git a/sound/soc/meson/aiu-codec-ctrl.c b/sound/soc/meson/aiu-codec-ctrl.c index 396f815077e2..60bb4cdfee52 100644 --- a/sound/soc/meson/aiu-codec-ctrl.c +++ b/sound/soc/meson/aiu-codec-ctrl.c @@ -28,6 +28,9 @@ static int aiu_codec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int mux, changed; + if (ucontrol->value.enumerated.item[0] >= e->items) + return -EINVAL; + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); changed = snd_soc_component_test_bits(component, e->reg, CTRL_DATA_SEL, diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 2ab378fb5032..2873e831a8de 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -549,6 +549,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 */ @@ -571,6 +575,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/renesas/rcar/rsnd.h b/sound/soc/renesas/rcar/rsnd.h index 04c70690f7a2..3e666125959b 100644 --- a/sound/soc/renesas/rcar/rsnd.h +++ b/sound/soc/renesas/rcar/rsnd.h @@ -624,7 +624,7 @@ struct rsnd_priv { #define RSND_GEN2 (2 << 0) #define RSND_GEN3 (3 << 0) #define RSND_GEN4 (4 << 0) -#define RSND_SOC_MASK (0xFF << 4) +#define RSND_SOC_MASK (0xF << 4) #define RSND_SOC_E (1 << 4) /* E1/E2/E3 */ /* diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 2bfc8e5aee31..243b83bb2bcf 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/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index e440c2327100..4e8646d231c4 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -818,6 +818,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .codec_name = "cs42l43-codec", + .component_name = "cs42l43-spk", .dai_name = "cs42l43-dp6", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d6192204e613..a26771c8e6ee 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2906,20 +2906,18 @@ static struct snd_soc_dapm_widget *dapm_find_widget( { struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *fallback = NULL; - char prefixed_pin[80]; - const char *pin_name; - const char *prefix = dapm_prefix(dapm); - - if (prefix) { - snprintf(prefixed_pin, sizeof(prefixed_pin), "%s %s", - prefix, pin); - pin_name = prefixed_pin; - } else { - pin_name = pin; - } + bool pin_has_prefix = snd_soc_dapm_pin_has_prefix(dapm->card, pin); + bool match; for_each_card_widgets(dapm->card, w) { - if (!strcmp(w->name, pin_name)) { + match = false; + + if (!strcmp(pin, w->name)) + match = true; + else if (!pin_has_prefix && !snd_soc_dapm_widget_name_cmp(w, pin)) + match = true; + + if (match) { if (w->dapm == dapm) return w; else @@ -4873,6 +4871,33 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); /** + * snd_soc_dapm_pin_has_prefix - check if given pin has a known prefix + * @card: card to be checked + * @pin: pin name + * + * Returns true if given pin has a known prefix + */ +bool snd_soc_dapm_pin_has_prefix(struct snd_soc_card *card, const char *pin) +{ + struct snd_soc_component *component; + const char *prefix; + size_t prefix_len; + + for_each_card_components(card, component) { + prefix = component->name_prefix; + if (!prefix) + continue; + + prefix_len = strlen(prefix); + if (!strncmp(pin, prefix, prefix_len) && pin[prefix_len] == ' ') + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_pin_has_prefix); + +/** * snd_soc_dapm_free - free dapm resources * @dapm: DAPM context * diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 85679c8e0229..35cbe29d2275 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1326,9 +1326,24 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg) return ret; } +static int soc_tplg_check_name(const char *name) +{ + if (strnlen(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + + return 0; +} + static int set_stream_info(struct soc_tplg *tplg, struct snd_soc_pcm_stream *stream, struct snd_soc_tplg_stream_caps *caps) { + int ret; + + ret = soc_tplg_check_name(caps->name); + if (ret) + return ret; + stream->stream_name = devm_kstrdup(tplg->dev, caps->name, GFP_KERNEL); if (!stream->stream_name) return -ENOMEM; @@ -1380,7 +1395,11 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, if (dai_drv == NULL) return -ENOMEM; - if (strlen(pcm->dai_name)) { + ret = soc_tplg_check_name(pcm->dai_name); + if (ret) + goto err; + + if (pcm->dai_name[0]) { dai_drv->name = devm_kstrdup(tplg->dev, pcm->dai_name, GFP_KERNEL); if (!dai_drv->name) { ret = -ENOMEM; @@ -1486,7 +1505,11 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, if (tplg->ops) link->dobj.unload = tplg->ops->link_unload; - if (strlen(pcm->pcm_name)) { + ret = soc_tplg_check_name(pcm->pcm_name); + if (ret) + goto err; + + if (pcm->pcm_name[0]) { link->name = devm_kstrdup(tplg->dev, pcm->pcm_name, GFP_KERNEL); link->stream_name = devm_kstrdup(tplg->dev, pcm->pcm_name, GFP_KERNEL); if (!link->name || !link->stream_name) { @@ -1496,7 +1519,11 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, } link->id = le32_to_cpu(pcm->pcm_id); - if (strlen(pcm->dai_name)) { + ret = soc_tplg_check_name(pcm->dai_name); + if (ret) + goto err; + + if (pcm->dai_name[0]) { link->cpus->dai_name = devm_kstrdup(tplg->dev, pcm->dai_name, GFP_KERNEL); if (!link->cpus->dai_name) { ret = -ENOMEM; @@ -1848,6 +1875,10 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg, memset(&dai_component, 0, sizeof(dai_component)); + ret = soc_tplg_check_name(d->dai_name); + if (ret) + return ret; + dai_component.dai_name = d->dai_name; dai = snd_soc_find_dai(&dai_component); if (!dai) { diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index e31f4c4061d8..915abbef398d 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -266,10 +266,8 @@ config SND_SOC_SOF_METEORLAKE config SND_SOC_SOF_INTEL_LNL tristate - select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE != n select SND_SOC_SOF_HDA_GENERIC select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE - select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_SOUNDWIRE != n select SND_SOC_SOF_IPC4 select SND_SOC_SOF_INTEL_MTL @@ -329,8 +327,10 @@ config SND_SOC_SOF_HDA_GENERIC select SND_INTEL_DSP_CONFIG select SND_SOC_SOF_HDA_LINK_BASELINE select SND_SOC_SOF_HDA_PROBES - select SND_SOC_SDW_UTILS if SND_SOC_SOF_INTEL_SOUNDWIRE + select SND_SOC_SDW_UTILS if SND_SOC_SOF_INTEL_SOUNDWIRE !=n select SND_SOC_SOF_HDA_MLINK if SND_SOC_SOF_HDA_LINK + select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_LNL != n && \ + SND_SOC_SOF_INTEL_SOUNDWIRE !=n help This option is not user-selectable but automagically handled by 'select' statements at a higher level. diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 2b1befad6d5c..d1697401b1da 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -315,10 +315,13 @@ static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol, } /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > scontrol->max_size - sizeof(*data)) { + if (data->size > scontrol->max_size - sizeof(*cdata) - + sizeof(*data)) { dev_err_ratelimited(scomp->dev, "%u bytes of control data is invalid, max is %zu\n", - data->size, scontrol->max_size - sizeof(*data)); + data->size, + scontrol->max_size - sizeof(*cdata) - + sizeof(*data)); return -EINVAL; } @@ -336,6 +339,8 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; struct sof_abi_hdr *data = cdata->data; + const struct sof_abi_hdr *new_hdr = + (const struct sof_abi_hdr *)ucontrol->value.bytes.data; size_t size; if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { @@ -344,14 +349,18 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, return -EINVAL; } - /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > scontrol->max_size - sizeof(*data)) { - dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n", - data->size, scontrol->max_size - sizeof(*data)); + /* Validate the new data's size, not the old one */ + if (new_hdr->size > scontrol->max_size - sizeof(*cdata) - + sizeof(*new_hdr)) { + dev_err_ratelimited(scomp->dev, + "data size too big %u bytes max is %zu\n", + new_hdr->size, + scontrol->max_size - sizeof(*cdata) - + sizeof(*new_hdr)); return -EINVAL; } - size = data->size + sizeof(*data); + size = new_hdr->size + sizeof(*new_hdr); /* copy from kcontrol */ memcpy(data, ucontrol->value.bytes.data, size); @@ -389,9 +398,17 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, } /* be->max is coming from topology */ - if (header.length > scontrol->max_size) { - dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n", - header.length, scontrol->max_size); + if (header.length > scontrol->max_size - sizeof(*cdata)) { + dev_err_ratelimited(scomp->dev, "Bytes data size %u exceeds max %zu\n", + header.length, scontrol->max_size - sizeof(*cdata)); + return -EINVAL; + } + + /* Ensure the data is large enough to contain the ABI header */ + if (header.length < sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, + "Bytes data size %u less than ABI header %zu\n", + header.length, sizeof(struct sof_abi_hdr)); return -EINVAL; } @@ -427,7 +444,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, } /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { + if (cdata->data->size > scontrol->max_size - sizeof(*cdata) - sizeof(struct sof_abi_hdr)) { dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n"); goto err_restore; } @@ -443,7 +460,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, err_restore: /* If we have an issue, we restore the old, valid bytes control data */ if (scontrol->old_ipc_control_data) { - memcpy(cdata->data, scontrol->old_ipc_control_data, scontrol->max_size); + memcpy(cdata, scontrol->old_ipc_control_data, scontrol->max_size); kfree(scontrol->old_ipc_control_data); scontrol->old_ipc_control_data = NULL; } @@ -482,10 +499,13 @@ static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, } /* check data size doesn't exceed max coming from topology */ - if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { - dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n", + if (cdata->data->size > scontrol->max_size - sizeof(*cdata) - + sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, + "User data size %u exceeds max size %zu\n", cdata->data->size, - scontrol->max_size - sizeof(struct sof_abi_hdr)); + scontrol->max_size - sizeof(*cdata) - + sizeof(struct sof_abi_hdr)); return -EINVAL; } @@ -535,6 +555,15 @@ static void snd_sof_update_control(struct snd_sof_control *scontrol, return; } + /* Verify the size fits within the allocation */ + if (cdata->num_elems > scontrol->max_size - sizeof(*local_cdata) - + sizeof(*local_cdata->data)) { + dev_err(scomp->dev, + "cdata binary size %u exceeds buffer\n", + cdata->num_elems); + return; + } + /* copy the new binary data */ memcpy(local_cdata->data, cdata->data, cdata->num_elems); } else if (cdata->num_elems != scontrol->num_channels) { @@ -626,16 +655,28 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ return; } - expected_size = sizeof(struct sof_ipc_ctrl_data); switch (cdata->type) { case SOF_CTRL_TYPE_VALUE_CHAN_GET: case SOF_CTRL_TYPE_VALUE_CHAN_SET: - expected_size += cdata->num_elems * - sizeof(struct sof_ipc_ctrl_value_chan); + if (check_mul_overflow((size_t)cdata->num_elems, + sizeof(struct sof_ipc_ctrl_value_chan), + &expected_size)) + return; + if (check_add_overflow(expected_size, + sizeof(struct sof_ipc_ctrl_data), + &expected_size)) + return; break; case SOF_CTRL_TYPE_DATA_GET: case SOF_CTRL_TYPE_DATA_SET: - expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr); + if (check_add_overflow((size_t)cdata->num_elems, + sizeof(struct sof_abi_hdr), + &expected_size)) + return; + if (check_add_overflow(expected_size, + sizeof(struct sof_ipc_ctrl_data), + &expected_size)) + return; break; default: return; diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 596c3d77a34e..b603a5449221 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -562,6 +562,8 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_abi_hdr *data = cdata->data; + const struct sof_abi_hdr *new_hdr = + (const struct sof_abi_hdr *)ucontrol->value.bytes.data; size_t size; int ret; @@ -572,15 +574,16 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, return -EINVAL; } - /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > scontrol->max_size - sizeof(*data)) { + /* Validate the new data's size, not the old one */ + if (new_hdr->size > scontrol->max_size - sizeof(*new_hdr)) { dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n", - data->size, scontrol->max_size - sizeof(*data)); + new_hdr->size, + scontrol->max_size - sizeof(*new_hdr)); return -EINVAL; } - size = data->size + sizeof(*data); + size = new_hdr->size + sizeof(*new_hdr); /* copy from kcontrol */ memcpy(data, ucontrol->value.bytes.data, size); @@ -880,6 +883,16 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) */ if (type == SND_SOC_TPLG_TYPE_BYTES) { struct sof_abi_hdr *data = cdata->data; + size_t source_size = struct_size(msg_data, data, msg_data->num_elems); + + if (source_size > ndata->event_data_size) { + dev_warn(sdev->dev, + "%s: invalid bytes notification size for %s (%zu, %u)\n", + __func__, scontrol->name, source_size, + ndata->event_data_size); + scontrol->comp_data_dirty = true; + goto notify; + } if (msg_data->num_elems > scontrol->max_size - sizeof(*data)) { dev_warn(sdev->dev, @@ -892,6 +905,17 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size; } } else { + size_t source_size = struct_size(msg_data, chanv, msg_data->num_elems); + + if (source_size > ndata->event_data_size) { + dev_warn(sdev->dev, + "%s: invalid channel notification size for %s (%zu, %u)\n", + __func__, scontrol->name, source_size, + ndata->event_data_size); + scontrol->comp_data_dirty = true; + goto notify; + } + for (i = 0; i < msg_data->num_elems; i++) { u32 channel = msg_data->chanv[i].channel; @@ -919,6 +943,8 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) scontrol->comp_data_dirty = true; } +notify: + /* * Look up the ALSA kcontrol of the scontrol to be able to send a * notification to user space diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 63d582c65891..8f6f89b983eb 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -733,10 +733,13 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp, int ret; while (array_size > 0 && total < count * token_instance_num) { + if (array_size < (int)sizeof(*array)) + return -EINVAL; + asize = le32_to_cpu(array->size); /* validate asize */ - if (asize < sizeof(*array)) { + if (asize < (int)sizeof(*array)) { dev_err(scomp->dev, "error: invalid array size 0x%x\n", asize); return -EINVAL; @@ -2534,6 +2537,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) if (strstr(file, "dummy")) { dev_err(scomp->dev, "Function topology is required, please upgrade sof-firmware\n"); + + kfree(tplg_files); return -EINVAL; } tplg_files[0] = file; diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index ece33b7ff190..efc8f3388668 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -62,13 +62,15 @@ static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl, struct snd_soc_dapm_update update[TEGRA_XBAR_UPDATE_MAX_REG] = { }; int val_bytes = snd_soc_component_regmap_val_bytes(cmpnt); unsigned int *item = uctl->value.enumerated.item; - unsigned int value = e->values[item[0]]; + unsigned int value; unsigned int i, bit_pos, reg_idx = 0, reg_val = 0; int change = 0; if (item[0] >= e->items) return -EINVAL; + value = e->values[item[0]]; + if (value) { /* Get the register index and value to set */ reg_idx = (value - 1) / (8 * val_bytes); |
