From 07c774dd64ba0c605dbf844132122e3edbdbea93 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 19 Feb 2026 04:53:52 +0000 Subject: ASoC: soc-compress: use function to clear symmetric params Current soc-compress.c clears symmetric_rate, but it clears rate only, not clear other symmetric_channels/sample_bits. static int soc_compr_clean(...) { ... if (!snd_soc_dai_active(cpu_dai)) => cpu_dai->symmetric_rate = 0; if (!snd_soc_dai_active(codec_dai)) => codec_dai->symmetric_rate = 0; ... }; This feature was added when v3.7 kernel [1], and there was only symmetric_rate, no symmetric_channels/sample_bits in that timing. symmetric_channels/sample_bits were added in v3.14 [2], but I guess it didn't notice that soc-compress.c is updating symmetric_xxx. We are clearing symmetry_xxx by soc_pcm_set_dai_params(), but is soc-pcm.c local function. Makes it global function and clear symmetry_xxx by it. [1] commit 1245b7005de02 ("ASoC: add compress stream support") [2] commit 3635bf09a89cf ("ASoC: soc-pcm: add symmetry for channels and sample bits") Fixes: 3635bf09a89c ("ASoC: soc-pcm: add symmetry for channels and sample bits") Cc: Nicolin Chen Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ms15e3kv.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 7d8376c8e1be..1e0b7cd8d956 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1412,6 +1412,9 @@ struct snd_soc_dai *snd_soc_find_dai( struct snd_soc_dai *snd_soc_find_dai_with_mutex( const struct snd_soc_dai_link_component *dlc); +void soc_pcm_set_dai_params(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params); + #include static inline -- cgit v1.2.3 From d075cef4af6327a5de4bee7bf77591e3201e54f4 Mon Sep 17 00:00:00 2001 From: Stefano Radaelli Date: Fri, 13 Feb 2026 16:03:55 +0100 Subject: ASoC: simple-card-utils: add sysclk ordering support When simple-audio-card programs sysclk for CPU and codec DAIs during hw_params, the ordering of these calls may matter on some platforms. Some CPU DAIs finalize or adjust the MCLK rate as part of their set_sysclk() callback (for example by calling clk_set_rate()). If the codec sysclk is configured before the CPU DAI applies the final MCLK rate, the codec may configure its internal clocking based on a non-final MCLK value. Such situations can arise depending on the clock provider/consumer relationship between the CPU DAI and the codec. Introduce an explicit sysclk ordering enum in simple-card-utils and use it to control the order of snd_soc_dai_set_sysclk() calls in the mclk-fs handling path. The default behaviour remains unchanged (codec-first) to avoid regressions. Signed-off-by: Stefano Radaelli Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20260213150355.442609-1-stefano.r@variscite.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 6 +++++ sound/soc/generic/simple-card-utils.c | 41 +++++++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 69a9c9c4d0e9..915e6ae5f68d 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -54,6 +54,11 @@ struct prop_nums { int platforms; }; +enum simple_util_sysclk_order { + SIMPLE_SYSCLK_ORDER_CODEC_FIRST = 0, + SIMPLE_SYSCLK_ORDER_CPU_FIRST, +}; + struct simple_util_priv { struct snd_soc_card snd_card; struct simple_dai_props { @@ -63,6 +68,7 @@ struct simple_util_priv { struct snd_soc_codec_conf *codec_conf; struct prop_nums num; unsigned int mclk_fs; + enum simple_util_sysclk_order sysclk_order; } *dai_props; struct simple_util_jack hp_jack; struct simple_util_jack mic_jack; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index bdc02e85b089..fdd8b76f2914 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -468,6 +468,7 @@ int simple_util_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *sdai; struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd); + enum simple_util_sysclk_order order = props->sysclk_order; unsigned int mclk, mclk_fs = 0; int i, ret; @@ -501,18 +502,36 @@ int simple_util_hw_params(struct snd_pcm_substream *substream, goto end; } - for_each_rtd_codec_dais(rtd, i, sdai) { - pdai = simple_props_to_dai_codec(props, i); - ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); - if (ret && ret != -ENOTSUPP) - goto end; - } + if (order == SIMPLE_SYSCLK_ORDER_CPU_FIRST) { + /* CPU first */ + for_each_rtd_cpu_dais(rtd, i, sdai) { + pdai = simple_props_to_dai_cpu(props, i); + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); + if (ret && ret != -ENOTSUPP) + goto end; + } - for_each_rtd_cpu_dais(rtd, i, sdai) { - pdai = simple_props_to_dai_cpu(props, i); - ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); - if (ret && ret != -ENOTSUPP) - goto end; + for_each_rtd_codec_dais(rtd, i, sdai) { + pdai = simple_props_to_dai_codec(props, i); + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); + if (ret && ret != -ENOTSUPP) + goto end; + } + } else { + /* default: codec first */ + for_each_rtd_codec_dais(rtd, i, sdai) { + pdai = simple_props_to_dai_codec(props, i); + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); + if (ret && ret != -ENOTSUPP) + goto end; + } + + for_each_rtd_cpu_dais(rtd, i, sdai) { + pdai = simple_props_to_dai_cpu(props, i); + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); + if (ret && ret != -ENOTSUPP) + goto end; + } } } -- cgit v1.2.3 From 80930d81c4b0753ba2ca750708e4d2fcc0627dc8 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 23 Feb 2026 15:02:55 +0000 Subject: ASoC: soc_sdw_utils: Add device info for CS47L47 Add a device info entry for the Cirrus Logic CS47L47. CS47L47 has UAJ (headset speaker + mic + jack detect) and DMICs. The audio ports are similar to the CS42L45 so can be based on the CS42L45 code. Signed-off-by: Richard Fitzgerald Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260223150256.326143-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/soc_sdw_utils.h | 2 + sound/soc/sdw_utils/Makefile | 1 + sound/soc/sdw_utils/soc_sdw_cs47l47.c | 80 +++++++++++++++++++++++++++++++++++ sound/soc/sdw_utils/soc_sdw_utils.c | 36 ++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 sound/soc/sdw_utils/soc_sdw_cs47l47.c (limited to 'include') diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 227347c8f0b3..98531e500cbb 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -259,6 +259,8 @@ int asoc_sdw_cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_so int asoc_sdw_cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); int asoc_sdw_cs42l45_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); int asoc_sdw_cs42l45_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); +int asoc_sdw_cs47l47_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); +int asoc_sdw_cs47l47_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); int asoc_sdw_maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai); /* TI */ diff --git a/sound/soc/sdw_utils/Makefile b/sound/soc/sdw_utils/Makefile index e8bd5ffb1a6a..a8d091fd374b 100644 --- a/sound/soc/sdw_utils/Makefile +++ b/sound/soc/sdw_utils/Makefile @@ -6,6 +6,7 @@ snd-soc-sdw-utils-y := soc_sdw_utils.o soc_sdw_dmic.o soc_sdw_rt_dmic.o \ soc_sdw_bridge_cs35l56.o \ soc_sdw_cs42l42.o soc_sdw_cs42l43.o \ soc_sdw_cs42l45.o \ + soc_sdw_cs47l47.o \ soc_sdw_cs_amp.o \ soc_sdw_maxim.o \ soc_sdw_ti_amp.o diff --git a/sound/soc/sdw_utils/soc_sdw_cs47l47.c b/sound/soc/sdw_utils/soc_sdw_cs47l47.c new file mode 100644 index 000000000000..259ecf1e0a71 --- /dev/null +++ b/sound/soc/sdw_utils/soc_sdw_cs47l47.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Based on sof_sdw_cs42l45.c +// This file incorporates work covered by the following copyright notice: +// Copyright (c) 2023 Intel Corporation +// Copyright (c) 2024 Advanced Micro Devices, Inc. + +/* + * soc_sdw_cs47l47 - Helpers to handle CS47L47 from generic machine driver + */ +#include +#include +#include +#include +#include +#include +#include +#include + +static struct snd_soc_jack_pin soc_jack_pins[] = { + { + .pin = "cs47l47 OT 43 Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "cs47l47 OT 45 Headset", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "cs47l47 IT 31 Microphone", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "cs47l47 IT 33 Headset", + .mask = SND_JACK_MICROPHONE, + }, +}; + +int asoc_sdw_cs47l47_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; + struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_jack *jack = &ctx->sdw_headset; + int ret; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:cs47l47", + card->components); + if (!card->components) + return -ENOMEM; + + ret = snd_soc_card_jack_new_pins(card, "Jack", SND_JACK_MECHANICAL | + SND_JACK_HEADSET | SND_JACK_LINEOUT, jack, + soc_jack_pins, ARRAY_SIZE(soc_jack_pins)); + if (ret) { + dev_err(card->dev, "Failed to create jack: %d\n", ret); + return ret; + } + + ret = snd_soc_component_set_jack(component, jack, NULL); + if (ret) { + dev_err(card->dev, "Failed to register jack: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS(asoc_sdw_cs47l47_hs_rtd_init, "SND_SOC_SDW_UTILS"); + +int asoc_sdw_cs47l47_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = rtd->card; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s mic:cs47l47-dmic", + card->components); + if (!card->components) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_NS(asoc_sdw_cs47l47_dmic_rtd_init, "SND_SOC_SDW_UTILS"); diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 0e67d9f34cba..eeeb91dded9d 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -759,6 +759,42 @@ struct asoc_sdw_codec_info codec_info_list[] = { }, .aux_num = 1, }, + { + .part_id = 0x4747, + .name_prefix = "cs47l47", + .dais = { + { + .direction = {true, false}, + .codec_name = "snd_soc_sdca.UAJ.1", + .dai_name = "IT 41", + .dai_type = SOC_SDW_DAI_TYPE_JACK, + .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, + .rtd_init = asoc_sdw_cs47l47_hs_rtd_init, + }, + { + .direction = {false, true}, + .codec_name = "snd_soc_sdca.SmartMic.0", + .dai_name = "OT 113", + .dai_type = SOC_SDW_DAI_TYPE_MIC, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, + .rtd_init = asoc_sdw_cs47l47_dmic_rtd_init, + }, + { + .direction = {false, true}, + .codec_name = "snd_soc_sdca.UAJ.1", + .dai_name = "OT 36", + .dai_type = SOC_SDW_DAI_TYPE_JACK, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, + }, + }, + .dai_num = 3, + .auxs = { + { + .codec_name = "snd_soc_sdca.HID.2", + }, + }, + .aux_num = 1, + }, { .part_id = 0xaaaa, /* generic codec mockup */ .name_prefix = "sdw_mockup_mmulti-function", -- cgit v1.2.3 From 501efdcb3b3ab099fc0ce2f6e668b1c4095dd476 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 25 Feb 2026 14:01:18 +0000 Subject: ASoC: SDCA: Pull the Q7.8 volume helpers out of soc-ops It is cleaner to keep the SDCA code contained and not update the core code for things that are unlikely to see reuse outside of SDCA. Move the Q7.8 volume helpers back into the SDCA core code. Reviewed-by: Pierre-Louis Bossart Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260225140118.402695-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/soc.h | 1 - sound/soc/sdca/sdca_asoc.c | 67 +++++++++++++++++++++++++++++++++++++++++++++- sound/soc/soc-ops.c | 56 ++++++++------------------------------ 3 files changed, 77 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 7d8376c8e1be..172bd68e1315 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1239,7 +1239,6 @@ struct soc_mixer_control { unsigned int sign_bit; unsigned int invert:1; unsigned int autodisable:1; - unsigned int sdca_q78:1; #ifdef CONFIG_SND_SOC_TOPOLOGY struct snd_soc_dobj dobj; #endif diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index e6f7c2778bec..a342a4e56717 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -805,6 +805,70 @@ int sdca_asoc_populate_dapm(struct device *dev, struct sdca_function_data *funct } EXPORT_SYMBOL_NS(sdca_asoc_populate_dapm, "SND_SOC_SDCA"); +static int q78_write(struct snd_soc_component *component, + struct soc_mixer_control *mc, + unsigned int reg, const int val) +{ + unsigned int mask = GENMASK(mc->sign_bit, 0); + unsigned int reg_val; + + if (val < 0 || val > mc->max - mc->min) + return -EINVAL; + + reg_val = (val + mc->min) * mc->shift; + + return snd_soc_component_update_bits(component, reg, mask, reg_val); +} + +static int q78_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int ret; + + ret = q78_write(component, mc, mc->reg, ucontrol->value.integer.value[0]); + if (ret < 0) + return ret; + + if (snd_soc_volsw_is_stereo(mc)) { + int err; /* Don't drop change flag */ + + err = q78_write(component, mc, mc->rreg, ucontrol->value.integer.value[1]); + if (err) + return err; + } + + return ret; +} + +static int q78_read(struct snd_soc_component *component, + struct soc_mixer_control *mc, unsigned int reg) +{ + unsigned int reg_val; + int val; + + reg_val = snd_soc_component_read(component, reg); + + val = (sign_extend32(reg_val, mc->sign_bit) / mc->shift) - mc->min; + + return val & GENMASK(mc->sign_bit, 0); +} + +static int q78_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = q78_read(component, mc, mc->reg); + + if (snd_soc_volsw_is_stereo(mc)) + ucontrol->value.integer.value[1] = q78_read(component, mc, mc->rreg); + + return 0; +} + static int control_limit_kctl(struct device *dev, struct sdca_entity *entity, struct sdca_control *control, @@ -845,10 +909,11 @@ static int control_limit_kctl(struct device *dev, mc->max = max / step; mc->shift = step; mc->sign_bit = 15; - mc->sdca_q78 = 1; kctl->tlv.p = tlv; kctl->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + kctl->get = q78_get_volsw; + kctl->put = q78_put_volsw; return 0; } diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 8ae6609ca961..0d633f38cfdc 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -110,29 +110,6 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); -static int sdca_soc_q78_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val, - unsigned int mask, unsigned int shift, int max, - bool sx) -{ - int val = reg_val; - - if (WARN_ON(!mc->shift)) - return -EINVAL; - - val = sign_extend32(val, mc->sign_bit); - - return ((val / mc->shift) - mc->min) & mask; -} - -static unsigned int sdca_soc_q78_ctl_to_reg(struct soc_mixer_control *mc, int val, - unsigned int mask, unsigned int shift, int max) -{ - if (WARN_ON(!mc->shift)) - return -EINVAL; - - return ((val + mc->min) * mc->shift) & mask; -} - static int soc_mixer_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val, unsigned int mask, unsigned int shift, int max, bool sx) @@ -226,27 +203,19 @@ static int soc_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc, int mask, int max) { - unsigned int (*ctl_to_reg)(struct soc_mixer_control *, int, unsigned int, unsigned int, int); struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); unsigned int val1, val_mask; unsigned int val2 = 0; bool double_r = false; int ret; - if (mc->sdca_q78) { - ctl_to_reg = sdca_soc_q78_ctl_to_reg; - val_mask = mask; - } else { - ctl_to_reg = soc_mixer_ctl_to_reg; - val_mask = mask << mc->shift; - } - ret = soc_mixer_valid_ctl(mc, ucontrol->value.integer.value[0], max); if (ret) return ret; - val1 = ctl_to_reg(mc, ucontrol->value.integer.value[0], + val1 = soc_mixer_ctl_to_reg(mc, ucontrol->value.integer.value[0], mask, mc->shift, max); + val_mask = mask << mc->shift; if (snd_soc_volsw_is_stereo(mc)) { ret = soc_mixer_valid_ctl(mc, ucontrol->value.integer.value[1], max); @@ -254,10 +223,14 @@ static int soc_put_volsw(struct snd_kcontrol *kcontrol, return ret; if (mc->reg == mc->rreg) { - val1 |= ctl_to_reg(mc, ucontrol->value.integer.value[1], mask, mc->rshift, max); + val1 |= soc_mixer_ctl_to_reg(mc, + ucontrol->value.integer.value[1], + mask, mc->rshift, max); val_mask |= mask << mc->rshift; } else { - val2 = ctl_to_reg(mc, ucontrol->value.integer.value[1], mask, mc->shift, max); + val2 = soc_mixer_ctl_to_reg(mc, + ucontrol->value.integer.value[1], + mask, mc->shift, max); double_r = true; } } @@ -281,28 +254,21 @@ static int soc_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc, int mask, int max, bool sx) { - int (*reg_to_ctl)(struct soc_mixer_control *, unsigned int, unsigned int, - unsigned int, int, bool); struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); unsigned int reg_val; int val; - if (mc->sdca_q78) - reg_to_ctl = sdca_soc_q78_reg_to_ctl; - else - reg_to_ctl = soc_mixer_reg_to_ctl; - reg_val = snd_soc_component_read(component, mc->reg); - val = reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx); + val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx); ucontrol->value.integer.value[0] = val; if (snd_soc_volsw_is_stereo(mc)) { if (mc->reg == mc->rreg) { - val = reg_to_ctl(mc, reg_val, mask, mc->rshift, max, sx); + val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->rshift, max, sx); } else { reg_val = snd_soc_component_read(component, mc->rreg); - val = reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx); + val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx); } ucontrol->value.integer.value[1] = val; -- cgit v1.2.3 From 2974aa42e6696a1d95b727d677dc01a71af5b998 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 27 Feb 2026 01:17:19 +0000 Subject: ASoC: remove snd_soc_pcm_subclass enum snd_soc_pcm_subclass has added at v3.1 commit b8c0dab9bf337 ("ASoC: core - PCM mutex per rtd"), but has never been used during this 15 years. Let's remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/878qcfyogw.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 7bf7ce085516..b1c5dad26edb 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -422,11 +422,6 @@ struct snd_soc_jack_pin; #include #include -enum snd_soc_pcm_subclass { - SND_SOC_PCM_CLASS_PCM = 0, - SND_SOC_PCM_CLASS_BE = 1, -}; - int snd_soc_register_card(struct snd_soc_card *card); void snd_soc_unregister_card(struct snd_soc_card *card); int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card); @@ -999,7 +994,6 @@ struct snd_soc_card { /* Mutex for PCM operations */ struct mutex pcm_mutex; - enum snd_soc_pcm_subclass pcm_subclass; int (*probe)(struct snd_soc_card *card); int (*late_probe)(struct snd_soc_card *card); @@ -1519,7 +1513,7 @@ static inline void _snd_soc_dapm_mutex_assert_held_d(struct snd_soc_dapm_context */ static inline void _snd_soc_dpcm_mutex_lock_c(struct snd_soc_card *card) { - mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass); + mutex_lock(&card->pcm_mutex); } static inline void _snd_soc_dpcm_mutex_unlock_c(struct snd_soc_card *card) -- cgit v1.2.3 From 84446536f63d471ab16b2faa25eeab1df21ace0a Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 24 Feb 2026 21:56:19 +0100 Subject: ALSA: control: Verify put() result when in debug mode The put() operation is expected to return: 1) 0 on success if no changes were made 2) 1 on success if changes were made 3) error code otherwise Currently 2) is usually ignored when writing control-operations. While forcing compliance is not an option right now, make it easier for developers to adhere to the expectations and notice problems by logging them when CONFIG_SND_CTL_DEBUG is enabled. Due to large size of struct snd_ctl_elem_value, 'value_buf' is provided as a reusable buffer for kctl->put() verification. This prevents exhausting the stack when verifying the operation. >From user perspective, patch introduces a new trace/events category 'snd_ctl' containing a single 'snd_ctl_put' event type. Log sample: amixer-1086 [003] ..... 8.035939: snd_ctl_put: success: expected=0, actual=0 for ctl numid=1, iface=MIXER, name='Master Playback Volume', index=0, device=0, subdevice=0, card=0 amixer-1087 [003] ..... 8.938721: snd_ctl_put: success: expected=1, actual=1 for ctl numid=1, iface=MIXER, name='Master Playback Volume', index=0, device=0, subdevice=0, card=0 amixer-1088 [003] ..... 9.631470: snd_ctl_put: success: expected=1, actual=1 for ctl numid=1, iface=MIXER, name='Master Playback Volume', index=0, device=0, subdevice=0, card=0 amixer-1089 [000] ..... 9.636786: snd_ctl_put: fail: expected=1, actual=0 for ctl numid=5, iface=MIXER, name='Loopback Mute', index=0, device=0, subdevice=0, card=0 Signed-off-by: Cezary Rojewski Reviewed-by: Mark Brown Reviewed-by: Jaroslav Kysela Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260224205619.584795-1-cezary.rojewski@intel.com --- include/sound/core.h | 3 ++ sound/core/Makefile | 1 + sound/core/control.c | 76 +++++++++++++++++++++++++++++++++++++++++++++- sound/core/control_trace.h | 55 +++++++++++++++++++++++++++++++++ sound/core/init.c | 8 +++++ 5 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 sound/core/control_trace.h (limited to 'include') diff --git a/include/sound/core.h b/include/sound/core.h index 64327e971122..4093ec82a0a1 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -133,6 +133,9 @@ struct snd_card { #ifdef CONFIG_SND_DEBUG struct dentry *debugfs_root; /* debugfs root for card */ #endif +#ifdef CONFIG_SND_CTL_DEBUG + struct snd_ctl_elem_value *value_buf; /* buffer for kctl->put() verification */ +#endif #ifdef CONFIG_PM unsigned int power_state; /* power state */ diff --git a/sound/core/Makefile b/sound/core/Makefile index 31a0623cc89d..fdd3bb6e81a9 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -23,6 +23,7 @@ snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o # for trace-points CFLAGS_pcm_lib.o := -I$(src) CFLAGS_pcm_native.o := -I$(src) +CFLAGS_control.o := -I$(src) snd-pcm-dmaengine-y := pcm_dmaengine.o diff --git a/sound/core/control.c b/sound/core/control.c index 934e84e93838..374e703d15a9 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -19,6 +19,13 @@ #include #include +#ifdef CONFIG_SND_CTL_DEBUG +#define CREATE_TRACE_POINTS +#include "control_trace.h" +#else +#define trace_snd_ctl_put(card, kctl, iname, expected, actual) +#endif + // Max allocation size for user controls. static int max_user_ctl_alloc_size = 8 * 1024 * 1024; module_param_named(max_user_ctl_alloc_size, max_user_ctl_alloc_size, int, 0444); @@ -1264,6 +1271,72 @@ static int snd_ctl_elem_read_user(struct snd_card *card, return result; } +#if IS_ENABLED(CONFIG_SND_CTL_DEBUG) + +static const char *const snd_ctl_elem_iface_names[] = { + [SNDRV_CTL_ELEM_IFACE_CARD] = "CARD", + [SNDRV_CTL_ELEM_IFACE_HWDEP] = "HWDEP", + [SNDRV_CTL_ELEM_IFACE_MIXER] = "MIXER", + [SNDRV_CTL_ELEM_IFACE_PCM] = "PCM", + [SNDRV_CTL_ELEM_IFACE_RAWMIDI] = "RAWMIDI", + [SNDRV_CTL_ELEM_IFACE_TIMER] = "TIMER", + [SNDRV_CTL_ELEM_IFACE_SEQUENCER] = "SEQUENCER", +}; + +static int snd_ctl_put_verify(struct snd_card *card, struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *control) +{ + struct snd_ctl_elem_value *original = card->value_buf; + struct snd_ctl_elem_info info; + const char *iname; + int ret, retcmp; + + memset(original, 0, sizeof(*original)); + memset(&info, 0, sizeof(info)); + + ret = kctl->info(kctl, &info); + if (ret) + return ret; + + ret = kctl->get(kctl, original); + if (ret) + return ret; + + ret = kctl->put(kctl, control); + if (ret < 0) + return ret; + + /* Sanitize the new value (control->value) before comparing. */ + fill_remaining_elem_value(control, &info, 0); + + /* With known state for both new and original, do the comparison. */ + retcmp = memcmp(&original->value, &control->value, sizeof(original->value)); + if (retcmp) + retcmp = 1; + + iname = snd_ctl_elem_iface_names[kctl->id.iface]; + trace_snd_ctl_put(&kctl->id, iname, card->number, ret, retcmp); + + return ret; +} + +static int snd_ctl_put(struct snd_card *card, struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *control, unsigned int access) +{ + if ((access & SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK) || + (access & SNDRV_CTL_ELEM_ACCESS_VOLATILE)) + return kctl->put(kctl, control); + + return snd_ctl_put_verify(card, kctl, control); +} +#else +static inline int snd_ctl_put(struct snd_card *card, struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *control, unsigned int access) +{ + return kctl->put(kctl, control); +} +#endif + static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, struct snd_ctl_elem_value *control) { @@ -1300,7 +1373,8 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, false); } if (!result) - result = kctl->put(kctl, control); + result = snd_ctl_put(card, kctl, control, vd->access); + if (result < 0) { up_write(&card->controls_rwsem); return result; diff --git a/sound/core/control_trace.h b/sound/core/control_trace.h new file mode 100644 index 000000000000..d30e654b0860 --- /dev/null +++ b/sound/core/control_trace.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM snd_ctl + +#if !defined(_TRACE_SND_CTL_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SND_CTL_H + +#include +#include + +TRACE_EVENT(snd_ctl_put, + + TP_PROTO(struct snd_ctl_elem_id *id, const char *iname, unsigned int card, + int expected, int actual), + + TP_ARGS(id, iname, card, expected, actual), + + TP_STRUCT__entry( + __field(unsigned int, numid) + __string(iname, iname) + __string(kname, id->name) + __field(unsigned int, index) + __field(unsigned int, device) + __field(unsigned int, subdevice) + __field(unsigned int, card) + __field(int, expected) + __field(int, actual) + ), + + TP_fast_assign( + __entry->numid = id->numid; + __assign_str(iname); + __assign_str(kname); + __entry->index = id->index; + __entry->device = id->device; + __entry->subdevice = id->subdevice; + __entry->card = card; + __entry->expected = expected; + __entry->actual = actual; + ), + + TP_printk("%s: expected=%d, actual=%d for ctl numid=%d, iface=%s, name='%s', index=%d, device=%d, subdevice=%d, card=%d\n", + __entry->expected == __entry->actual ? "success" : "fail", + __entry->expected, __entry->actual, __entry->numid, + __get_str(iname), __get_str(kname), __entry->index, + __entry->device, __entry->subdevice, __entry->card) +); + +#endif /* _TRACE_SND_CTL_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE control_trace +#include diff --git a/sound/core/init.c b/sound/core/init.c index 2f1bd9cbdbed..0c316189e947 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -362,6 +362,11 @@ static int snd_card_init(struct snd_card *card, struct device *parent, #ifdef CONFIG_SND_DEBUG card->debugfs_root = debugfs_create_dir(dev_name(&card->card_dev), sound_debugfs_root); +#endif +#ifdef CONFIG_SND_CTL_DEBUG + card->value_buf = kmalloc(sizeof(*card->value_buf), GFP_KERNEL); + if (!card->value_buf) + return -ENOMEM; #endif return 0; @@ -587,6 +592,9 @@ static int snd_card_do_free(struct snd_card *card) snd_device_free_all(card); if (card->private_free) card->private_free(card); +#ifdef CONFIG_SND_CTL_DEBUG + kfree(card->value_buf); +#endif if (snd_info_card_free(card) < 0) { dev_warn(card->dev, "unable to free card info\n"); /* Not fatal error */ -- cgit v1.2.3 From 73942a6ea26bd7e02b7c260b8b7aa942397be894 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Tue, 24 Feb 2026 16:18:05 +0000 Subject: firmware: cs_dsp: Add API to hibernate the DSP For some parts, the DSP is kept running when in low power mode (hibernation), leaving the firmware ALSA controls enabled, but the registers are inaccessible. Attempts to access volatile firmware controls whilst in this state would produce errors in the kernel log due to a regmap_raw_read() into DSP registers whilst the regmap is in cache_only. To remove this error log, add a hibernating flag to indicate that the controls are inaccessible, so we no longer try to read or write to the registers whilst the regmap is in cache_only. This would still produce an error when trying to read or write to these controls, but this would be a different error (-EPERM instead of -EBUSY), and would not produce a spurious error log in the kernel. Upon wake from hibernation, the control caches are re-synced to the hardware, if the DSP is running. Signed-off-by: Stefan Binding Link: https://patch.msgid.link/20260224161821.93365-2-sbinding@opensource.cirrus.com Reviewed-by: Richard Fitzgerald Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 49 +++++++++++++++++++++++++++++++--- include/linux/firmware/cirrus/cs_dsp.h | 3 +++ 2 files changed, 48 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index b4f1c01e3b5b..f9d8a883900d 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -515,6 +515,7 @@ void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root) debugfs_create_bool("booted", 0444, root, &dsp->booted); debugfs_create_bool("running", 0444, root, &dsp->running); + debugfs_create_bool("hibernating", 0444, root, &dsp->hibernating); debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id); debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version); @@ -703,7 +704,7 @@ int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int lockdep_assert_held(&dsp->pwr_lock); - if (!dsp->running) + if (!dsp->running || dsp->hibernating) return -EPERM; ret = cs_dsp_coeff_base_reg(ctl, ®, 0); @@ -827,7 +828,7 @@ int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, } ctl->set = 1; - if (ctl->enabled && ctl->dsp->running) + if (ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating) ret = cs_dsp_coeff_write_ctrl_raw(ctl, off, buf, len); if (ret < 0) @@ -920,12 +921,12 @@ int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, return -EINVAL; if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { - if (ctl->enabled && ctl->dsp->running) + if (ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating) return cs_dsp_coeff_read_ctrl_raw(ctl, off, buf, len); else return -EPERM; } else { - if (!ctl->flags && ctl->enabled && ctl->dsp->running) + if (!ctl->flags && ctl->enabled && ctl->dsp->running && !ctl->dsp->hibernating) ret = cs_dsp_coeff_read_ctrl_raw(ctl, 0, ctl->cache, ctl->len); if (buf != ctl->cache) @@ -1108,6 +1109,44 @@ err_ctl: return ret; } + +/** + * cs_dsp_hibernate() - Disable or enable all controls for a DSP + * @dsp: pointer to DSP structure + * @hibernate: whether to set controls to cache only mode + * + * When @hibernate is true, the DSP is entering hibernation mode where the + * regmap is inaccessible, and all controls become cache only. + * When @hibernate is false, the DSP has exited hibernation mode. If the DSP + * is running, all controls are re-synced to the DSP. + * + */ +void cs_dsp_hibernate(struct cs_dsp *dsp, bool hibernate) +{ + mutex_lock(&dsp->pwr_lock); + + if (!dsp->running) { + cs_dsp_dbg(dsp, "Cannot hibernate, DSP not running\n"); + goto out; + } + + if (dsp->hibernating == hibernate) + goto out; + + cs_dsp_dbg(dsp, "Set hibernating to %d\n", hibernate); + dsp->hibernating = hibernate; + + if (!dsp->hibernating && dsp->running) { + int ret = cs_dsp_coeff_sync_controls(dsp); + + if (ret) + cs_dsp_err(dsp, "Error syncing controls: %d\n", ret); + } +out: + mutex_unlock(&dsp->pwr_lock); +} +EXPORT_SYMBOL_NS_GPL(cs_dsp_hibernate, "FW_CS_DSP"); + struct cs_dsp_coeff_parsed_alg { int id; const u8 *name; @@ -2498,6 +2537,7 @@ int cs_dsp_adsp1_power_up(struct cs_dsp *dsp, goto err_ena; dsp->booted = true; + dsp->hibernating = false; /* Start the core running */ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, @@ -2776,6 +2816,7 @@ int cs_dsp_power_up(struct cs_dsp *dsp, dsp->ops->disable_core(dsp); dsp->booted = true; + dsp->hibernating = false; mutex_unlock(&dsp->pwr_lock); diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 0ec1cdc5585d..4e3baa557068 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -179,6 +179,7 @@ struct cs_dsp { bool booted; bool running; + bool hibernating; struct list_head ctl_list; @@ -354,4 +355,6 @@ int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val); int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch); int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits); +void cs_dsp_hibernate(struct cs_dsp *dsp, bool hibernating); + #endif -- cgit v1.2.3 From 98eb42c7de6b0185c914df4cca61b49ff76821ee Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 25 Feb 2026 21:50:02 +0800 Subject: ASoC: add snd_soc_lookup_component_by_name helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a helper to help user to get the component by name. Signed-off-by: Bard Liao Reviewed-by: Charles Keepax Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20260225135004.2322987-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 7d8376c8e1be..1a486153dc76 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -465,6 +465,7 @@ struct snd_soc_component *snd_soc_lookup_component_nolocked(struct device *dev, const char *driver_name); struct snd_soc_component *snd_soc_lookup_component(struct device *dev, const char *driver_name); +struct snd_soc_component *snd_soc_lookup_component_by_name(const char *component_name); int soc_new_pcm(struct snd_soc_pcm_runtime *rtd); #ifdef CONFIG_SND_SOC_COMPRESS diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d0fffef65daf..f41ca5109a6a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -404,6 +404,19 @@ struct snd_soc_component *snd_soc_lookup_component(struct device *dev, } EXPORT_SYMBOL_GPL(snd_soc_lookup_component); +struct snd_soc_component *snd_soc_lookup_component_by_name(const char *component_name) +{ + struct snd_soc_component *component; + + guard(mutex)(&client_mutex); + for_each_component(component) + if (strstr(component->name, component_name)) + return component; + + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_lookup_component_by_name); + struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) -- cgit v1.2.3 From a6fe20d67dc7d512f9b5dc11c5777fb1e1ff70ce Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Fri, 6 Mar 2026 15:28:10 +0000 Subject: mfd: cs42l43: Add support for the B variant Introducing CS42L43B codec, a variant of CS42L43 which can be driven by the same driver. Changes in CS42L43 driver specific for CS42L43B: - Decimator 1 and 2 are dedicated to ADC, can't be selected for PDM - Decimators 3 and 4 are connected to PDM1 - Added Decimator 5 and 6 for PDM2 - Supports SoundWire Clock Gearing - Updated ROM requiring no patching - Reduced RAM space - Each ISRC has 4 decimators now Signed-off-by: Maciej Strozek Acked-by: Lee Jones Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260306152829.3130530-4-mstrozek@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/mfd/cs42l43-i2c.c | 7 ++- drivers/mfd/cs42l43-sdw.c | 4 +- drivers/mfd/cs42l43.c | 93 ++++++++++++++++++++++++++++++++++------ drivers/mfd/cs42l43.h | 2 +- include/linux/mfd/cs42l43-regs.h | 76 ++++++++++++++++++++++++++++++++ include/linux/mfd/cs42l43.h | 1 + 6 files changed, 166 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/mfd/cs42l43-i2c.c b/drivers/mfd/cs42l43-i2c.c index a2ab001a600a..0a0ab5e549a5 100644 --- a/drivers/mfd/cs42l43-i2c.c +++ b/drivers/mfd/cs42l43-i2c.c @@ -47,6 +47,7 @@ static int cs42l43_i2c_probe(struct i2c_client *i2c) cs42l43->irq = i2c->irq; /* A device on an I2C is always attached by definition. */ cs42l43->attached = true; + cs42l43->variant_id = (long)device_get_match_data(cs42l43->dev); cs42l43->regmap = devm_regmap_init_i2c(i2c, &cs42l43_i2c_regmap); if (IS_ERR(cs42l43->regmap)) @@ -58,7 +59,8 @@ static int cs42l43_i2c_probe(struct i2c_client *i2c) #if IS_ENABLED(CONFIG_OF) static const struct of_device_id cs42l43_of_match[] = { - { .compatible = "cirrus,cs42l43", }, + { .compatible = "cirrus,cs42l43", .data = (void *)CS42L43_DEVID_VAL }, + { .compatible = "cirrus,cs42l43b", .data = (void *)CS42L43B_DEVID_VAL }, {} }; MODULE_DEVICE_TABLE(of, cs42l43_of_match); @@ -66,7 +68,8 @@ MODULE_DEVICE_TABLE(of, cs42l43_of_match); #if IS_ENABLED(CONFIG_ACPI) static const struct acpi_device_id cs42l43_acpi_match[] = { - { "CSC4243", 0 }, + { "CSC4243", CS42L43_DEVID_VAL }, + { "CSC2A3B", CS42L43B_DEVID_VAL }, {} }; MODULE_DEVICE_TABLE(acpi, cs42l43_acpi_match); diff --git a/drivers/mfd/cs42l43-sdw.c b/drivers/mfd/cs42l43-sdw.c index 023f7e1a30f8..794c98378175 100644 --- a/drivers/mfd/cs42l43-sdw.c +++ b/drivers/mfd/cs42l43-sdw.c @@ -178,6 +178,7 @@ static int cs42l43_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id * cs42l43->dev = dev; cs42l43->sdw = sdw; + cs42l43->variant_id = (long)id->driver_data; cs42l43->regmap = devm_regmap_init_sdw(sdw, &cs42l43_sdw_regmap); if (IS_ERR(cs42l43->regmap)) @@ -188,7 +189,8 @@ static int cs42l43_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id * } static const struct sdw_device_id cs42l43_sdw_id[] = { - SDW_SLAVE_ENTRY(0x01FA, 0x4243, 0), + SDW_SLAVE_ENTRY(0x01FA, 0x4243, (void *) CS42L43_DEVID_VAL), + SDW_SLAVE_ENTRY(0x01FA, 0x2A3B, (void *) CS42L43B_DEVID_VAL), {} }; MODULE_DEVICE_TABLE(sdw, cs42l43_sdw_id); diff --git a/drivers/mfd/cs42l43.c b/drivers/mfd/cs42l43.c index 107cfb983fec..166881751e69 100644 --- a/drivers/mfd/cs42l43.c +++ b/drivers/mfd/cs42l43.c @@ -115,9 +115,14 @@ const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS] = { { CS42L43_DECIM_HPF_WNF_CTRL2, 0x00000001 }, { CS42L43_DECIM_HPF_WNF_CTRL3, 0x00000001 }, { CS42L43_DECIM_HPF_WNF_CTRL4, 0x00000001 }, + { CS42L43B_DECIM_HPF_WNF_CTRL5, 0x00000001 }, + { CS42L43B_DECIM_HPF_WNF_CTRL6, 0x00000001 }, { CS42L43_DMIC_PDM_CTRL, 0x00000000 }, { CS42L43_DECIM_VOL_CTRL_CH1_CH2, 0x20122012 }, { CS42L43_DECIM_VOL_CTRL_CH3_CH4, 0x20122012 }, + { CS42L43B_DECIM_VOL_CTRL_CH1_CH2, 0x20122012 }, + { CS42L43B_DECIM_VOL_CTRL_CH3_CH4, 0x20122012 }, + { CS42L43B_DECIM_VOL_CTRL_CH5_CH6, 0x20122012 }, { CS42L43_INTP_VOLUME_CTRL1, 0x00000180 }, { CS42L43_INTP_VOLUME_CTRL2, 0x00000180 }, { CS42L43_AMP1_2_VOL_RAMP, 0x00000022 }, @@ -155,8 +160,12 @@ const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS] = { { CS42L43_SWIRE_DP2_CH2_INPUT, 0x00000000 }, { CS42L43_SWIRE_DP3_CH1_INPUT, 0x00000000 }, { CS42L43_SWIRE_DP3_CH2_INPUT, 0x00000000 }, + { CS42L43B_SWIRE_DP3_CH3_INPUT, 0x00000000 }, + { CS42L43B_SWIRE_DP3_CH4_INPUT, 0x00000000 }, { CS42L43_SWIRE_DP4_CH1_INPUT, 0x00000000 }, { CS42L43_SWIRE_DP4_CH2_INPUT, 0x00000000 }, + { CS42L43B_SWIRE_DP4_CH3_INPUT, 0x00000000 }, + { CS42L43B_SWIRE_DP4_CH4_INPUT, 0x00000000 }, { CS42L43_ASRC_INT1_INPUT1, 0x00000000 }, { CS42L43_ASRC_INT2_INPUT1, 0x00000000 }, { CS42L43_ASRC_INT3_INPUT1, 0x00000000 }, @@ -169,10 +178,14 @@ const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS] = { { CS42L43_ISRC1INT2_INPUT1, 0x00000000 }, { CS42L43_ISRC1DEC1_INPUT1, 0x00000000 }, { CS42L43_ISRC1DEC2_INPUT1, 0x00000000 }, + { CS42L43B_ISRC1DEC3_INPUT1, 0x00000000 }, + { CS42L43B_ISRC1DEC4_INPUT1, 0x00000000 }, { CS42L43_ISRC2INT1_INPUT1, 0x00000000 }, { CS42L43_ISRC2INT2_INPUT1, 0x00000000 }, { CS42L43_ISRC2DEC1_INPUT1, 0x00000000 }, { CS42L43_ISRC2DEC2_INPUT1, 0x00000000 }, + { CS42L43B_ISRC2DEC3_INPUT1, 0x00000000 }, + { CS42L43B_ISRC2DEC4_INPUT1, 0x00000000 }, { CS42L43_EQ1MIX_INPUT1, 0x00800000 }, { CS42L43_EQ1MIX_INPUT2, 0x00800000 }, { CS42L43_EQ1MIX_INPUT3, 0x00800000 }, @@ -269,6 +282,8 @@ EXPORT_SYMBOL_NS_GPL(cs42l43_reg_default, "MFD_CS42L43"); bool cs42l43_readable_register(struct device *dev, unsigned int reg) { + struct cs42l43 *cs42l43 = dev_get_drvdata(dev); + switch (reg) { case CS42L43_DEVID: case CS42L43_REVID: @@ -292,7 +307,6 @@ bool cs42l43_readable_register(struct device *dev, unsigned int reg) case CS42L43_ADC_B_CTRL1 ... CS42L43_ADC_B_CTRL2: case CS42L43_DECIM_HPF_WNF_CTRL1 ... CS42L43_DECIM_HPF_WNF_CTRL4: case CS42L43_DMIC_PDM_CTRL: - case CS42L43_DECIM_VOL_CTRL_CH1_CH2 ... CS42L43_DECIM_VOL_CTRL_CH3_CH4: case CS42L43_INTP_VOLUME_CTRL1 ... CS42L43_INTP_VOLUME_CTRL2: case CS42L43_AMP1_2_VOL_RAMP: case CS42L43_ASP_CTRL: @@ -387,8 +401,16 @@ bool cs42l43_readable_register(struct device *dev, unsigned int reg) case CS42L43_BOOT_CONTROL: case CS42L43_BLOCK_EN: case CS42L43_SHUTTER_CONTROL: - case CS42L43_MCU_SW_REV ... CS42L43_MCU_RAM_MAX: - return true; + case CS42L43B_MCU_SW_REV ... CS42L43B_MCU_RAM_MAX: + return true; // registers present on all variants + case CS42L43_MCU_SW_REV ... CS42L43B_MCU_SW_REV - 1: + case CS42L43B_MCU_RAM_MAX + 1 ... CS42L43_MCU_RAM_MAX: + case CS42L43_DECIM_VOL_CTRL_CH1_CH2 ... CS42L43_DECIM_VOL_CTRL_CH3_CH4: + return cs42l43->variant_id == CS42L43_DEVID_VAL; // regs only in CS42L43 variant + case CS42L43B_DECIM_VOL_CTRL_CH1_CH2 ... CS42L43B_DECIM_HPF_WNF_CTRL6: + case CS42L43B_SWIRE_DP3_CH3_INPUT ... CS42L43B_SWIRE_DP4_CH4_INPUT: + case CS42L43B_ISRC1DEC3_INPUT1 ... CS42L43B_ISRC2DEC4_INPUT1: + return cs42l43->variant_id == CS42L43B_DEVID_VAL; // regs only in CS42L43B variant default: return false; } @@ -597,15 +619,27 @@ static int cs42l43_wait_for_attach(struct cs42l43 *cs42l43) static int cs42l43_mcu_stage_2_3(struct cs42l43 *cs42l43, bool shadow) { unsigned int need_reg = CS42L43_NEED_CONFIGS; + unsigned int boot_reg; unsigned int val; int ret; - if (shadow) - need_reg = CS42L43_FW_SH_BOOT_CFG_NEED_CONFIGS; + switch (cs42l43->variant_id) { + case CS42L43_DEVID_VAL: + if (shadow) + need_reg = CS42L43_FW_SH_BOOT_CFG_NEED_CONFIGS; + boot_reg = CS42L43_BOOT_STATUS; + break; + case CS42L43B_DEVID_VAL: + need_reg = CS42L43B_NEED_CONFIGS; + boot_reg = CS42L43B_BOOT_STATUS; + break; + default: + return -EINVAL; + } regmap_write(cs42l43->regmap, need_reg, 0); - ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_BOOT_STATUS, + ret = regmap_read_poll_timeout(cs42l43->regmap, boot_reg, val, (val == CS42L43_MCU_BOOT_STAGE3), CS42L43_MCU_POLL_US, CS42L43_MCU_CMD_TIMEOUT_US); if (ret) { @@ -644,13 +678,25 @@ static int cs42l43_mcu_stage_3_2(struct cs42l43 *cs42l43) */ static int cs42l43_mcu_disable(struct cs42l43 *cs42l43) { - unsigned int val; + unsigned int val, cfg_reg, ctrl_reg; int ret; - regmap_write(cs42l43->regmap, CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_REG, - CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_DISABLE_VAL); - regmap_write(cs42l43->regmap, CS42L43_FW_MISSION_CTRL_MM_CTRL_SELECTION, - CS42L43_FW_MM_CTRL_MCU_SEL_MASK); + switch (cs42l43->variant_id) { + case CS42L43_DEVID_VAL: + cfg_reg = CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_REG; + ctrl_reg = CS42L43_FW_MISSION_CTRL_MM_CTRL_SELECTION; + break; + case CS42L43B_DEVID_VAL: + cfg_reg = CS42L43B_FW_MISSION_CTRL_MM_MCU_CFG_REG; + ctrl_reg = CS42L43B_FW_MISSION_CTRL_MM_CTRL_SELECTION; + break; + default: + return -EINVAL; + } + + regmap_write(cs42l43->regmap, cfg_reg, CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_DISABLE_VAL); + regmap_write(cs42l43->regmap, ctrl_reg, CS42L43_FW_MM_CTRL_MCU_SEL_MASK); + regmap_write(cs42l43->regmap, CS42L43_MCU_SW_INTERRUPT, CS42L43_CONTROL_IND_MASK); regmap_write(cs42l43->regmap, CS42L43_MCU_SW_INTERRUPT, 0); @@ -740,18 +786,32 @@ static int cs42l43_mcu_update_step(struct cs42l43 *cs42l43) { unsigned int mcu_rev, bios_rev, boot_status, secure_cfg; bool patched, shadow; + int boot_status_reg, mcu_sw_rev_reg; int ret; + switch (cs42l43->variant_id) { + case CS42L43_DEVID_VAL: + boot_status_reg = CS42L43_BOOT_STATUS; + mcu_sw_rev_reg = CS42L43_MCU_SW_REV; + break; + case CS42L43B_DEVID_VAL: + boot_status_reg = CS42L43B_BOOT_STATUS; + mcu_sw_rev_reg = CS42L43B_MCU_SW_REV; + break; + default: + return -EINVAL; + } + /* Clear any stale software interrupt bits. */ regmap_read(cs42l43->regmap, CS42L43_SOFT_INT, &mcu_rev); - ret = regmap_read(cs42l43->regmap, CS42L43_BOOT_STATUS, &boot_status); + ret = regmap_read(cs42l43->regmap, boot_status_reg, &boot_status); if (ret) { dev_err(cs42l43->dev, "Failed to read boot status: %d\n", ret); return ret; } - ret = regmap_read(cs42l43->regmap, CS42L43_MCU_SW_REV, &mcu_rev); + ret = regmap_read(cs42l43->regmap, mcu_sw_rev_reg, &mcu_rev); if (ret) { dev_err(cs42l43->dev, "Failed to read firmware revision: %d\n", ret); return ret; @@ -918,6 +978,13 @@ static void cs42l43_boot_work(struct work_struct *work) switch (devid) { case CS42L43_DEVID_VAL: + case CS42L43B_DEVID_VAL: + if (devid != cs42l43->variant_id) { + dev_err(cs42l43->dev, + "Device ID (0x%06x) does not match variant ID (0x%06lx)\n", + devid, cs42l43->variant_id); + goto err; + } break; default: dev_err(cs42l43->dev, "Unrecognised devid: 0x%06x\n", devid); diff --git a/drivers/mfd/cs42l43.h b/drivers/mfd/cs42l43.h index f3da783930f5..a0068f6572e2 100644 --- a/drivers/mfd/cs42l43.h +++ b/drivers/mfd/cs42l43.h @@ -9,7 +9,7 @@ #ifndef CS42L43_CORE_INT_H #define CS42L43_CORE_INT_H -#define CS42L43_N_DEFAULTS 176 +#define CS42L43_N_DEFAULTS 189 struct dev_pm_ops; struct device; diff --git a/include/linux/mfd/cs42l43-regs.h b/include/linux/mfd/cs42l43-regs.h index c39a49269cb7..68831f113589 100644 --- a/include/linux/mfd/cs42l43-regs.h +++ b/include/linux/mfd/cs42l43-regs.h @@ -1181,4 +1181,80 @@ /* CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_REG */ #define CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_DISABLE_VAL 0xF05AA50F +/* CS42L43B VARIANT REGISTERS */ +#define CS42L43B_DEVID_VAL 0x0042A43B + +#define CS42L43B_DECIM_VOL_CTRL_CH1_CH2 0x00008280 +#define CS42L43B_DECIM_VOL_CTRL_CH3_CH4 0x00008284 + +#define CS42L43B_DECIM_VOL_CTRL_CH5_CH6 0x00008290 +#define CS42L43B_DECIM_VOL_CTRL_UPDATE 0x0000829C + +#define CS42L43B_DECIM_HPF_WNF_CTRL5 0x000082A0 +#define CS42L43B_DECIM_HPF_WNF_CTRL6 0x000082A4 + +#define CS42L43B_SWIRE_DP3_CH3_INPUT 0x0000C320 +#define CS42L43B_SWIRE_DP3_CH4_INPUT 0x0000C330 +#define CS42L43B_SWIRE_DP4_CH3_INPUT 0x0000C340 +#define CS42L43B_SWIRE_DP4_CH4_INPUT 0x0000C350 + +#define CS42L43B_ISRC1DEC3_INPUT1 0x0000C780 +#define CS42L43B_ISRC1DEC4_INPUT1 0x0000C790 +#define CS42L43B_ISRC2DEC3_INPUT1 0x0000C7A0 +#define CS42L43B_ISRC2DEC4_INPUT1 0x0000C7B0 + +#define CS42L43B_FW_MISSION_CTRL_NEED_CONFIGS 0x00117E00 +#define CS42L43B_FW_MISSION_CTRL_HAVE_CONFIGS 0x00117E04 +#define CS42L43B_FW_MISSION_CTRL_PATCH_START_ADDR_REG 0x00117E08 +#define CS42L43B_FW_MISSION_CTRL_MM_CTRL_SELECTION 0x00117E0C +#define CS42L43B_FW_MISSION_CTRL_MM_MCU_CFG_REG 0x00117E10 + +#define CS42L43B_MCU_SW_REV 0x00117314 +#define CS42L43B_PATCH_START_ADDR 0x00117318 +#define CS42L43B_CONFIG_SELECTION 0x0011731C +#define CS42L43B_NEED_CONFIGS 0x00117320 +#define CS42L43B_BOOT_STATUS 0x00117330 + +#define CS42L43B_FW_MISSION_CTRL_NEED_CONFIGS 0x00117E00 +#define CS42L43B_FW_MISSION_CTRL_HAVE_CONFIGS 0x00117E04 +#define CS42L43B_FW_MISSION_CTRL_PATCH_START_ADDR_REG 0x00117E08 +#define CS42L43B_FW_MISSION_CTRL_MM_CTRL_SELECTION 0x00117E0C +#define CS42L43B_FW_MISSION_CTRL_MM_MCU_CFG_REG 0x00117E10 + +#define CS42L43B_MCU_RAM_MAX 0x00117FFF + +/* CS42L43B_DECIM_DECIM_VOL_CTRL_CH5_CH6 */ +#define CS42L43B_DECIM6_MUTE_MASK 0x80000000 +#define CS42L43B_DECIM6_MUTE_SHIFT 31 +#define CS42L43B_DECIM6_VOL_MASK 0x3FC00000 +#define CS42L43B_DECIM6_VOL_SHIFT 22 +#define CS42L43B_DECIM6_PATH1_VOL_FALL_RATE_MASK 0x00380000 +#define CS42L43B_DECIM6_PATH1_VOL_FALL_RATE_SHIFT 19 +#define CS42L43B_DECIM6_PATH1_VOL_RISE_RATE_MASK 0x00070000 +#define CS42L43B_DECIM6_PATH1_VOL_RISE_RATE_SHIFT 16 +#define CS42L43B_DECIM5_MUTE_MASK 0x00008000 +#define CS42L43B_DECIM5_MUTE_SHIFT 15 +#define CS42L43B_DECIM5_VOL_MASK 0x00003FC0 +#define CS42L43B_DECIM5_VOL_SHIFT 6 +#define CS42L43B_DECIM5_PATH1_VOL_FALL_RATE_MASK 0x00000038 +#define CS42L43B_DECIM5_PATH1_VOL_FALL_RATE_SHIFT 3 +#define CS42L43B_DECIM5_PATH1_VOL_RISE_RATE_MASK 0x00000007 +#define CS42L43B_DECIM5_PATH1_VOL_RISE_RATE_SHIFT 0 + +/* CS42L43B_DECIM_VOL_CTRL_UPDATE */ +#define CS42L43B_DECIM6_PATH1_VOL_TRIG_MASK 0x00000800 +#define CS42L43B_DECIM6_PATH1_VOL_TRIG_SHIFT 11 +#define CS42L43B_DECIM5_PATH1_VOL_TRIG_MASK 0x00000100 +#define CS42L43B_DECIM5_PATH1_VOL_TRIG_SHIFT 8 +#define CS42L43B_DECIM4_VOL_UPDATE_MASK 0x00000020 +#define CS42L43B_DECIM4_VOL_UPDATE_SHIFT 5 + +/* CS42L43_ISRC1_CTRL..CS42L43_ISRC2_CTRL */ +#define CS42L43B_ISRC_DEC4_EN_MASK 0x00000008 +#define CS42L43B_ISRC_DEC4_EN_SHIFT 3 +#define CS42L43B_ISRC_DEC4_EN_WIDTH 1 +#define CS42L43B_ISRC_DEC3_EN_MASK 0x00000004 +#define CS42L43B_ISRC_DEC3_EN_SHIFT 2 +#define CS42L43B_ISRC_DEC3_EN_WIDTH 1 + #endif /* CS42L43_CORE_REGS_H */ diff --git a/include/linux/mfd/cs42l43.h b/include/linux/mfd/cs42l43.h index 2239d8585e78..ff0f7e365a19 100644 --- a/include/linux/mfd/cs42l43.h +++ b/include/linux/mfd/cs42l43.h @@ -98,6 +98,7 @@ struct cs42l43 { bool sdw_pll_active; bool attached; bool hw_lock; + long variant_id; }; #endif /* CS42L43_CORE_EXT_H */ -- cgit v1.2.3 From 97af961568c8682c44506c9ad4b26c8a5455ec1d Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 9 Mar 2026 12:45:43 +0000 Subject: ASoC: cs35l56: Put OTP register defines in correct address order Move the defines for the OTP registers to keep the register defines in order of increasing address. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260309124543.1135247-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index ae1e1489b671..51b153bb8598 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -32,9 +32,6 @@ struct snd_ctl_elem_value; #define CS35L56_UPDATE_REGS 0x0002A0C #define CS35L56_REFCLK_INPUT 0x0002C04 #define CS35L56_GLOBAL_SAMPLE_RATE 0x0002C0C -#define CS35L56_OTP_MEM_53 0x00300D4 -#define CS35L56_OTP_MEM_54 0x00300D8 -#define CS35L56_OTP_MEM_55 0x00300DC #define CS35L56_ASP1_ENABLES1 0x0004800 #define CS35L56_ASP1_CONTROL1 0x0004804 #define CS35L56_ASP1_CONTROL2 0x0004808 @@ -86,6 +83,9 @@ struct snd_ctl_elem_value; #define CS35L56_DIE_STS1 0x0017040 #define CS35L56_DIE_STS2 0x0017044 #define CS35L56_DSP_RESTRICT_STS1 0x00190F0 +#define CS35L56_OTP_MEM_53 0x00300D4 +#define CS35L56_OTP_MEM_54 0x00300D8 +#define CS35L56_OTP_MEM_55 0x00300DC #define CS35L56_DSP1_XMEM_PACKED_0 0x2000000 #define CS35L56_DSP1_XMEM_PACKED_6143 0x2005FFC #define CS35L56_DSP1_XMEM_UNPACKED32_0 0x2400000 -- cgit v1.2.3 From 7d8632f1ef6c8ed0b53771c16f130f18d636931e Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 1 Mar 2026 18:05:23 +1000 Subject: ASoC: soc-dai: define possible idle TDM slot modes Some audio devices, such as certain Texas Instruments codecs, include configurable bus keepers. We currently don't have a standardised way to configure such hardware, and instead rely on the hardware initialising setting itself up into a sane state. There are situations where this is insufficient, however, and some platforms require more concrete guarantees as to the state of the bus, and being able to explicitly configure bus keepers enables this. For example, some Apple Silicon machines have an odd bus topology where the SDOUT pins of all codecs are split across two data lines, which are summed via an OR gate in front of the receiving port on the SoC's I2S peripheral. Each line must transmit 0 while a codec on the other line is actively transmitting data, or the SoC will receive garbage data. To do this, one codec on each line must be configured to transmit zeroes during the other line's active TDM slots. Thus, we define seven possible bus-keeping modes that a device can be in: NONE (UB/as initialised), OFF (explicitly disabled), ZERO (actively transmit a 0), PULLDOWN, HIZ (floating), PULLUP, and DRIVE_HIGH. These will be consumed by CODEC/CPU drivers via a common DAI op, enabling the explicit configuration of bus keepers where required. Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260301-tdm-idle-slots-v3-4-c6ac5351489a@gmail.com Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 224396927aef..44dd06add52e 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -52,6 +52,21 @@ struct snd_compr_stream; #define SND_SOC_POSSIBLE_DAIFMT_AC97 (1 << SND_SOC_DAI_FORMAT_AC97) #define SND_SOC_POSSIBLE_DAIFMT_PDM (1 << SND_SOC_DAI_FORMAT_PDM) +/* + * DAI TDM slot idle modes + * + * Describes a CODEC/CPU's behaviour when not actively receiving or + * transmitting on a given TDM slot. NONE is undefined behaviour. + * Add new modes to the end. + */ +#define SND_SOC_DAI_TDM_IDLE_NONE 0 +#define SND_SOC_DAI_TDM_IDLE_OFF 1 +#define SND_SOC_DAI_TDM_IDLE_ZERO 2 +#define SND_SOC_DAI_TDM_IDLE_PULLDOWN 3 +#define SND_SOC_DAI_TDM_IDLE_HIZ 4 +#define SND_SOC_DAI_TDM_IDLE_PULLUP 5 +#define SND_SOC_DAI_TDM_IDLE_DRIVE_HIGH 6 + /* * DAI Clock gating. * -- cgit v1.2.3 From b758d3574e88537f9089bd757a51b35cf9675179 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 1 Mar 2026 18:05:24 +1000 Subject: ASoC: soc-dai: add common operation to set TDM idle mode Some audio devices, like certain Texas Instruments codecs, integrate configurable bus keepers that dictate the codec's behaviour during idle TDM slots. Now that we have definitions for various idle modes, add a snd_soc_dai_set_tdm_idle() operation to control this in a standardised way. This is useful on Apple Silicon laptops, where a single I2S bus is comprised of two physical lines which are ORed just before the receiving port. When a codec on one line is transmitting, we must guarantee that the other line is low. We can achieve this by configuring one codec on each line to use its bus keeper to fill its line with zeroes during the active slots of the other line. Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260301-tdm-idle-slots-v3-5-c6ac5351489a@gmail.com Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 7 +++++++ sound/soc/soc-dai.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 44dd06add52e..6a42812bba8c 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -196,6 +196,10 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); +int snd_soc_dai_set_tdm_idle(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int tx_mode, int rx_mode); + int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, const unsigned int *tx_slot, unsigned int rx_num, const unsigned int *rx_slot); @@ -312,6 +316,9 @@ struct snd_soc_dai_ops { int (*set_tdm_slot)(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); + int (*set_tdm_idle)(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int tx_mode, int rx_mode); int (*set_channel_map)(struct snd_soc_dai *dai, unsigned int tx_num, const unsigned int *tx_slot, unsigned int rx_num, const unsigned int *rx_slot); diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index a1e05307067d..2f370fda1266 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -282,6 +282,46 @@ err: } EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); +/** + * snd_soc_dai_set_tdm_idle() - Configure a DAI's TDM idle mode + * @dai: The DAI to configure + * @tx_mask: bitmask representing idle TX slots. + * @rx_mask: bitmask representing idle RX slots. + * @tx_mode: idle mode to set for TX slots. + * @rx_mode: idle mode to set for RX slots. + * + * This function configures the DAI to handle idle TDM slots in the + * specified manner. @tx_mode and @rx_mode can be one of + * SND_SOC_DAI_TDM_IDLE_NONE, SND_SOC_DAI_TDM_IDLE_ZERO, + * SND_SOC_DAI_TDM_IDLE_PULLDOWN, or SND_SOC_DAI_TDM_IDLE_HIZ. + * SND_SOC_TDM_IDLE_NONE represents the DAI's default/unset idle slot + * handling state and could be any of the other modes depending on the + * hardware behind the DAI. It is therefore undefined behaviour when set + * explicitly. + * + * Mode and mask can be set independently for both the TX and RX direction. + * Some hardware may ignore both TX and RX masks depending on its + * capabilities. + */ +int snd_soc_dai_set_tdm_idle(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int tx_mode, int rx_mode) +{ + int ret = -EOPNOTSUPP; + + /* You can't write to the RX line */ + if (rx_mode == SND_SOC_DAI_TDM_IDLE_ZERO) + return soc_dai_ret(dai, -EINVAL); + + if (dai->driver->ops && + dai->driver->ops->set_tdm_idle) + ret = dai->driver->ops->set_tdm_idle(dai, tx_mask, rx_mask, + tx_mode, rx_mode); + + return soc_dai_ret(dai, ret); +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_idle); + /** * snd_soc_dai_set_channel_map - configure DAI audio channel map * @dai: DAI -- cgit v1.2.3 From cb15d8e6cbe8d085ac585016deb2e1e0107b99e5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 14 Mar 2026 23:56:49 +0100 Subject: ASoC: codec: arizona: Convert to use GPIO descriptors This converts the Arizona driver to use GPIO descriptors exclusively, deletes the legacy code path an updates the in-tree user of legacy GPIO. The GPIO lines for mic detect polarity and headphone ID detection are made exclusively descriptor-oriented. The headphone ID detection could actually only be used by the legacy GPIO code, but I converted it to use a descriptor if someone would actually need it so we don't just drop useful code. The compatible "wlf,hpdet-id-gpio" is not in the device tree bindings and only intended to be used by software nodes if any. If someone insists I can try to add a binding for it, but I doubt there is any real user so it seems pointless. Signed-off-by: Linus Walleij Reviewed-by: Charles Keepax Reviewed-by: Bartosz Golaszewski Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260314-asoc-arizona-v1-1-ecc9a165307c@kernel.org Signed-off-by: Mark Brown --- arch/arm/mach-s3c/mach-crag6410-module.c | 6 +- include/linux/mfd/arizona/pdata.h | 10 ---- sound/soc/codecs/arizona-jack.c | 95 ++++++++++---------------------- sound/soc/codecs/arizona.h | 1 + 4 files changed, 34 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-s3c/mach-crag6410-module.c b/arch/arm/mach-s3c/mach-crag6410-module.c index 4ffcf024b09d..14b0f9cc103e 100644 --- a/arch/arm/mach-s3c/mach-crag6410-module.c +++ b/arch/arm/mach-s3c/mach-crag6410-module.c @@ -239,7 +239,6 @@ static struct gpiod_lookup_table wm8994_gpiod_table = { static struct arizona_pdata wm5102_reva_pdata = { .gpio_base = CODEC_GPIO_BASE, .irq_flags = IRQF_TRIGGER_HIGH, - .micd_pol_gpio = CODEC_GPIO_BASE + 4, .micd_rate = 6, .gpio_defaults = { [2] = 0x10000, /* AIF3TXLRCLK */ @@ -265,6 +264,8 @@ static struct gpiod_lookup_table wm5102_reva_gpiod_table = { .table = { GPIO_LOOKUP("GPION", 7, "wlf,ldoena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("arizona", 4, + "wlf,micd-pol", GPIO_ACTIVE_HIGH), { }, }, }; @@ -272,7 +273,6 @@ static struct gpiod_lookup_table wm5102_reva_gpiod_table = { static struct arizona_pdata wm5102_pdata = { .gpio_base = CODEC_GPIO_BASE, .irq_flags = IRQF_TRIGGER_HIGH, - .micd_pol_gpio = CODEC_GPIO_BASE + 2, .gpio_defaults = { [2] = 0x10000, /* AIF3TXLRCLK */ [3] = 0x4, /* OPCLK */ @@ -297,6 +297,8 @@ static struct gpiod_lookup_table wm5102_gpiod_table = { .table = { GPIO_LOOKUP("GPION", 7, "wlf,ldo1ena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("arizona", 2, + "wlf,micd-pol", GPIO_ACTIVE_HIGH), { }, }, }; diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index f72e6d4b14a7..d465dcd8c90a 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -117,11 +117,6 @@ struct arizona_pdata { /** Check for line output with HPDET method */ bool hpdet_acc_id_line; -#ifdef CONFIG_GPIOLIB_LEGACY - /** GPIO used for mic isolation with HPDET */ - int hpdet_id_gpio; -#endif - /** Channel to use for headphone detection */ unsigned int hpdet_channel; @@ -131,11 +126,6 @@ struct arizona_pdata { /** Extra debounce timeout used during initial mic detection (ms) */ unsigned int micd_detect_debounce; -#ifdef CONFIG_GPIOLIB_LEGACY - /** GPIO for mic detection polarity */ - int micd_pol_gpio; -#endif - /** Mic detect ramp rate */ unsigned int micd_bias_start_time; diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c index 303c1d44ebd8..a9063bac2752 100644 --- a/sound/soc/codecs/arizona-jack.c +++ b/sound/soc/codecs/arizona-jack.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -459,11 +458,6 @@ static int arizona_hpdet_do_id(struct arizona_priv *info, int *reading, bool *mic) { struct arizona *arizona = info->arizona; -#ifdef CONFIG_GPIOLIB_LEGACY - int id_gpio = arizona->pdata.hpdet_id_gpio; -#else - int id_gpio = 0; -#endif if (!arizona->pdata.hpdet_acc_id) return 0; @@ -474,9 +468,8 @@ static int arizona_hpdet_do_id(struct arizona_priv *info, int *reading, */ info->hpdet_res[info->num_hpdet_res++] = *reading; -#ifdef CONFIG_GPIOLIB_LEGACY /* Only check the mic directly if we didn't already ID it */ - if (id_gpio && info->num_hpdet_res == 1) { + if (info->hpdet_id_gpio && info->num_hpdet_res == 1) { dev_dbg(arizona->dev, "Measuring mic\n"); regmap_update_bits(arizona->regmap, @@ -486,13 +479,12 @@ static int arizona_hpdet_do_id(struct arizona_priv *info, int *reading, ARIZONA_ACCDET_MODE_HPR | info->micd_modes[0].src); - gpio_set_value_cansleep(id_gpio, 1); + gpiod_set_value_cansleep(info->hpdet_id_gpio, 1); regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, ARIZONA_HP_POLL, ARIZONA_HP_POLL); return -EAGAIN; } -#endif /* OK, got both. Now, compare... */ dev_dbg(arizona->dev, "HPDET measured %d %d\n", @@ -514,7 +506,7 @@ static int arizona_hpdet_do_id(struct arizona_priv *info, int *reading, /* * If we measure the mic as high impedance */ - if (!id_gpio || info->hpdet_res[1] > 50) { + if (!info->hpdet_id_gpio || info->hpdet_res[1] > 50) { dev_dbg(arizona->dev, "Detected mic\n"); *mic = true; info->detecting = true; @@ -533,9 +525,6 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) { struct arizona_priv *info = data; struct arizona *arizona = info->arizona; -#ifdef CONFIG_GPIOLIB_LEGACY - int id_gpio = arizona->pdata.hpdet_id_gpio; -#endif int ret, reading, state, report; bool mic = false; @@ -591,10 +580,8 @@ done: arizona_extcon_hp_clamp(info, false); -#ifdef CONFIG_GPIOLIB_LEGACY - if (id_gpio) - gpio_set_value_cansleep(id_gpio, 0); -#endif + if (info->hpdet_id_gpio) + gpiod_set_value_cansleep(info->hpdet_id_gpio, 0); /* If we have a mic then reenable MICDET */ if (state && (mic || info->mic)) @@ -1325,58 +1312,33 @@ int arizona_jack_codec_dev_probe(struct arizona_priv *info, struct device *dev) regmap_update_bits(arizona->regmap, ARIZONA_GP_SWITCH_1, ARIZONA_SW1_MODE_MASK, arizona->pdata.gpsw); -#ifdef CONFIG_GPIOLIB_LEGACY - if (pdata->micd_pol_gpio > 0) { - if (info->micd_modes[0].gpio) - mode = GPIOF_OUT_INIT_HIGH; - else - mode = GPIOF_OUT_INIT_LOW; - - ret = devm_gpio_request_one(dev, pdata->micd_pol_gpio, - mode, "MICD polarity"); - if (ret != 0) { - dev_err(arizona->dev, "Failed to request GPIO%d: %d\n", - pdata->micd_pol_gpio, ret); - return ret; - } - - info->micd_pol_gpio = gpio_to_desc(pdata->micd_pol_gpio); - } else -#endif - { - if (info->micd_modes[0].gpio) - mode = GPIOD_OUT_HIGH; - else - mode = GPIOD_OUT_LOW; + if (info->micd_modes[0].gpio) + mode = GPIOD_OUT_HIGH; + else + mode = GPIOD_OUT_LOW; - /* We can't use devm here because we need to do the get - * against the MFD device, as that is where the of_node - * will reside, but if we devm against that the GPIO - * will not be freed if the extcon driver is unloaded. - */ - info->micd_pol_gpio = gpiod_get_optional(arizona->dev, - "wlf,micd-pol", - mode); - if (IS_ERR(info->micd_pol_gpio)) { - ret = PTR_ERR(info->micd_pol_gpio); - dev_err_probe(arizona->dev, ret, "getting microphone polarity GPIO\n"); - return ret; - } + /* We can't use devm here because we need to do the get + * against the MFD device, as that is where the of_node + * will reside, but if we devm against that the GPIO + * will not be freed if the extcon driver is unloaded. + */ + info->micd_pol_gpio = gpiod_get_optional(arizona->dev, + "wlf,micd-pol", + mode); + if (IS_ERR(info->micd_pol_gpio)) { + ret = PTR_ERR(info->micd_pol_gpio); + dev_err_probe(arizona->dev, ret, "getting microphone polarity GPIO\n"); + return ret; } -#ifdef CONFIG_GPIOLIB_LEGACY - if (arizona->pdata.hpdet_id_gpio > 0) { - ret = devm_gpio_request_one(dev, arizona->pdata.hpdet_id_gpio, - GPIOF_OUT_INIT_LOW, - "HPDET"); - if (ret != 0) { - dev_err(arizona->dev, "Failed to request GPIO%d: %d\n", - arizona->pdata.hpdet_id_gpio, ret); - gpiod_put(info->micd_pol_gpio); - return ret; - } + info->hpdet_id_gpio = gpiod_get_optional(arizona->dev, + "wlf,hpdet-id-gpio", + mode); + if (IS_ERR(info->hpdet_id_gpio)) { + ret = PTR_ERR(info->hpdet_id_gpio); + dev_err_probe(arizona->dev, ret, "getting headphone detect ID GPIO\n"); + return ret; } -#endif return 0; } @@ -1385,6 +1347,7 @@ EXPORT_SYMBOL_GPL(arizona_jack_codec_dev_probe); int arizona_jack_codec_dev_remove(struct arizona_priv *info) { gpiod_put(info->micd_pol_gpio); + gpiod_put(info->hpdet_id_gpio); return 0; } EXPORT_SYMBOL_GPL(arizona_jack_codec_dev_remove); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index ecd8890eefc1..0703182d87b3 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -100,6 +100,7 @@ struct arizona_priv { struct snd_soc_jack *jack; struct regulator *micvdd; struct gpio_desc *micd_pol_gpio; + struct gpio_desc *hpdet_id_gpio; u16 last_jackdet; -- cgit v1.2.3 From 68130eef1e0d3c1770952e738f7f8d9f340bd42d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 16 Mar 2026 02:24:43 +0000 Subject: ASoC: soc-component: re-add pcm_new()/pcm_free() Because old pcm_new()/pcm_free() didn't care about parameter component, to avoid name collisions, we have added pcm_construct()/pcm_destruct() by commit c64bfc9066007 ("ASoC: soc-core: add new pcm_construct/pcm_destruct") Because all driver switch to new pcm_construct()/pcm_destruct(), old pcm_new()/pcm_free() were remoted by commit e9067bb502787 ("ASoC: soc-component: remove snd_pcm_ops from component driver") But naming of pcm_construct()/pcm_destruct() are not goot. re-add pcm_new()/pcm_free(), and switch to use it, again. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87a4w8lde4.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 4 ++++ sound/soc/generic/audio-graph-card.c | 1 + sound/soc/soc-component.c | 10 +++++++++- 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 2a2b74b24a60..0435ba376369 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -90,6 +90,10 @@ struct snd_soc_component_driver { struct snd_soc_pcm_runtime *rtd); void (*pcm_destruct)(struct snd_soc_component *component, struct snd_pcm *pcm); + int (*pcm_new)(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd); + void (*pcm_free)(struct snd_soc_component *component, + struct snd_pcm *pcm); /* component wide operations */ int (*set_sysclk)(struct snd_soc_component *component, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 8a5f41704739..74e8f2ab7ffc 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -77,6 +77,7 @@ static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc) struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc); if (dai && (dai->component->driver->pcm_construct || + dai->component->driver->pcm_new || (dai->driver->ops && dai->driver->ops->pcm_new))) return true; diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 89f236ab3034..77ad33383974 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -1042,6 +1042,11 @@ int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd) if (ret < 0) return soc_component_ret(component, ret); } + if (component->driver->pcm_new) { + ret = component->driver->pcm_new(component, rtd); + if (ret < 0) + return soc_component_ret(component, ret); + } } return 0; @@ -1055,9 +1060,12 @@ void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd) if (!rtd->pcm) return; - for_each_rtd_components(rtd, i, component) + for_each_rtd_components(rtd, i, component) { if (component->driver->pcm_destruct) component->driver->pcm_destruct(component, rtd->pcm); + if (component->driver->pcm_free) + component->driver->pcm_free(component, rtd->pcm); + } } int snd_soc_pcm_component_prepare(struct snd_pcm_substream *substream) -- cgit v1.2.3 From 175f733325ac2ce875cafd051980be2d2c06dec9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 16 Mar 2026 02:27:57 +0000 Subject: ASoC: soc-component: remove pcm_construct()/pcm_destruct() All driver have switched to use pcm_new()/pcm_free(), let's remove pcm_construct()/pcm_destruct(). Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/875x6wjyoa.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 4 ---- sound/soc/generic/audio-graph-card.c | 3 +-- sound/soc/soc-component.c | 10 +--------- 3 files changed, 2 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 0435ba376369..60f73c4b0bbb 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -86,10 +86,6 @@ struct snd_soc_component_driver { unsigned int reg, unsigned int val); /* pcm creation and destruction */ - int (*pcm_construct)(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd); - void (*pcm_destruct)(struct snd_soc_component *component, - struct snd_pcm *pcm); int (*pcm_new)(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd); void (*pcm_free)(struct snd_soc_component *component, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 74e8f2ab7ffc..18ce4ee06350 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -76,8 +76,7 @@ static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc) { struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc); - if (dai && (dai->component->driver->pcm_construct || - dai->component->driver->pcm_new || + if (dai && (dai->component->driver->pcm_new || (dai->driver->ops && dai->driver->ops->pcm_new))) return true; diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 77ad33383974..0f5e120d32b7 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -1037,11 +1037,6 @@ int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd) int i; for_each_rtd_components(rtd, i, component) { - if (component->driver->pcm_construct) { - ret = component->driver->pcm_construct(component, rtd); - if (ret < 0) - return soc_component_ret(component, ret); - } if (component->driver->pcm_new) { ret = component->driver->pcm_new(component, rtd); if (ret < 0) @@ -1060,12 +1055,9 @@ void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd) if (!rtd->pcm) return; - for_each_rtd_components(rtd, i, component) { - if (component->driver->pcm_destruct) - component->driver->pcm_destruct(component, rtd->pcm); + for_each_rtd_components(rtd, i, component) if (component->driver->pcm_free) component->driver->pcm_free(component, rtd->pcm); - } } int snd_soc_pcm_component_prepare(struct snd_pcm_substream *substream) -- cgit v1.2.3 From 032322b44c02f5e8a127d1dca6798f91cc72eb1d Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Mon, 16 Mar 2026 16:50:47 +0800 Subject: ALSA: pcm: oss: use proper stream lock for runtime->state access __snd_pcm_set_state() writes runtime->state under the PCM stream lock. However, the OSS I/O functions snd_pcm_oss_write3(), snd_pcm_oss_read3(), snd_pcm_oss_writev3() and snd_pcm_oss_readv3() read runtime->state without holding the stream lock, only holding oss.params_lock (a different mutex that does not synchronize with the stream lock). Since __snd_pcm_set_state() is called from IRQ context (e.g., snd_pcm_period_elapsed -> snd_pcm_update_state -> __snd_pcm_xrun -> snd_pcm_stop -> snd_pcm_post_stop) while the OSS read/write paths run in process context, these are concurrent accesses that constitute a data race. Rather than using READ_ONCE()/WRITE_ONCE() barriers, introduce a snd_pcm_get_state() helper that reads runtime->state under the stream lock, matching the locking discipline used elsewhere in the PCM layer. Also export snd_pcm_set_state() for completeness. Use snd_pcm_get_state() in all four OSS I/O functions, caching the result in a local variable where the same snapshot is used for multiple comparisons to avoid taking the lock repeatedly. Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260316085047.2876451-1-zzzccc427@gmail.com Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 4 ++++ sound/core/oss/pcm_oss.c | 44 +++++++++++++++++++++++++------------------- sound/core/pcm_native.c | 23 +++++++++++++++++++++-- 3 files changed, 50 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index a7860c047503..76fc33dce537 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -729,6 +729,10 @@ static inline void __snd_pcm_set_state(struct snd_pcm_runtime *runtime, runtime->status->state = state; /* copy for mmap */ } +void snd_pcm_set_state(struct snd_pcm_substream *substream, + snd_pcm_state_t state); +snd_pcm_state_t snd_pcm_get_state(struct snd_pcm_substream *substream); + /** * bytes_to_samples - Unit conversion of the size from bytes to samples * @runtime: PCM runtime instance diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index d4fd4dfc7fc3..a140a0d9abb8 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -1227,14 +1227,16 @@ static int snd_pcm_oss_capture_position_fixup(struct snd_pcm_substream *substrea snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel) { struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_state_t state; int ret; while (1) { - if (runtime->state == SNDRV_PCM_STATE_XRUN || - runtime->state == SNDRV_PCM_STATE_SUSPENDED) { + state = snd_pcm_get_state(substream); + if (state == SNDRV_PCM_STATE_XRUN || + state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG pcm_dbg(substream->pcm, "pcm_oss: write: recovering from %s\n", - runtime->state == SNDRV_PCM_STATE_XRUN ? + state == SNDRV_PCM_STATE_XRUN ? "XRUN" : "SUSPEND"); #endif ret = snd_pcm_oss_prepare(substream); @@ -1249,7 +1251,7 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const break; /* test, if we can't store new data, because the stream */ /* has not been started */ - if (runtime->state == SNDRV_PCM_STATE_PREPARED) + if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_PREPARED) return -EAGAIN; } return ret; @@ -1259,20 +1261,22 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_sframes_t delay; + snd_pcm_state_t state; int ret; while (1) { - if (runtime->state == SNDRV_PCM_STATE_XRUN || - runtime->state == SNDRV_PCM_STATE_SUSPENDED) { + state = snd_pcm_get_state(substream); + if (state == SNDRV_PCM_STATE_XRUN || + state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG pcm_dbg(substream->pcm, "pcm_oss: read: recovering from %s\n", - runtime->state == SNDRV_PCM_STATE_XRUN ? + state == SNDRV_PCM_STATE_XRUN ? "XRUN" : "SUSPEND"); #endif ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); if (ret < 0) break; - } else if (runtime->state == SNDRV_PCM_STATE_SETUP) { + } else if (state == SNDRV_PCM_STATE_SETUP) { ret = snd_pcm_oss_prepare(substream); if (ret < 0) break; @@ -1285,7 +1289,7 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p frames, in_kernel); mutex_lock(&runtime->oss.params_lock); if (ret == -EPIPE) { - if (runtime->state == SNDRV_PCM_STATE_DRAINING) { + if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_DRAINING) { ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); if (ret < 0) break; @@ -1301,15 +1305,16 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p #ifdef CONFIG_SND_PCM_OSS_PLUGINS snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames) { - struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_state_t state; int ret; while (1) { - if (runtime->state == SNDRV_PCM_STATE_XRUN || - runtime->state == SNDRV_PCM_STATE_SUSPENDED) { + state = snd_pcm_get_state(substream); + if (state == SNDRV_PCM_STATE_XRUN || + state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG pcm_dbg(substream->pcm, "pcm_oss: writev: recovering from %s\n", - runtime->state == SNDRV_PCM_STATE_XRUN ? + state == SNDRV_PCM_STATE_XRUN ? "XRUN" : "SUSPEND"); #endif ret = snd_pcm_oss_prepare(substream); @@ -1322,7 +1327,7 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void /* test, if we can't store new data, because the stream */ /* has not been started */ - if (runtime->state == SNDRV_PCM_STATE_PREPARED) + if (snd_pcm_get_state(substream) == SNDRV_PCM_STATE_PREPARED) return -EAGAIN; } return ret; @@ -1330,21 +1335,22 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames) { - struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_state_t state; int ret; while (1) { - if (runtime->state == SNDRV_PCM_STATE_XRUN || - runtime->state == SNDRV_PCM_STATE_SUSPENDED) { + state = snd_pcm_get_state(substream); + if (state == SNDRV_PCM_STATE_XRUN || + state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG pcm_dbg(substream->pcm, "pcm_oss: readv: recovering from %s\n", - runtime->state == SNDRV_PCM_STATE_XRUN ? + state == SNDRV_PCM_STATE_XRUN ? "XRUN" : "SUSPEND"); #endif ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); if (ret < 0) break; - } else if (runtime->state == SNDRV_PCM_STATE_SETUP) { + } else if (state == SNDRV_PCM_STATE_SETUP) { ret = snd_pcm_oss_prepare(substream); if (ret < 0) break; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 67cf6a0e17ba..394f86bc4d29 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -618,13 +618,32 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime) return usecs; } -static void snd_pcm_set_state(struct snd_pcm_substream *substream, - snd_pcm_state_t state) +/** + * snd_pcm_set_state - Set the PCM runtime state with stream lock + * @substream: PCM substream + * @state: state to set + */ +void snd_pcm_set_state(struct snd_pcm_substream *substream, + snd_pcm_state_t state) { guard(pcm_stream_lock_irq)(substream); if (substream->runtime->state != SNDRV_PCM_STATE_DISCONNECTED) __snd_pcm_set_state(substream->runtime, state); } +EXPORT_SYMBOL_GPL(snd_pcm_set_state); + +/** + * snd_pcm_get_state - Read the PCM runtime state with stream lock + * @substream: PCM substream + * + * Return: the current PCM state + */ +snd_pcm_state_t snd_pcm_get_state(struct snd_pcm_substream *substream) +{ + guard(pcm_stream_lock_irqsave)(substream); + return substream->runtime->state; +} +EXPORT_SYMBOL_GPL(snd_pcm_get_state); static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream, int event) -- cgit v1.2.3 From e29d097ead33d0172f028b5b23f10812fe8e8335 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 3 Mar 2026 15:53:07 +0000 Subject: ASoC: dapm: Add a named controls variant of a mux widget There is already a version of the mixer widget that forces use of the specified control name, rather than factoring in the widget name. Add the same feature for mux widgets. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260303155308.138989-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 + sound/soc/soc-dapm.c | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 49f0fe05db01..4f8fb7622a13 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -424,6 +424,7 @@ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ snd_soc_dapm_output, /* output pin */ snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ + snd_soc_dapm_mux_named_ctl, /* mux with named controls */ snd_soc_dapm_demux, /* connects the input to one of multiple outputs */ snd_soc_dapm_mixer, /* mixes several analog signals together */ snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 2768ba5bfc9f..d6192204e613 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -89,6 +89,7 @@ static int dapm_up_seq[] = { [snd_soc_dapm_input] = 6, [snd_soc_dapm_output] = 6, [snd_soc_dapm_mux] = 7, + [snd_soc_dapm_mux_named_ctl] = 7, [snd_soc_dapm_demux] = 7, [snd_soc_dapm_dac] = 8, [snd_soc_dapm_switch] = 9, @@ -140,6 +141,7 @@ static int dapm_down_seq[] = { [snd_soc_dapm_micbias] = 10, [snd_soc_dapm_vmid] = 10, [snd_soc_dapm_mux] = 11, + [snd_soc_dapm_mux_named_ctl] = 11, [snd_soc_dapm_demux] = 11, [snd_soc_dapm_aif_in] = 12, [snd_soc_dapm_aif_out] = 12, @@ -577,6 +579,7 @@ static int dapm_check_dynamic_path( switch (sink->id) { case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: case snd_soc_dapm_switch: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: @@ -668,6 +671,7 @@ static int dapm_add_path( switch (wsink->id) { case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: ret = dapm_connect_mux(dapm, path, control, wsink); if (ret != 0) goto err; @@ -766,6 +770,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, break; case snd_soc_dapm_demux: case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: e = (struct soc_enum *)kcontrol->private_value; if (e->autodisable) { @@ -915,6 +920,7 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, break; case snd_soc_dapm_demux: case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: data->widget->on_val = value >> data->widget->shift; break; default: @@ -1198,6 +1204,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, wname_in_long_name = true; kcname_in_long_name = true; break; + case snd_soc_dapm_mux_named_ctl: case snd_soc_dapm_mixer_named_ctl: wname_in_long_name = false; kcname_in_long_name = true; @@ -1317,6 +1324,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) switch (w->id) { case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: dir = SND_SOC_DAPM_DIR_OUT; type = "mux"; break; @@ -2399,6 +2407,7 @@ static const char * const dapm_type_name[] = { [snd_soc_dapm_input] = "input", [snd_soc_dapm_output] = "output", [snd_soc_dapm_mux] = "mux", + [snd_soc_dapm_mux_named_ctl] = "mux_named_ctl", [snd_soc_dapm_demux] = "demux", [snd_soc_dapm_mixer] = "mixer", [snd_soc_dapm_mixer_named_ctl] = "mixer_named_ctl", @@ -3347,6 +3356,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) dapm_new_mixer(w); break; case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: case snd_soc_dapm_demux: dapm_new_mux(w); break; @@ -3834,6 +3844,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, break; case snd_soc_dapm_mux: + case snd_soc_dapm_mux_named_ctl: case snd_soc_dapm_demux: case snd_soc_dapm_switch: case snd_soc_dapm_mixer: -- cgit v1.2.3 From 785639b5bf2a87eaf0cda14baaa068b3728c7be2 Mon Sep 17 00:00:00 2001 From: Cássio Gabriel Date: Mon, 16 Mar 2026 10:39:38 -0300 Subject: ALSA: timer: keep a list of open masters for slave lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit snd_timer_check_slave() still walks all registered timers and all open timer instances to find a matching master for a newly opened slave. Maintain a global list of open master instances that can accept slave links and use it for the slave lookup path instead. This keeps the existing matching semantics while avoiding the nested walk over snd_timer_list and each timer open_list_head. The reverse path in snd_timer_check_master() already scans only the pending slave list, so this makes both lookup paths closer in shape without changing the master/slave linking logic. Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260316-alsa-timer-master-list-v1-1-fb95e547110a@gmail.com Signed-off-by: Takashi Iwai --- include/sound/timer.h | 1 + sound/core/timer.c | 29 ++++++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/sound/timer.h b/include/sound/timer.h index 760e132cc0cd..83bafe70cf33 100644 --- a/include/sound/timer.h +++ b/include/sound/timer.h @@ -102,6 +102,7 @@ struct snd_timer_instance { unsigned int slave_id; struct list_head open_list; struct list_head active_list; + struct list_head master_list; struct list_head ack_list; struct list_head slave_list_head; struct list_head slave_active_head; diff --git a/sound/core/timer.c b/sound/core/timer.c index 6a70df7ae019..820901d503af 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -129,6 +129,9 @@ static LIST_HEAD(snd_timer_list); /* list of slave instances */ static LIST_HEAD(snd_timer_slave_list); +/* list of open master instances that can accept slave links */ +static LIST_HEAD(snd_timer_master_list); + /* lock for slave active lists */ static DEFINE_SPINLOCK(slave_active_lock); @@ -161,6 +164,7 @@ struct snd_timer_instance *snd_timer_instance_new(const char *owner) } INIT_LIST_HEAD(&timeri->open_list); INIT_LIST_HEAD(&timeri->active_list); + INIT_LIST_HEAD(&timeri->master_list); INIT_LIST_HEAD(&timeri->ack_list); INIT_LIST_HEAD(&timeri->slave_list_head); INIT_LIST_HEAD(&timeri->slave_active_head); @@ -245,6 +249,12 @@ static int check_matching_master_slave(struct snd_timer_instance *master, return 1; } +static bool snd_timer_has_slave_key(const struct snd_timer_instance *timeri) +{ + return !(timeri->flags & SNDRV_TIMER_IFLG_SLAVE) && + timeri->slave_class > SNDRV_TIMER_SCLASS_NONE; +} + /* * look for a master instance matching with the slave id of the given slave. * when found, relink the open_link of the slave. @@ -253,19 +263,15 @@ static int check_matching_master_slave(struct snd_timer_instance *master, */ static int snd_timer_check_slave(struct snd_timer_instance *slave) { - struct snd_timer *timer; struct snd_timer_instance *master; int err = 0; - /* FIXME: it's really dumb to look up all entries.. */ - list_for_each_entry(timer, &snd_timer_list, device_list) { - list_for_each_entry(master, &timer->open_list_head, open_list) { - err = check_matching_master_slave(master, slave); - if (err != 0) /* match found or error */ - goto out; - } + list_for_each_entry(master, &snd_timer_master_list, master_list) { + err = check_matching_master_slave(master, slave); + if (err != 0) /* match found or error */ + goto out; } - out: +out: return err < 0 ? err : 0; } @@ -377,6 +383,8 @@ int snd_timer_open(struct snd_timer_instance *timeri, timeri->slave_id = slave_id; list_add_tail(&timeri->open_list, &timer->open_list_head); + if (snd_timer_has_slave_key(timeri)) + list_add_tail(&timeri->master_list, &snd_timer_master_list); timer->num_instances++; err = snd_timer_check_master(timeri); list_added: @@ -431,6 +439,9 @@ static void snd_timer_close_locked(struct snd_timer_instance *timeri, num_slaves--; } + if (!list_empty(&timeri->master_list)) + list_del_init(&timeri->master_list); + /* force to stop the timer */ snd_timer_stop(timeri); -- cgit v1.2.3 From c2da4813882b8037198cd8e67182293e17b44573 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Mar 2026 00:15:03 +0000 Subject: ASoC: soc-component: add snd_soc_component_regmap_val_bytes() component has component->val_bytes which is set via snd_soc_component_setup_regmap(). But it can be calculated via component->regmap. No need to keep it as component->val_bytes. Add snd_soc_component_regmap_val_bytes() for it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/878qbxzywo.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 1 + sound/soc/soc-component.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 2a2b74b24a60..c08922290cf1 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -327,6 +327,7 @@ int snd_soc_component_stream_event(struct snd_soc_component *component, int snd_soc_component_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level); +int snd_soc_component_regmap_val_bytes(struct snd_soc_component *component); void snd_soc_component_setup_regmap(struct snd_soc_component *component); #ifdef CONFIG_REGMAP void snd_soc_component_init_regmap(struct snd_soc_component *component, diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 89f236ab3034..9dc2cbdc2dda 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -351,6 +351,23 @@ void snd_soc_component_setup_regmap(struct snd_soc_component *component) component->val_bytes = val_bytes; } +int snd_soc_component_regmap_val_bytes(struct snd_soc_component *component) +{ + int val_bytes; + + /* Errors are legitimate for non-integer byte multiples */ + + if (!component->regmap) + return 0; + + val_bytes = regmap_get_val_bytes(component->regmap); + if (val_bytes < 0) + return 0; + + return val_bytes; +} +EXPORT_SYMBOL_GPL(snd_soc_component_regmap_val_bytes); + #ifdef CONFIG_REGMAP /** -- cgit v1.2.3 From b84d27531744e046a72120882f513f42e361269d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Mar 2026 00:15:55 +0000 Subject: ASoC: soc-component: remove component->val_bytes component has component->val_bytes which is set via snd_soc_component_setup_regmap(). But it can be calculated via component->regmap. No need to keep it as component->val_bytes. No one is using component->val_bytes. Remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/874imlzyv8.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 2 -- sound/soc/soc-component.c | 10 ---------- sound/soc/soc-core.c | 2 -- 3 files changed, 14 deletions(-) (limited to 'include') diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index c08922290cf1..65dc540246aa 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -224,7 +224,6 @@ struct snd_soc_component { int num_dai; struct regmap *regmap; - int val_bytes; struct mutex io_mutex; @@ -328,7 +327,6 @@ int snd_soc_component_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level); int snd_soc_component_regmap_val_bytes(struct snd_soc_component *component); -void snd_soc_component_setup_regmap(struct snd_soc_component *component); #ifdef CONFIG_REGMAP void snd_soc_component_init_regmap(struct snd_soc_component *component, struct regmap *regmap); diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 9dc2cbdc2dda..2eaad5db2130 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -342,15 +342,6 @@ int snd_soc_component_of_xlate_dai_name(struct snd_soc_component *component, return -ENOTSUPP; } -void snd_soc_component_setup_regmap(struct snd_soc_component *component) -{ - int val_bytes = regmap_get_val_bytes(component->regmap); - - /* Errors are legitimate for non-integer byte multiples */ - if (val_bytes > 0) - component->val_bytes = val_bytes; -} - int snd_soc_component_regmap_val_bytes(struct snd_soc_component *component) { int val_bytes; @@ -385,7 +376,6 @@ void snd_soc_component_init_regmap(struct snd_soc_component *component, struct regmap *regmap) { component->regmap = regmap; - snd_soc_component_setup_regmap(component); } EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 573693e21780..112857a5300b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2909,8 +2909,6 @@ int snd_soc_add_component(struct snd_soc_component *component, if (!component->regmap) component->regmap = dev_get_regmap(component->dev, NULL); - if (component->regmap) - snd_soc_component_setup_regmap(component); } /* see for_each_component */ -- cgit v1.2.3 From d12dc8c558b955e74bf3dc9c01926c3c109f2d69 Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Tue, 24 Mar 2026 09:42:59 +0530 Subject: ASoC: sdw_utils: add vendor_id to asoc_sdw_codec_info struct asoc_sdw_codec_info has part_id which is not sufficient to uniquely identify devices. This change adds the vendor_id field and updates the codec_info list with the corresponding vendor id as per the Manufacturer's id in https://mid.mipi.org/ Signed-off-by: Niranjan H Y Reviewed-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260324041300.784-1-niranjan.hy@ti.com Signed-off-by: Mark Brown --- include/sound/soc_sdw_utils.h | 1 + sound/soc/sdw_utils/soc_sdw_utils.c | 48 +++++++++++++++++++++++++++++++++---- sound/soc/sof/intel/hda.c | 7 ++++-- 3 files changed, 49 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 98531e500cbb..48f516ba682f 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -71,6 +71,7 @@ struct asoc_sdw_aux_info { }; struct asoc_sdw_codec_info { + const int vendor_id; const int part_id; const int version_id; const char *name_prefix; diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 75658148df57..637c640001d6 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -73,6 +73,7 @@ static const struct snd_kcontrol_new rt700_controls[] = { struct asoc_sdw_codec_info codec_info_list[] = { { + .vendor_id = 0x0102, .part_id = 0x0000, /* TAS2783A */ .name_prefix = "tas2783", .dais = { @@ -92,6 +93,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x700, .name_prefix = "rt700", .dais = { @@ -110,6 +112,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x711, .name_prefix = "rt711", .version_id = 3, @@ -131,6 +134,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x711, .name_prefix = "rt711", .version_id = 2, @@ -152,6 +156,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x712, .name_prefix = "rt712", .version_id = 3, @@ -194,6 +199,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 3, }, { + .vendor_id = 0x025d, .part_id = 0x1712, .name_prefix = "rt712-dmic", .version_id = 3, @@ -209,6 +215,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x713, .name_prefix = "rt713", .version_id = 3, @@ -237,6 +244,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 2, }, { + .vendor_id = 0x025d, .part_id = 0x1713, .name_prefix = "rt713-dmic", .version_id = 3, @@ -252,6 +260,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x1308, .name_prefix = "rt1308", .acpi_id = "10EC1308", @@ -275,6 +284,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .ops = &soc_sdw_rt1308_i2s_ops, }, { + .vendor_id = 0x025d, .part_id = 0x1316, .name_prefix = "rt1316", .dais = { @@ -296,6 +306,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x1318, .name_prefix = "rt1318", .dais = { @@ -317,6 +328,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x1320, .name_prefix = "rt1320", .dais = { @@ -338,6 +350,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x1321, .name_prefix = "rt1320", .dais = { @@ -359,6 +372,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x714, .name_prefix = "rt714", .version_id = 3, @@ -375,6 +389,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x715, .name_prefix = "rt715", .version_id = 3, @@ -391,6 +406,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x714, .name_prefix = "rt714", .version_id = 2, @@ -407,6 +423,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x715, .name_prefix = "rt715", .version_id = 2, @@ -423,6 +440,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x721, .name_prefix = "rt721", .version_id = 3, @@ -466,6 +484,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 3, }, { + .vendor_id = 0x025d, .part_id = 0x722, .name_prefix = "rt722", .version_id = 3, @@ -513,6 +532,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 3, }, { + .vendor_id = 0x019f, .part_id = 0x8373, .name_prefix = "Left", .dais = { @@ -533,6 +553,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x019f, .part_id = 0x8363, .name_prefix = "Left", .dais = { @@ -553,6 +574,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x025d, .part_id = 0x5682, .name_prefix = "rt5682", .dais = { @@ -571,6 +593,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x01fa, .part_id = 0x3556, .name_prefix = "AMP", .dais = { @@ -598,6 +621,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 2, }, { + .vendor_id = 0x01fa, .part_id = 0x3557, .name_prefix = "AMP", .dais = { @@ -625,6 +649,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 2, }, { + .vendor_id = 0x01fa, .part_id = 0x3563, .name_prefix = "AMP", .dais = { @@ -652,6 +677,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 2, }, { + .vendor_id = 0x01fa, .part_id = 0x4242, .name_prefix = "cs42l42", .dais = { @@ -670,6 +696,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x01fa, .part_id = 0x4243, .name_prefix = "cs42l43", .count_sidecar = asoc_sdw_bridge_cs35l56_count_sidecar, @@ -724,6 +751,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 4, }, { + .vendor_id = 0x01fa, .part_id = 0x2A3B, .name_prefix = "cs42l43", .count_sidecar = asoc_sdw_bridge_cs35l56_count_sidecar, @@ -778,6 +806,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 4, }, { + .vendor_id = 0x01fa, .part_id = 0x4245, .name_prefix = "cs42l45", .dais = { @@ -814,6 +843,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .aux_num = 1, }, { + .vendor_id = 0x01fa, .part_id = 0x4249, .name_prefix = "cs42l45", /* Use same user-space as cs42l45 */ .dais = { @@ -850,6 +880,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .aux_num = 1, }, { + .vendor_id = 0x01fa, .part_id = 0x4747, .name_prefix = "cs47l47", .dais = { @@ -886,6 +917,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .aux_num = 1, }, { + .vendor_id = 0x0105, .part_id = 0xaaaa, /* generic codec mockup */ .name_prefix = "sdw_mockup_mmulti-function", .version_id = 0, @@ -912,6 +944,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 3, }, { + .vendor_id = 0x0105, .part_id = 0xaa55, /* headset codec mockup */ .name_prefix = "sdw_mockup_headset0", .version_id = 0, @@ -926,6 +959,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x0105, .part_id = 0x55aa, /* amplifier mockup */ .name_prefix = "sdw_mockup_amp1", .version_id = 0, @@ -940,6 +974,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .vendor_id = 0x0105, .part_id = 0x5555, .name_prefix = "sdw_mockup_mic0", .version_id = 0, @@ -964,9 +999,10 @@ EXPORT_SYMBOL_NS(asoc_sdw_get_codec_info_list_count, "SND_SOC_SDW_UTILS"); struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr) { - unsigned int part_id, sdw_version; + unsigned int vendor_id, part_id, sdw_version; int i; + vendor_id = SDW_MFG_ID(adr); part_id = SDW_PART_ID(adr); sdw_version = SDW_VERSION(adr); for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) @@ -975,6 +1011,7 @@ struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr) * version_id is not specified in the codec info. */ if (part_id == codec_info_list[i].part_id && + vendor_id == codec_info_list[i].vendor_id && (!codec_info_list[i].version_id || sdw_version == codec_info_list[i].version_id)) return &codec_info_list[i]; @@ -989,6 +1026,7 @@ static struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_sdw_id(const struct for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) if (id->part_id == codec_info_list[i].part_id && + id->mfg_id == codec_info_list[i].vendor_id && (!codec_info_list[i].version_id || id->sdw_version == codec_info_list[i].version_id)) return &codec_info_list[i]; @@ -1093,8 +1131,8 @@ int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) ret = snd_soc_add_card_controls(card, codec_info->dais[dai_index].controls, codec_info->dais[dai_index].num_controls); if (ret) { - dev_err(card->dev, "%#x controls addition failed: %d\n", - codec_info->part_id, ret); + dev_err(card->dev, "%#x-%#x controls addition failed: %d\n", + codec_info->vendor_id, codec_info->part_id, ret); return ret; } } @@ -1103,8 +1141,8 @@ int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) codec_info->dais[dai_index].widgets, codec_info->dais[dai_index].num_widgets); if (ret) { - dev_err(card->dev, "%#x widgets addition failed: %d\n", - codec_info->part_id, ret); + dev_err(card->dev, "%#x-%#x widgets addition failed: %d\n", + codec_info->vendor_id, codec_info->part_id, ret); return ret; } } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index c0cc7d3ce526..9ec33147d9af 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1179,6 +1179,9 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev, struct snd_soc_acpi_endpoint *endpoints; int amp_group_id = 1; + if (sdw_device->id.mfg_id != codec_info_list[i].vendor_id) + continue; + if (sdw_device->id.part_id != codec_info_list[i].part_id) continue; @@ -1193,8 +1196,8 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev, * dereference */ if (!name_prefix) { - dev_err(dev, "codec_info_list name_prefix of part id %#x is missing\n", - codec_info_list[i].part_id); + dev_err(dev, "codec_info_list name_prefix of part id %#x-%#x is missing\n", + codec_info_list[i].vendor_id, codec_info_list[i].part_id); return NULL; } for (j = 0; j < codec_info_list[i].dai_num; j++) { -- cgit v1.2.3 From 37c277f050e8d24cb3db6d090d4e9cdd263ba1a3 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 25 Mar 2026 17:08:40 +0000 Subject: ASoC: soc.h: Add SOC_SINGLE_BOOL_EXT_ACC() to allow setting access flags Add a macro SOC_SINGLE_BOOL_EXT_ACC() to allow the access permission flags to be set. This is the same as SOC_SINGLE_BOOL_EXT() but with an extra argument for the access flags. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260325170841.1405368-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/soc.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 7d8376c8e1be..8055def75195 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -311,6 +311,12 @@ struct platform_device; .info = snd_soc_info_bool_ext, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = xdata } +#define SOC_SINGLE_BOOL_EXT_ACC(xname, xdata, xhandler_get, xhandler_put, xaccess) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = xaccess, \ + .info = snd_soc_info_bool_ext, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = xdata } #define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ -- cgit v1.2.3 From ee7d655dbaf5e57145c73fd3925b5f44f7a1a5cc Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 25 Mar 2026 17:08:41 +0000 Subject: ASoC: cs35l56: Allow factory calibration through ALSA controls Add support for using ALSA controls to trigger a factory calibration. This is protected by a new Kconfig option so that it is only available if explicitly enabled in the kernel. By default it is not enabled. Factory calibration is normally done through debugfs files. Google have requested that factory calibration can be performed by repair shops. These repair shops only have access to the standard "user" kernel, which does not include debugfs. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260325170841.1405368-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 1 + sound/soc/codecs/Kconfig | 13 ++++++ sound/soc/codecs/cs35l56-shared.c | 9 ++++ sound/soc/codecs/cs35l56.c | 96 +++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l56.h | 2 + 5 files changed, 121 insertions(+) (limited to 'include') diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 28f9f5940ab6..4c1969cd84d8 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -435,6 +435,7 @@ ssize_t cs35l56_cal_data_debugfs_read(struct cs35l56_base *cs35l56_base, ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base, const char __user *from, size_t count, loff_t *ppos); +int cs35l56_factory_calibrate(struct cs35l56_base *cs35l56_base); void cs35l56_create_cal_debugfs(struct cs35l56_base *cs35l56_base, const struct cs35l56_cal_debugfs_fops *fops); void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index adb3fb923be3..8bcffb812828 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -928,6 +928,19 @@ config SND_SOC_CS35L56_CAL_SET_CTRL If unsure select "N". +config SND_SOC_CS35L56_CAL_PERFORM_CTRL + bool "CS35L56 ALSA control to perform factory calibration" + default N + select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON + help + Allow performing factory calibration data through an ALSA + control. It is recommended to use the debugfs method instead + because debugfs has restricted access permissions. + + On most platforms this is not needed. + + If unsure select "N". + config SND_SOC_CS35L56_TEST tristate "KUnit test for Cirrus Logic cs35l56 driver" if !KUNIT_ALL_TESTS depends on SND_SOC_CS35L56 && KUNIT diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index af87ebae98cb..e05d975ba794 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -1185,6 +1185,15 @@ ssize_t cs35l56_calibrate_debugfs_write(struct cs35l56_base *cs35l56_base, } EXPORT_SYMBOL_NS_GPL(cs35l56_calibrate_debugfs_write, "SND_SOC_CS35L56_SHARED"); +int cs35l56_factory_calibrate(struct cs35l56_base *cs35l56_base) +{ + if (!IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_PERFORM_CTRL)) + return -ENXIO; + + return cs35l56_perform_calibration(cs35l56_base); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_factory_calibrate, "SND_SOC_CS35L56_SHARED"); + ssize_t cs35l56_cal_ambient_debugfs_write(struct cs35l56_base *cs35l56_base, const char __user *from, size_t count, loff_t *ppos) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 37909a319f88..f03a2b47dc6c 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1109,6 +1109,88 @@ static int cs35l56_cal_data_ctl_set(struct snd_kcontrol *kcontrol, return 1; } +static int cs35l56_cal_ambient_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = cs35l56->ambient_ctl_value; + + return 0; +} + +static int cs35l56_cal_ambient_ctl_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm; + int temperature = ucontrol->value.integer.value[0]; + int ret; + + if (temperature == cs35l56->ambient_ctl_value) + return 0; + + if ((temperature < 0) || (temperature > 40)) + return -EINVAL; + + dapm = cs35l56_power_up_for_cal(cs35l56); + if (IS_ERR(dapm)) + return PTR_ERR(dapm); + + ret = cs_amp_write_ambient_temp(&cs35l56->dsp.cs_dsp, + cs35l56->base.calibration_controls, + temperature); + cs35l56_power_down_after_cal(cs35l56); + + if (ret) + return ret; + + cs35l56->ambient_ctl_value = temperature; + + return 1; +} + +static int cs35l56_calibrate_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Allow reading because of user-side libraries that assume all + * controls are readable. But always return false to prevent dumb + * save-restore tools like alsactl accidentically triggering a + * factory calibration when they restore. + */ + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int cs35l56_calibrate_ctl_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm; + int ret; + + if (ucontrol->value.integer.value[0] == 0) + return 0; + + dapm = cs35l56_power_up_for_cal(cs35l56); + if (IS_ERR(dapm)) + return PTR_ERR(dapm); + + snd_soc_dapm_mutex_lock(dapm); + ret = cs35l56_factory_calibrate(&cs35l56->base); + snd_soc_dapm_mutex_unlock(dapm); + cs35l56_power_down_after_cal(cs35l56); + if (ret < 0) + return ret; + + return 1; +} + static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = { SND_SOC_BYTES_E("CAL_DATA", 0, sizeof(struct cirrus_amp_cal_data) / sizeof(u32), cs35l56_cal_data_ctl_get, cs35l56_cal_data_ctl_set), @@ -1117,6 +1199,14 @@ static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = { SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE), }; +static const struct snd_kcontrol_new cs35l56_cal_perform_controls[] = { + SOC_SINGLE_EXT("CAL_AMBIENT", SND_SOC_NOPM, 0, 40, 0, + cs35l56_cal_ambient_ctl_get, cs35l56_cal_ambient_ctl_set), + SOC_SINGLE_BOOL_EXT_ACC("Calibrate Switch", 0, + cs35l56_calibrate_ctl_get, cs35l56_calibrate_ctl_set, + SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_VOLATILE), +}; + VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) { unsigned short vendor, device; @@ -1290,6 +1380,12 @@ static int cs35l56_component_probe(struct snd_soc_component *component) ARRAY_SIZE(cs35l56_cal_data_restore_controls)); } + if (!ret && IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_PERFORM_CTRL)) { + ret = snd_soc_add_component_controls(component, + cs35l56_cal_perform_controls, + ARRAY_SIZE(cs35l56_cal_perform_controls)); + } + if (ret) return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n"); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 691f857d0bd8..9aaff2140bbb 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -53,6 +53,8 @@ struct cs35l56_private { bool sysclk_set; u8 sdw_link_num; u8 sdw_unique_id; + + u8 ambient_ctl_value; }; static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_base *cs35l56_base) -- cgit v1.2.3 From dd4a1963ddf0d0f5e129efec03f34ea37109b4b7 Mon Sep 17 00:00:00 2001 From: Derek Fang Date: Thu, 26 Mar 2026 15:53:01 +0800 Subject: ASoC: SOF: Intel: Add a is_amp flag to fix the wrong name prefix According to the Intel sof design, it will create the name prefix appended with amp index for the amp codec only, such as: rt1318-1, rt1318-2, etc... But the rt1320 is a codec with amp and mic codec functions, it doesn't have the amp index in its name prefix as above. And then it will be hard to identify the codec if in multi-rt1320 case. So we add a flag to force the amp index to be appended. Signed-off-by: Derek Fang Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260326075303.1083567-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc_sdw_utils.h | 2 ++ sound/soc/sof/intel/hda.c | 10 ++++++++++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 48f516ba682f..489083183673 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -83,6 +83,8 @@ struct asoc_sdw_codec_info { const int dai_num; struct asoc_sdw_aux_info auxs[SOC_SDW_MAX_AUX_NUM]; const int aux_num; + /* Force AMP-style name_prefix handling (append AMP index) even if MIC/Jack DAIs exist */ + const bool is_amp; int (*codec_card_late_probe)(struct snd_soc_card *card); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 9ec33147d9af..edb80c2fa770 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1232,6 +1232,16 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev, return NULL; } + /* + * codec_info_list[].is_amp is a codec-level override: for multi-function + * codecs we must treat the whole codec as an AMP when it is described as + * such in the codec info table, even if some endpoints were detected as + * non-AMP above. Callers/UCM rely on this to keep name_prefix and AMP + * indexing stable and backwards compatible. + */ + if (codec_info_list[i].is_amp) + is_amp = true; + adr_dev[index].adr = ((u64)sdw_device->id.class_id & 0xFF) | ((u64)sdw_device->id.part_id & 0xFFFF) << 8 | ((u64)sdw_device->id.mfg_id & 0xFFFF) << 24 | -- cgit v1.2.3 From 7caae0aed04137545e9f8c146d8d1dbb7a8e9865 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 27 Mar 2026 02:43:31 +0000 Subject: ASoC: soc-core: remove unused dobj_list commit 8a9782346dccd ("ASoC: topology: Add topology core") added dobj_list to Component and Card, but Card side has never been used. Remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/874im2xa98.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 3 --- sound/soc/soc-core.c | 1 - 2 files changed, 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index fd6c1c8055d2..d66164fd83e5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1073,9 +1073,6 @@ struct snd_soc_card { struct list_head dapm_list; struct list_head dapm_dirty; - /* attached dynamic objects */ - struct list_head dobj_list; - /* Generic DAPM context for the card */ struct snd_soc_dapm_context *dapm; struct snd_soc_dapm_stats dapm_stats; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f80f1a149ad1..67d9ea319c84 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2574,7 +2574,6 @@ int snd_soc_register_card(struct snd_soc_card *card) INIT_LIST_HEAD(&card->list); INIT_LIST_HEAD(&card->rtd_list); INIT_LIST_HEAD(&card->dapm_dirty); - INIT_LIST_HEAD(&card->dobj_list); card->instantiated = 0; mutex_init(&card->mutex); -- cgit v1.2.3 From 3bd246d1cf609a80cae19e4aefb599256a72b1a6 Mon Sep 17 00:00:00 2001 From: Cássio Gabriel Date: Sat, 28 Mar 2026 01:53:35 -0300 Subject: ALSA: hda/proc: show GPI and GPO state in codec proc output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit print_gpio() prints the GPIO capability header and the bidirectional GPIO state, but it never reports the separate GPI and GPO pins even though AC_PAR_GPIO_CAP exposes their counts. The HD-audio specification defines dedicated GPI and GPO verbs alongside the GPIO ones, so codecs with input-only or output-only general-purpose pins currently lose that state from /proc/asound/card*/codec#* altogether. Add the missing read verb definitions and extend print_gpio() to dump the GPI and GPO pins, too, while leaving the existing IO[] output unchanged. Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260328-hda-proc-gpi-gpo-v1-1-fabb36564bee@gmail.com Signed-off-by: Takashi Iwai --- include/sound/hda_verbs.h | 7 +++- sound/hda/common/proc.c | 100 +++++++++++++++++++++++++++++++--------------- 2 files changed, 74 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/sound/hda_verbs.h b/include/sound/hda_verbs.h index 006d358acce2..127e7016e4fe 100644 --- a/include/sound/hda_verbs.h +++ b/include/sound/hda_verbs.h @@ -56,7 +56,12 @@ enum { #define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d #define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */ #define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f -/* f10-f1a: GPIO */ +/* f10-f1a: GPI/GPO/GPIO */ +#define AC_VERB_GET_GPI_DATA 0x0f10 +#define AC_VERB_GET_GPI_WAKE_MASK 0x0f11 +#define AC_VERB_GET_GPI_UNSOLICITED_RSP_MASK 0x0f12 +#define AC_VERB_GET_GPI_STICKY_MASK 0x0f13 +#define AC_VERB_GET_GPO_DATA 0x0f14 #define AC_VERB_GET_GPIO_DATA 0x0f15 #define AC_VERB_GET_GPIO_MASK 0x0f16 #define AC_VERB_GET_GPIO_DIRECTION 0x0f17 diff --git a/sound/hda/common/proc.c b/sound/hda/common/proc.c index 3bc33c5617b2..c83796b13d3d 100644 --- a/sound/hda/common/proc.c +++ b/sound/hda/common/proc.c @@ -640,41 +640,78 @@ static void print_gpio(struct snd_info_buffer *buffer, { unsigned int gpio = param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); - unsigned int enable, direction, wake, unsol, sticky, data; - int i, max; + int i, gpio_max, gpo_max, gpi_max; + + gpio_max = gpio & AC_GPIO_IO_COUNT; + gpo_max = (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT; + gpi_max = (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT; + snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, " "unsolicited=%d, wake=%d\n", - gpio & AC_GPIO_IO_COUNT, - (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT, - (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT, + gpio_max, gpo_max, gpi_max, (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0, (gpio & AC_GPIO_WAKE) ? 1 : 0); - max = gpio & AC_GPIO_IO_COUNT; - if (!max || max > 8) - return; - enable = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_MASK, 0); - direction = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_DIRECTION, 0); - wake = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_WAKE_MASK, 0); - unsol = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0); - sticky = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_STICKY_MASK, 0); - data = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_DATA, 0); - for (i = 0; i < max; ++i) - snd_iprintf(buffer, - " IO[%d]: enable=%d, dir=%d, wake=%d, " - "sticky=%d, data=%d, unsol=%d\n", i, - (enable & (1<mixers); print_nid_array(buffer, codec, nid, &codec->nids); } @@ -940,4 +977,3 @@ int snd_hda_codec_proc_new(struct hda_codec *codec) snprintf(name, sizeof(name), "codec#%d", codec->core.addr); return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info); } - -- cgit v1.2.3 From 38f6e93dedbc1b6c2a6e97110ab7e872c257a5e3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 28 Mar 2026 14:43:05 +0100 Subject: ALSA: hda: Add missing SET_GPI_* and SET_GPO_* verb definitions We've added the definitions of the missing GPI and GPO verbs for reading in the previous commit, but the counter-part for setting values is missing. Add the definitions of missing verbs for comprehensiveness. Link: https://patch.msgid.link/20260328134319.207482-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- include/sound/hda_verbs.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/sound/hda_verbs.h b/include/sound/hda_verbs.h index 127e7016e4fe..6066954409aa 100644 --- a/include/sound/hda_verbs.h +++ b/include/sound/hda_verbs.h @@ -104,6 +104,11 @@ enum { #define AC_VERB_SET_DIGI_CONVERT_2 0x70e #define AC_VERB_SET_DIGI_CONVERT_3 0x73e #define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f +#define AC_VERB_SET_GPI_DATA 0x710 +#define AC_VERB_SET_GPI_WAKE_MASK 0x711 +#define AC_VERB_SET_SPI_UNSOLICITED_RSP_MASK 0x712 +#define AC_VERB_SET_GPI_STICKY_MASK 0x713 +#define AC_VERB_SET_GPO_DATA 0x714 #define AC_VERB_SET_GPIO_DATA 0x715 #define AC_VERB_SET_GPIO_MASK 0x716 #define AC_VERB_SET_GPIO_DIRECTION 0x717 -- cgit v1.2.3 From 5a77906982df26975aa26caefb81b7d6f53d9b3f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 1 Apr 2026 00:19:06 +0000 Subject: ASoC: soc.h: remove snd_soc_of_parse_audio_prefix() No one is using snd_soc_of_parse_audio_prefix(). Remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/877bqrttvp.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d66164fd83e5..f70edd9c23b2 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1337,15 +1337,6 @@ void snd_soc_of_parse_node_prefix(struct device_node *np, struct snd_soc_codec_conf *codec_conf, struct device_node *of_node, const char *propname); -static inline -void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, - struct snd_soc_codec_conf *codec_conf, - struct device_node *of_node, - const char *propname) -{ - snd_soc_of_parse_node_prefix(card->dev->of_node, - codec_conf, of_node, propname); -} int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); -- cgit v1.2.3 From e46957f27c6004f3ab5ec456f4e848950364ebc1 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:11 +0000 Subject: ASoC: dt-bindings: qcom: add LPASS LPI MI2S dai ids Add new dai ids entries for LPASS LPI MI2S and SENARY MI2S audio lines. Co-developed-by: Mohammad Rafi Shaik Signed-off-by: Mohammad Rafi Shaik Signed-off-by: Srinivas Kandagatla Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260402081118.348071-7-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml | 5 ++++- include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml index 08c618e7e428..2b27d6c8f58f 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml @@ -126,13 +126,16 @@ patternProperties: reg: contains: # MI2S DAI ID range PRIMARY_MI2S_RX - QUATERNARY_MI2S_TX and - # QUINARY_MI2S_RX - QUINARY_MI2S_TX + # QUINARY_MI2S_RX - QUINARY_MI2S_TX and + # LPI_MI2S_RX_0 - SENARY_MI2S_TX items: oneOf: - minimum: 16 maximum: 23 - minimum: 127 maximum: 128 + - minimum: 137 + maximum: 148 then: required: - qcom,sd-lines diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index 6d1ce7f5da51..45850f2d4342 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -140,6 +140,18 @@ #define DISPLAY_PORT_RX_6 134 #define DISPLAY_PORT_RX_7 135 #define USB_RX 136 +#define LPI_MI2S_RX_0 137 +#define LPI_MI2S_TX_0 138 +#define LPI_MI2S_RX_1 139 +#define LPI_MI2S_TX_1 140 +#define LPI_MI2S_RX_2 141 +#define LPI_MI2S_TX_2 142 +#define LPI_MI2S_RX_3 143 +#define LPI_MI2S_TX_3 144 +#define LPI_MI2S_RX_4 145 +#define LPI_MI2S_TX_4 146 +#define SENARY_MI2S_RX 147 +#define SENARY_MI2S_TX 148 #define LPASS_CLK_ID_PRI_MI2S_IBIT 1 #define LPASS_CLK_ID_PRI_MI2S_EBIT 2 -- cgit v1.2.3 From ba2a0e81d4d71c3bbc61c420b6fac5abaeddd77d Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Wed, 1 Apr 2026 18:51:45 +0530 Subject: ASoC: SDCA: Export Q7.8 volume control helpers Export the Q7.8 volume control helpers to allow reuse by other ASoC drivers. These functions handle 16-bit signed Q7.8 fixed-point format values for volume controls. Changes include: - Rename q78_get_volsw to sdca_asoc_q78_get_volsw - Rename q78_put_volsw to sdca_asoc_q78_put_volsw - Add a convenience macro SDCA_SINGLE_Q78_TLV and SDCA_DOUBLE_Q78_TLV for creating mixer controls This allows other ASoC drivers to easily implement controls using the Q7.8 fixed-point format without duplicating code. Signed-off-by: Niranjan H Y Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260401132148.2367-1-niranjan.hy@ti.com Signed-off-by: Mark Brown --- include/sound/sdca_asoc.h | 43 ++++++++++++++++++++++++++++++++++++++++++- sound/soc/sdca/sdca_asoc.c | 14 ++++++++------ 2 files changed, 50 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index aa9124f93218..46a61a52decc 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -13,6 +13,8 @@ struct device; struct regmap; struct sdca_function_data; +struct snd_ctl_elem_value; +struct snd_kcontrol; struct snd_kcontrol_new; struct snd_pcm_hw_params; struct snd_pcm_substream; @@ -23,6 +25,42 @@ struct snd_soc_dai_ops; struct snd_soc_dapm_route; struct snd_soc_dapm_widget; +/* convenient macro to handle the mono volume in 7.8 fixed format representation */ +#define SDCA_SINGLE_Q78_TLV(xname, xreg, xmin, xmax, xstep, tlv_array) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = sdca_asoc_q78_get_volsw, \ + .put = sdca_asoc_q78_put_volsw, \ + .private_value = (unsigned long)&(struct soc_mixer_control) { \ + .reg = (xreg), .rreg = (xreg), \ + .min = (xmin), .max = (xmax), \ + .shift = (xstep), .rshift = (xstep), \ + .sign_bit = 15 \ + } \ +} + +/* convenient macro for stereo volume in 7.8 fixed format with separate registers for L/R */ +#define SDCA_DOUBLE_Q78_TLV(xname, xreg_l, xreg_r, xmin, xmax, xstep, tlv_array) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = sdca_asoc_q78_get_volsw, \ + .put = sdca_asoc_q78_put_volsw, \ + .private_value = (unsigned long)&(struct soc_mixer_control) { \ + .reg = (xreg_l), .rreg = (xreg_r), \ + .min = (xmin), .max = (xmax), \ + .shift = (xstep), .rshift = (xstep), \ + .sign_bit = 15 \ + } \ +} + int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *function, int *num_widgets, int *num_routes, int *num_controls, int *num_dais); @@ -57,5 +95,8 @@ int sdca_asoc_hw_params(struct device *dev, struct regmap *regmap, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai); - +int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); #endif // __SDCA_ASOC_H__ diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 7709a4ce26e0..2bfc8e5aee31 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -820,8 +820,8 @@ static int q78_write(struct snd_soc_component *component, return snd_soc_component_update_bits(component, reg, mask, reg_val); } -static int q78_put_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); @@ -841,6 +841,7 @@ static int q78_put_volsw(struct snd_kcontrol *kcontrol, return ret; } +EXPORT_SYMBOL_NS(sdca_asoc_q78_put_volsw, "SND_SOC_SDCA"); static int q78_read(struct snd_soc_component *component, struct soc_mixer_control *mc, unsigned int reg) @@ -855,8 +856,8 @@ static int q78_read(struct snd_soc_component *component, return val & GENMASK(mc->sign_bit, 0); } -static int q78_get_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); @@ -868,6 +869,7 @@ static int q78_get_volsw(struct snd_kcontrol *kcontrol, return 0; } +EXPORT_SYMBOL_NS(sdca_asoc_q78_get_volsw, "SND_SOC_SDCA"); static int control_limit_kctl(struct device *dev, struct sdca_entity *entity, @@ -912,8 +914,8 @@ static int control_limit_kctl(struct device *dev, kctl->tlv.p = tlv; kctl->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; - kctl->get = q78_get_volsw; - kctl->put = q78_put_volsw; + kctl->get = sdca_asoc_q78_get_volsw; + kctl->put = sdca_asoc_q78_put_volsw; return 0; } -- cgit v1.2.3 From d38e9457ddf0780dd55c953886ae48abbe4d33b8 Mon Sep 17 00:00:00 2001 From: Cássio Gabriel Date: Mon, 6 Apr 2026 00:20:03 -0300 Subject: ALSA: gus: add shared GF1 suspend and resume helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gusclassic and gusextreme still leave their ISA PM callbacks disabled because the shared GF1 core only provides probe-time startup and full shutdown paths. Those helpers are not suitable for suspend and resume. They reset software handlers and tear down runtime state such as the DRAM allocator, timer state, DMA queues, PCM state and UART setup. Resume instead needs a narrower recovery path that rebuilds the GF1 hardware state without rerunning probe-only detection or discarding the bookkeeping kept by the card instance. Add shared GF1 suspend and resume helpers for that recovery path. Suspend now quiesces GF1 PCM, aborts queued GF1 DMA work, resets the UART and powers the chip down without tearing down allocator, timer or rawmidi bookkeeping. Resume rebuilds the GF1 hardware state, restores timer and UART handlers, and brings the chip back to a usable post-resume state for the ISA front-ends. The scope is limited to restoring post-resume usability. It does not attempt transparent continuation of active GF1 PCM or synth state across suspend, and userspace may still need to reprepare streams or reload onboard sample data after resume. Open rawmidi substreams are restored only to a usable post-resume state. Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260406-b4-alsa-gus-isa-pm-v1-1-b6829a7457cd@gmail.com --- include/sound/gus.h | 8 ++++++ sound/isa/gus/gus_dma.c | 33 +++++++++++++++++++++++++ sound/isa/gus/gus_main.c | 36 +++++++++++++++++++++++++++ sound/isa/gus/gus_pcm.c | 7 +++--- sound/isa/gus/gus_reset.c | 62 +++++++++++++++++++++++++++++++++++++++-------- sound/isa/gus/gus_timer.c | 14 +++++++++++ sound/isa/gus/gus_uart.c | 47 +++++++++++++++++++++++++++++++++++ 7 files changed, 194 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/sound/gus.h b/include/sound/gus.h index 321ae93625eb..3feb42627de1 100644 --- a/include/sound/gus.h +++ b/include/sound/gus.h @@ -536,6 +536,7 @@ int snd_gf1_dma_transfer_block(struct snd_gus_card * gus, struct snd_gf1_dma_block * block, int atomic, int synth); +void snd_gf1_dma_suspend(struct snd_gus_card *gus); /* gus_volume.c */ @@ -552,6 +553,8 @@ struct snd_gus_voice *snd_gf1_alloc_voice(struct snd_gus_card * gus, int type, i void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice); int snd_gf1_start(struct snd_gus_card * gus); int snd_gf1_stop(struct snd_gus_card * gus); +int snd_gf1_suspend(struct snd_gus_card *gus); +int snd_gf1_resume(struct snd_gus_card *gus); /* gus_mixer.c */ @@ -572,6 +575,8 @@ int snd_gus_create(struct snd_card *card, int effect, struct snd_gus_card ** rgus); int snd_gus_initialize(struct snd_gus_card * gus); +int snd_gus_suspend(struct snd_gus_card *gus); +int snd_gus_resume(struct snd_gus_card *gus); /* gus_irq.c */ @@ -583,6 +588,8 @@ void snd_gus_irq_profile_init(struct snd_gus_card *gus); /* gus_uart.c */ int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device); +void snd_gf1_uart_suspend(struct snd_gus_card *gus); +void snd_gf1_uart_resume(struct snd_gus_card *gus); /* gus_dram.c */ int snd_gus_dram_write(struct snd_gus_card *gus, char __user *ptr, @@ -593,5 +600,6 @@ int snd_gus_dram_read(struct snd_gus_card *gus, char __user *ptr, /* gus_timer.c */ void snd_gf1_timers_init(struct snd_gus_card *gus); void snd_gf1_timers_done(struct snd_gus_card *gus); +void snd_gf1_timers_resume(struct snd_gus_card *gus); #endif /* __SOUND_GUS_H */ diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c index ffc69e26227e..30bd76eee96e 100644 --- a/sound/isa/gus/gus_dma.c +++ b/sound/isa/gus/gus_dma.c @@ -173,6 +173,39 @@ int snd_gf1_dma_done(struct snd_gus_card * gus) return 0; } +void snd_gf1_dma_suspend(struct snd_gus_card *gus) +{ + struct snd_gf1_dma_block *block; + + guard(mutex)(&gus->dma_mutex); + if (!gus->gf1.dma_shared) + return; + + snd_dma_disable(gus->gf1.dma1); + snd_gf1_dma_ack(gus); + if (gus->gf1.dma_ack) + gus->gf1.dma_ack(gus, gus->gf1.dma_private_data); + gus->gf1.dma_ack = NULL; + gus->gf1.dma_private_data = NULL; + + while ((block = gus->gf1.dma_data_pcm)) { + gus->gf1.dma_data_pcm = block->next; + if (block->ack) + block->ack(gus, block->private_data); + kfree(block); + } + while ((block = gus->gf1.dma_data_synth)) { + gus->gf1.dma_data_synth = block->next; + if (block->ack) + block->ack(gus, block->private_data); + kfree(block); + } + + gus->gf1.dma_data_pcm_last = NULL; + gus->gf1.dma_data_synth_last = NULL; + gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER; +} + int snd_gf1_dma_transfer_block(struct snd_gus_card * gus, struct snd_gf1_dma_block * __block, int atomic, diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c index b2b189c83569..6adf8b698e2b 100644 --- a/sound/isa/gus/gus_main.c +++ b/sound/isa/gus/gus_main.c @@ -404,6 +404,42 @@ int snd_gus_initialize(struct snd_gus_card *gus) return 0; } +int snd_gus_suspend(struct snd_gus_card *gus) +{ + int err; + + if (gus->pcm) { + err = snd_pcm_suspend_all(gus->pcm); + if (err < 0) + return err; + } + + err = snd_gf1_suspend(gus); + if (err < 0) + return err; + + snd_power_change_state(gus->card, SNDRV_CTL_POWER_D3hot); + return 0; +} +EXPORT_SYMBOL(snd_gus_suspend); + +int snd_gus_resume(struct snd_gus_card *gus) +{ + int err; + + err = snd_gus_init_dma_irq(gus, 1); + if (err < 0) + return err; + + err = snd_gf1_resume(gus); + if (err < 0) + return err; + + snd_power_change_state(gus->card, SNDRV_CTL_POWER_D0); + return 0; +} +EXPORT_SYMBOL(snd_gus_resume); + /* gus_io.c */ EXPORT_SYMBOL(snd_gf1_delay); EXPORT_SYMBOL(snd_gf1_write8); diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index caf371897b78..a0757e1ede46 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -471,7 +471,8 @@ static int snd_gf1_pcm_playback_trigger(struct snd_pcm_substream *substream, if (cmd == SNDRV_PCM_TRIGGER_START) { snd_gf1_pcm_trigger_up(substream); - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + } else if (cmd == SNDRV_PCM_TRIGGER_STOP || + cmd == SNDRV_PCM_TRIGGER_SUSPEND) { scoped_guard(spinlock, &pcmp->lock) { pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE; } @@ -558,7 +559,8 @@ static int snd_gf1_pcm_capture_trigger(struct snd_pcm_substream *substream, if (cmd == SNDRV_PCM_TRIGGER_START) { val = gus->gf1.pcm_rcntrl_reg; - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + } else if (cmd == SNDRV_PCM_TRIGGER_STOP || + cmd == SNDRV_PCM_TRIGGER_SUSPEND) { val = 0; } else { return -EINVAL; @@ -856,4 +858,3 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) return 0; } - diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c index a7a3e764bb77..998fa245708c 100644 --- a/sound/isa/gus/gus_reset.c +++ b/sound/isa/gus/gus_reset.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -263,11 +264,18 @@ void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice) private_free(voice); } -/* - * call this function only by start of driver - */ +static void snd_gf1_init_software_state(struct snd_gus_card *gus) +{ + unsigned int i; -int snd_gf1_start(struct snd_gus_card * gus) + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL); + for (i = 0; i < 32; i++) { + gus->gf1.voices[i].number = i; + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i); + } +} + +static void snd_gf1_hw_start(struct snd_gus_card *gus, bool initial) { unsigned int i; @@ -277,14 +285,14 @@ int snd_gf1_start(struct snd_gus_card * gus) udelay(160); snd_gf1_i_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); - snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL); - for (i = 0; i < 32; i++) { - gus->gf1.voices[i].number = i; - snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i); + if (initial) { + snd_gf1_init_software_state(gus); + snd_gf1_uart_cmd(gus, 0x03); + } else { + guard(spinlock_irqsave)(&gus->uart_cmd_lock); + outb(0x03, GUSP(gus, MIDICTRL)); } - snd_gf1_uart_cmd(gus, 0x03); /* huh.. this cleanup took me some time... */ - if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); @@ -293,6 +301,8 @@ int snd_gf1_start(struct snd_gus_card * gus) snd_gf1_select_active_voices(gus); snd_gf1_delay(gus); gus->gf1.default_voice_address = gus->gf1.memory > 0 ? 0 : 512 - 8; + gus->gf1.hw_lfo = 0; + gus->gf1.sw_lfo = 0; /* initialize LFOs & clear LFOs memory */ if (gus->gf1.enh_mode && gus->gf1.memory) { gus->gf1.hw_lfo = 1; @@ -321,7 +331,15 @@ int snd_gf1_start(struct snd_gus_card * gus) outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); } +} +int snd_gf1_start(struct snd_gus_card *gus) +{ + /* + * Probe-time startup initializes both GF1 hardware and the + * software state that suspend/resume keeps across PM cycles. + */ + snd_gf1_hw_start(gus, true); snd_gf1_timers_init(gus); snd_gf1_look_regs(gus); snd_gf1_mem_init(gus); @@ -357,3 +375,27 @@ int snd_gf1_stop(struct snd_gus_card * gus) return 0; } + +int snd_gf1_suspend(struct snd_gus_card *gus) +{ + snd_gf1_dma_suspend(gus); + snd_gf1_uart_suspend(gus); + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); + snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); + snd_gf1_stop_voices(gus, 0, 31); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); + snd_dma_disable(gus->gf1.dma2); + + return 0; +} + +int snd_gf1_resume(struct snd_gus_card *gus) +{ + snd_gf1_hw_start(gus, false); + snd_gf1_timers_resume(gus); + snd_gf1_uart_resume(gus); + + return 0; +} diff --git a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c index e3a8847e02cf..14dcde138bc7 100644 --- a/sound/isa/gus/gus_timer.c +++ b/sound/isa/gus/gus_timer.c @@ -178,3 +178,17 @@ void snd_gf1_timers_done(struct snd_gus_card * gus) gus->gf1.timer2 = NULL; } } + +void snd_gf1_timers_resume(struct snd_gus_card *gus) +{ + if (gus->gf1.timer1) { + gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1; + if (gus->gf1.timer_enabled & 4) + snd_gf1_timer1_start(gus->gf1.timer1); + } + if (gus->gf1.timer2) { + gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2; + if (gus->gf1.timer_enabled & 8) + snd_gf1_timer2_start(gus->gf1.timer2); + } +} diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c index 770d8f3e4cff..25057a5a81b0 100644 --- a/sound/isa/gus/gus_uart.c +++ b/sound/isa/gus/gus_uart.c @@ -232,3 +232,50 @@ int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device) gus->midi_uart = rmidi; return err; } + +void snd_gf1_uart_suspend(struct snd_gus_card *gus) +{ + guard(spinlock_irqsave)(&gus->uart_cmd_lock); + outb(0x03, GUSP(gus, MIDICTRL)); +} + +void snd_gf1_uart_resume(struct snd_gus_card *gus) +{ + unsigned short uart_cmd; + bool active; + int i; + + scoped_guard(spinlock_irqsave, &gus->uart_cmd_lock) { + active = gus->midi_substream_input || gus->midi_substream_output; + } + if (!active) + return; + + /* snd_gf1_hw_start() already left MIDICTRL in reset. */ + usleep_range(160, 200); + + guard(spinlock_irqsave)(&gus->uart_cmd_lock); + if (!gus->midi_substream_input && !gus->midi_substream_output) + return; + + if (gus->midi_substream_output) + gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out; + if (gus->midi_substream_input) + gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in; + + if (!gus->uart_enable) + return; + + uart_cmd = gus->gf1.uart_cmd; + snd_gf1_uart_cmd(gus, 0x00); + + if (gus->midi_substream_input) { + for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++) + snd_gf1_uart_get(gus); + if (i >= 1000) + dev_err(gus->card->dev, + "gus midi uart resume - cleanup error\n"); + } + + snd_gf1_uart_cmd(gus, uart_cmd); +} -- cgit v1.2.3 From d78ddeb8938a366aabfabf60255c1a94de8d8ea1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 6 Apr 2026 05:51:51 +0000 Subject: ASoC: soc.h: remove unused card->pmdown_time commit f0fba2ad1b6b ("ASoC: multi-component - ASoC Multi-Component Support") has replaced "card->pmdown_time" to "rtd->pmdown_time". card->pmdown_time has been not used this 15 years. Let's remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87eckstz49.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index f70edd9c23b2..5e3eb617d832 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1027,8 +1027,6 @@ struct snd_soc_card { void (*remove_dai_link)(struct snd_soc_card *, struct snd_soc_dai_link *link); - long pmdown_time; - /* CPU <--> Codec DAI links */ struct snd_soc_dai_link *dai_link; /* predefined links only */ int num_links; /* predefined links only */ -- cgit v1.2.3 From 19cbb3e0c27f28feb7781641994226cb2ee206a2 Mon Sep 17 00:00:00 2001 From: Cássio Gabriel Date: Tue, 7 Apr 2026 12:35:42 -0300 Subject: ALSA: tea6330t: add mixer state restore helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The InterWave STB variant uses a TEA6330T mixer on its private I2C bus. The mixer state is cached in software, but there is no helper to push that register image back to hardware after system resume. Add a small restore helper that reapplies the cached TEA6330T register image to the device so board drivers can restore the external mixer state as part of their PM resume path. Take snd_i2c_lock() around the full device lookup and restore sequence so the bus device list traversal is also protected. Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260407-alsa-interwave-pm-v2-2-8dd96c6129e9@gmail.com --- include/sound/tea6330t.h | 1 + sound/i2c/tea6330t.c | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/tea6330t.h b/include/sound/tea6330t.h index 1c77b78f6533..3a34033d2aa3 100644 --- a/include/sound/tea6330t.h +++ b/include/sound/tea6330t.h @@ -12,5 +12,6 @@ int snd_tea6330t_detect(struct snd_i2c_bus *bus, int equalizer); int snd_tea6330t_update_mixer(struct snd_card *card, struct snd_i2c_bus *bus, int equalizer, int fader); +int snd_tea6330t_restore_mixer(struct snd_i2c_bus *bus); #endif /* __SOUND_TEA6330T_H */ diff --git a/sound/i2c/tea6330t.c b/sound/i2c/tea6330t.c index 5341907b85d1..39c5e87c6ab0 100644 --- a/sound/i2c/tea6330t.c +++ b/sound/i2c/tea6330t.c @@ -356,5 +356,42 @@ int snd_tea6330t_update_mixer(struct snd_card *card, snd_i2c_device_free(device); return err; } - EXPORT_SYMBOL(snd_tea6330t_update_mixer); + +int snd_tea6330t_restore_mixer(struct snd_i2c_bus *bus) +{ + struct snd_i2c_device *device; + struct tea6330t *tea; + unsigned char bytes[7]; + unsigned int idx; + int err; + + if (!bus) + return -EINVAL; + + snd_i2c_lock(bus); + list_for_each_entry(device, &bus->devices, list) { + if (device->addr != TEA6330T_ADDR) + continue; + + tea = device->private_data; + if (!tea) { + err = -EINVAL; + goto unlock; + } + + bytes[0] = TEA6330T_SADDR_VOLUME_LEFT; + for (idx = 0; idx < 6; idx++) + bytes[idx + 1] = tea->regs[idx]; + err = snd_i2c_sendbytes(device, bytes, 7); + err = err < 0 ? err : 0; + goto unlock; + } + + err = -ENODEV; + +unlock: + snd_i2c_unlock(bus); + return err; +} +EXPORT_SYMBOL(snd_tea6330t_restore_mixer); -- cgit v1.2.3 From b0762dd2fcab5b8b4b953314f3f6eb9d92bc16bc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 11:38:14 +0200 Subject: ALSA: hda: Add sync version of snd_hda_codec_write() We used snd_hda_codec_read() for the verb write when a synchronization is needed after the write, e.g. for the power state toggle or such cases. It works in principle, but it looks rather confusing and too hackish. For improving the code readability, introduce a new helper function, snd_hda_codec_write_sync(), which is another variant of snd_hda_codec_write(), and replace the existing snd_hda_codec_read() calls with this one. No behavior change but just the code refactoring. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260409093826.1317626-2-tiwai@suse.de --- include/sound/hda_codec.h | 11 +++++++++++ sound/hda/codecs/generic.c | 2 +- sound/hda/codecs/hdmi/intelhdmi.c | 2 +- sound/hda/codecs/realtek/realtek.c | 3 +-- sound/hda/codecs/sigmatel.c | 4 ++-- sound/hda/common/codec.c | 11 +++++------ 6 files changed, 21 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 5d9f0ef228af..292d6024388b 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -336,6 +336,17 @@ snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, return snd_hdac_codec_write(&codec->core, nid, flags, verb, parm); } +/* sync after write */ +static inline int +snd_hda_codec_write_sync(struct hda_codec *codec, hda_nid_t nid, int flags, + unsigned int verb, unsigned int parm) +{ + /* use snd_hda_codec_read() for writing; + * the returned value is usually discarded + */ + return snd_hdac_codec_read(&codec->core, nid, flags, verb, parm); +} + #define snd_hda_param_read(codec, nid, param) \ snd_hdac_read_parm(&(codec)->core, nid, param) #define snd_hda_get_sub_nodes(codec, nid, start_nid) \ diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c index 092428ada29d..660a9f2c0ded 100644 --- a/sound/hda/codecs/generic.c +++ b/sound/hda/codecs/generic.c @@ -863,7 +863,7 @@ static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid) { if (nid) { msleep(10); - snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + snd_hda_codec_write_sync(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); } } diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c index 9460c8db39a9..6a7882544ab7 100644 --- a/sound/hda/codecs/hdmi/intelhdmi.c +++ b/sound/hda/codecs/hdmi/intelhdmi.c @@ -85,7 +85,7 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, } } - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); + snd_hda_codec_write_sync(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); snd_hda_codec_set_power_to_all(codec, fg, power_state); } diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c index b240f13b0438..39a1ead3b743 100644 --- a/sound/hda/codecs/realtek/realtek.c +++ b/sound/hda/codecs/realtek/realtek.c @@ -411,9 +411,8 @@ void alc_headset_mic_no_shutup(struct hda_codec *codec) return; snd_array_for_each(&codec->init_pins, i, pin) { - /* use read here for syncing after issuing each verb */ if (pin->nid != mic_pin) - snd_hda_codec_read(codec, pin->nid, 0, + snd_hda_codec_write_sync(codec, pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); } diff --git a/sound/hda/codecs/sigmatel.c b/sound/hda/codecs/sigmatel.c index acbbc7c3508b..4ff80a65168f 100644 --- a/sound/hda/codecs/sigmatel.c +++ b/sound/hda/codecs/sigmatel.c @@ -311,12 +311,12 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_read(codec, fg, 0, + snd_hda_codec_write_sync(codec, fg, 0, AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ msleep(1); - snd_hda_codec_read(codec, fg, 0, + snd_hda_codec_write_sync(codec, fg, 0, AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ } diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 5123df32ad89..3ac4bf6005d6 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -606,9 +606,8 @@ void snd_hda_shutup_pins(struct hda_codec *codec) if (codec->bus->shutdown) return; snd_array_for_each(&codec->init_pins, i, pin) { - /* use read here for syncing after issuing each verb */ - snd_hda_codec_read(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write_sync(codec, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); } codec->pins_shutup = 1; } @@ -2794,9 +2793,9 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, if (codec->power_filter) state = codec->power_filter(codec, fg, state); if (state == power_state || power_state != AC_PWRST_D3) - snd_hda_codec_read(codec, fg, flags, - AC_VERB_SET_POWER_STATE, - state); + snd_hda_codec_write_sync(codec, fg, flags, + AC_VERB_SET_POWER_STATE, + state); snd_hda_codec_set_power_to_all(codec, fg, power_state); } state = snd_hda_sync_power_state(codec, fg, power_state); -- cgit v1.2.3 From cd8fd5a0566e0d93fbd408e6b06ca484a78b5ccd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 11:38:15 +0200 Subject: ALSA: hda: Add a simple GPIO setup helper function Introduce a common GPIO setup helper function, so that we can clean up the open code found in many codec drivers later. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260409093826.1317626-3-tiwai@suse.de --- include/sound/hda_codec.h | 4 ++++ sound/hda/common/codec.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'include') diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 292d6024388b..24581080e26a 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -481,6 +481,10 @@ void snd_hda_unlock_devices(struct hda_bus *bus); void snd_hda_bus_reset(struct hda_bus *bus); void snd_hda_bus_reset_codecs(struct hda_bus *bus); +void snd_hda_codec_set_gpio(struct hda_codec *codec, unsigned int mask, + unsigned int dir, unsigned int data, + unsigned int delay); + int snd_hda_codec_set_name(struct hda_codec *codec, const char *name); /* diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 3ac4bf6005d6..c2af2511a831 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -4052,6 +4052,35 @@ void snd_hda_bus_reset_codecs(struct hda_bus *bus) } } +/** + * snd_hda_codec_set_gpio - Set up GPIO bits for AFG + * @codec: the HDA codec + * @mask: GPIO bitmask + * @dir: GPIO direction bits + * @data: GPIO data bits + * @delay: the delay in msec before writing GPIO data bits + */ +void snd_hda_codec_set_gpio(struct hda_codec *codec, unsigned int mask, + unsigned int dir, unsigned int data, + unsigned int delay) +{ + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_MASK, mask); + if (delay) { + snd_hda_codec_write_sync(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DIRECTION, dir); + msleep(delay); + snd_hda_codec_write_sync(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, data); + } else { + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DIRECTION, dir); + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, data); + } +} +EXPORT_SYMBOL_GPL(snd_hda_codec_set_gpio); + /** * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer * @pcm: PCM caps bits -- cgit v1.2.3 From 0cb7aa965ad02e90ba7d6bf847f3de07e8d0c05e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 9 Apr 2026 23:39:31 +0200 Subject: ASoC: uda1380: Modernize the driver This codec driver depended on the legacy GPIO API, and nothing in the kernel is defining the platform data, so get rid of this. Two in-kernel device trees are defining this codec using undocumented device tree properties, so support these for now. The same properties can be defined using software nodes if board files are desired. The device tree use the "-gpio" rather than "-gpios" suffix but the GPIO DT parser will deal with that. Since there may be out of tree users, migrate to GPIO descriptors, drop the platform data that is unused, and assign the dac_clk the value that was used in all platforms found in a historical dig, and support setting the clock to the PLL using the undocumented device tree property. Add some menuconfig so the codec can be selected and tested. Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260409-asoc-uda1380-v3-1-b3d5a53f31be@kernel.org Signed-off-by: Mark Brown --- include/sound/uda1380.h | 19 -------------- sound/soc/codecs/Kconfig | 6 +++-- sound/soc/codecs/uda1380.c | 65 ++++++++++++++++++++++++---------------------- 3 files changed, 38 insertions(+), 52 deletions(-) delete mode 100644 include/sound/uda1380.h (limited to 'include') diff --git a/include/sound/uda1380.h b/include/sound/uda1380.h deleted file mode 100644 index 2e42ea2d0cfd..000000000000 --- a/include/sound/uda1380.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * UDA1380 ALSA SoC Codec driver - * - * Copyright 2009 Philipp Zabel - */ - -#ifndef __UDA1380_H -#define __UDA1380_H - -struct uda1380_platform_data { - int gpio_power; - int gpio_reset; - int dac_clk; -#define UDA1380_DAC_CLK_SYSCLK 0 -#define UDA1380_DAC_CLK_WSPLL 1 -}; - -#endif /* __UDA1380_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index ca3e47db126e..cf94a1c756e0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2373,9 +2373,11 @@ config SND_SOC_UDA1342 mic inputs), stereo audio DAC, with basic audio processing. config SND_SOC_UDA1380 - tristate + tristate "Philips UDA1380 CODEC" depends on I2C - depends on GPIOLIB_LEGACY + help + The UDA1380 codec is used in the HTC Magician and on a number of + Samsung reference boards, as well as the LPC32xx series. config SND_SOC_WCD_CLASSH tristate diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 63c3ea878fcf..55c83d95bfba 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -16,16 +16,19 @@ #include #include #include -#include +#include #include #include +#include #include #include #include #include #include #include -#include + +#define UDA1380_DAC_CLK_SYSCLK 0 +#define UDA1380_DAC_CLK_WSPLL 1 #include "uda1380.h" @@ -36,6 +39,8 @@ struct uda1380_priv { struct work_struct work; struct i2c_client *i2c; u16 *reg_cache; + struct gpio_desc *power; + struct gpio_desc *reset; }; /* @@ -169,13 +174,12 @@ static void uda1380_sync_cache(struct snd_soc_component *component) static int uda1380_reset(struct snd_soc_component *component) { - struct uda1380_platform_data *pdata = component->dev->platform_data; struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(pdata->gpio_reset)) { - gpio_set_value(pdata->gpio_reset, 1); + if (uda1380->reset) { + gpiod_set_value(uda1380->reset, 1); mdelay(1); - gpio_set_value(pdata->gpio_reset, 0); + gpiod_set_value(uda1380->reset, 0); } else { u8 data[3]; @@ -608,9 +612,9 @@ static int uda1380_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); int pm = uda1380_read_reg_cache(component, UDA1380_PM); int reg; - struct uda1380_platform_data *pdata = component->dev->platform_data; switch (level) { case SND_SOC_BIAS_ON: @@ -620,8 +624,8 @@ static int uda1380_set_bias_level(struct snd_soc_component *component, break; case SND_SOC_BIAS_STANDBY: if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) { - if (gpio_is_valid(pdata->gpio_power)) { - gpio_set_value(pdata->gpio_power, 1); + if (uda1380->power) { + gpiod_set_value(uda1380->power, 1); mdelay(1); uda1380_reset(component); } @@ -631,10 +635,10 @@ static int uda1380_set_bias_level(struct snd_soc_component *component, uda1380_write(component, UDA1380_PM, 0x0); break; case SND_SOC_BIAS_OFF: - if (!gpio_is_valid(pdata->gpio_power)) + if (!uda1380->power) break; - gpio_set_value(pdata->gpio_power, 0); + gpiod_set_value(uda1380->power, 0); /* Mark mixer regs cache dirty to sync them with * codec regs on power on. @@ -713,13 +717,12 @@ static struct snd_soc_dai_driver uda1380_dai[] = { static int uda1380_probe(struct snd_soc_component *component) { - struct uda1380_platform_data *pdata =component->dev->platform_data; struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); int ret; uda1380->component = component; - if (!gpio_is_valid(pdata->gpio_power)) { + if (!uda1380->power) { ret = uda1380_reset(component); if (ret) return ret; @@ -728,7 +731,7 @@ static int uda1380_probe(struct snd_soc_component *component) INIT_WORK(&uda1380->work, uda1380_flush_work); /* set clock input */ - switch (pdata->dac_clk) { + switch (uda1380->dac_clk) { case UDA1380_DAC_CLK_SYSCLK: uda1380_write_reg_cache(component, UDA1380_CLK, 0); break; @@ -760,31 +763,31 @@ static const struct snd_soc_component_driver soc_component_dev_uda1380 = { static int uda1380_i2c_probe(struct i2c_client *i2c) { - struct uda1380_platform_data *pdata = i2c->dev.platform_data; + struct device *dev = &i2c->dev; struct uda1380_priv *uda1380; int ret; - if (!pdata) - return -EINVAL; - uda1380 = devm_kzalloc(&i2c->dev, sizeof(struct uda1380_priv), GFP_KERNEL); if (uda1380 == NULL) return -ENOMEM; - if (gpio_is_valid(pdata->gpio_reset)) { - ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_reset, - GPIOF_OUT_INIT_LOW, "uda1380 reset"); - if (ret) - return ret; - } - - if (gpio_is_valid(pdata->gpio_power)) { - ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_power, - GPIOF_OUT_INIT_LOW, "uda1380 power"); - if (ret) - return ret; - } + uda1380->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(uda1380->reset)) + return dev_err_probe(dev, PTR_ERR(uda1380->reset), + "error obtaining reset GPIO\n"); + gpiod_set_consumer_name(uda1380->reset, "uda1380 reset"); + + uda1380->power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(uda1380->power)) + return dev_err_probe(dev, PTR_ERR(uda1380->power), + "error obtaining power GPIO\n"); + gpiod_set_consumer_name(uda1380->power, "uda1380 power"); + + /* This is just some default */ + uda1380->dac_clk = UDA1380_DAC_CLK_SYSCLK; + if (device_property_match_string(dev, "dac-clk", "wspll") >= 0) + uda1380->dac_clk = UDA1380_DAC_CLK_WSPLL; uda1380->reg_cache = devm_kmemdup_array(&i2c->dev, uda1380_reg, ARRAY_SIZE(uda1380_reg), sizeof(uda1380_reg[0]), GFP_KERNEL); -- cgit v1.2.3