* Re: [PATCH] ASoC: sdw_utils: cs42l43: allow spk component names to be combined
From: Aaron Ma @ 2026-04-20 7:47 UTC (permalink / raw)
To: mstrozek
Cc: broonie, linux-kernel, linux-sound, patches, perex, tiwai,
yung-chuan.liao
In-Reply-To: <20260417144529.2385360-1-mstrozek@opensource.cirrus.com>
Hi Maciej,
Tested on hardware, 4x CS35L57 on Links 1&2, cs42l43 on Link 3.
Card fails to probe with -524.
In spk_rtd_init(), snd_soc_rtd_to_codec(rtd, 0)->component gets
cs35l56 (enumerates first), not cs42l43. cs35l56 has no component-level
set_sysclk, so it returns -ENOTSUPP.
Fix: use dai->component instead of snd_soc_rtd_to_codec(rtd, 0)->component.
Thanks,
Aaron
^ permalink raw reply
* RE: [PATCH v4 14/17] dmaengine: sh: rz-dmac: Add suspend to RAM support
From: Biju Das @ 2026-04-20 7:42 UTC (permalink / raw)
To: Claudiu.Beznea, vkoul@kernel.org, Frank.Li@kernel.org,
lgirdwood@gmail.com, broonie@kernel.org, perex@perex.cz,
tiwai@suse.com, Prabhakar Mahadev Lad, p.zabel@pengutronix.de,
geert+renesas@glider.be, Fabrizio Castro, Long Luu
Cc: Claudiu.Beznea, dmaengine@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-sound@vger.kernel.org,
linux-renesas-soc@vger.kernel.org, Claudiu Beznea
In-Reply-To: <20260411114303.2814115-15-claudiu.beznea.uj@bp.renesas.com>
Hi Claudiu,
> -----Original Message-----
> From: Claudiu <claudiu.beznea@tuxon.dev>
> Sent: 11 April 2026 12:43
> Subject: [PATCH v4 14/17] dmaengine: sh: rz-dmac: Add suspend to RAM support
>
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>
> The Renesas RZ/G3S SoC supports a power saving mode in which power to most of the SoC components is
> turned off, including the DMA IP. Add suspend to RAM support to save and restore the DMA IP registers.
>
> Cyclic DMA channels require special handling. Since they can be paused and resumed during system
> suspend/resume, the driver restores additional registers for these channels during the system resume
> phase. If a channel was not explicitly paused during suspend, the driver ensures that it is paused and
> resumed as part of the system suspend/resume flow. This might be the case of a serial device being used
> with no_console_suspend.
>
> For non-cyclic channels, the dev_pm_ops::prepare callback waits for all the ongoing transfers to
> complete before allowing suspend-to-RAM to proceed.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> ---
>
> Changes in v4:
> - in rz_dmac_device_synchronize() kept the read_poll_timeout() as
> this doesn't fail anymore with the proper status return from
> ->device_tx_status() API in case the channel is paused; with it
> the patch description was updated
> - keep the cleanup path in rz_dmac_suspend() simpler to avoid
> confusion when using guard()
> - used SYSTEM_SLEEP_PM_OPS() as there is no need for having the
> suspend/resume callbacks being called in NOIRQ phase
>
> Changes in v3:
> - dropped RZ_DMAC_CHAN_STATUS_SYS_SUSPENDED
> - dropped read_poll_timeout() from rz_dmac_device_synchronze() as
> with audio drivers this times out all the time on suspend because
> the audio DMA is already paused when the rz_dmac_device_synchronize()
> is called; updated the commit description to describe this change
> - call rz_dmac_device_pause_internal() only if RZ_DMAC_CHAN_STATUS_PAUSED
> bit is not set or the device is enabled in HW
> - updated rz_dmac_device_resume_set() to have it simpler and cover
> the cases when it is called with the channel enabled or paused;
> updated the comment describing the covered use cases
> - call rz_dmac_device_resume_internal() only if
> RZ_DMAC_CHAN_STATUS_PAUSED_INTERNAL bit is set
> - in rz_dmac_chan_is_enabled() return -EAGAIN only if the channel is
> enabled in HW
> - in rz_dmac_suspend_recover() drop the update of
> RZ_DMAC_CHAN_STATUS_SYS_SUSPENDED as this is not available anymore
> - in rz_dmac_suspend() call rz_dmac_device_pause_internal() unconditionally
> as the logic is now handled inside the called function; also, do not
> ignore anymore the failure of internal suspend and abort the suspend
> instead
> - report channel internal resume failures in rz_dmac_resume()
> - use rz_dmac_disable_hw() instead of open coding it in rz_dmac_resume()
> - call rz_dmac_device_resume_internal() uncoditionally as the skip
> logic is now handled in the function itself
> - use NOIRQ_SYSTEM_SLEEP_PM_OPS()
> - didn't collect Tommaso's Tb tag as the series was changed a lot since
> v2
>
> Changes in v2:
> - fixed typos in patch description
> - in rz_dmac_suspend_prepare(): return -EAGAIN based on the value returned
> by vchan_issue_pending()
> - in rz_dmac_suspend_recover(): clear RZ_DMAC_CHAN_STATUS_SYS_SUSPENDED for
> non cyclic channels
> - in rz_dmac_resume(): call rz_dmac_set_dma_req_no() only for cyclic channels
>
> drivers/dma/sh/rz-dmac.c | 188 +++++++++++++++++++++++++++++++++++++--
> 1 file changed, 183 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 9a10430109e5..00e18d8213ca
> 100644
> --- a/drivers/dma/sh/rz-dmac.c
> +++ b/drivers/dma/sh/rz-dmac.c
> @@ -69,10 +69,12 @@ struct rz_dmac_desc {
> * enum rz_dmac_chan_status: RZ DMAC channel status
> * @RZ_DMAC_CHAN_STATUS_PAUSED: Channel is paused though DMA engine callbacks
> * @RZ_DMAC_CHAN_STATUS_CYCLIC: Channel is cyclic
> + * @RZ_DMAC_CHAN_STATUS_PAUSED_INTERNAL: Channel is paused through
> + driver internal logic
> */
> enum rz_dmac_chan_status {
> RZ_DMAC_CHAN_STATUS_PAUSED,
> RZ_DMAC_CHAN_STATUS_CYCLIC,
> + RZ_DMAC_CHAN_STATUS_PAUSED_INTERNAL,
> };
>
> struct rz_dmac_chan {
> @@ -92,6 +94,10 @@ struct rz_dmac_chan {
> u32 chctrl;
> int mid_rid;
>
> + struct {
> + u32 nxla;
> + } pm_state;
> +
> struct list_head ld_free;
>
> struct {
> @@ -962,20 +968,57 @@ static int rz_dmac_device_pause(struct dma_chan *chan)
> return rz_dmac_device_pause_set(channel, BIT(RZ_DMAC_CHAN_STATUS_PAUSED)); }
>
> +static int rz_dmac_device_pause_internal(struct rz_dmac_chan *channel)
> +{
> + lockdep_assert_held(&channel->vc.lock);
> +
> + /* Skip channels explicitly paused by consummers or disabled. */
> + if (channel->status & BIT(RZ_DMAC_CHAN_STATUS_PAUSED) ||
> + !rz_dmac_chan_is_enabled(channel))
> + return 0;
> +
> + return rz_dmac_device_pause_set(channel,
> +BIT(RZ_DMAC_CHAN_STATUS_PAUSED_INTERNAL));
> +}
> +
> static int rz_dmac_device_resume_set(struct rz_dmac_chan *channel,
> unsigned long clear_bitmask)
> {
> - int ret = 0;
> u32 val;
> + int ret;
>
> lockdep_assert_held(&channel->vc.lock);
>
> - /* Do not check CHSTAT_SUS but rely on HW capabilities. */
> + /*
> + * We can be:
> + *
> + * 1/ after the channel was paused by a consummer and now it
> + * needs to be resummed
> + * 2/ after the channel was paused internally (as a result of
> + * a system suspend with power loss or not)
> + * 3/ after the channel was paused by a consummer, the system
> + * went through a system suspend (with power loss or not)
> + * and the consummer wants to resume the channel
> + *
> + * To cover all the above cases we set both CLRSUS and SETEN.
> + *
> + * In case 1/ setting SETEN while the channel is still enabled
> + * is harmless for the controller.
> + *
> + * In case 2/ the channel is disabled when calling this function
> + * and setting CLRSUS is harmless for the controller as the
> + * channel is disabled anyway.
> + *
> + * In case 3/ the channel is disabled/enabled if the system
> + * went though a suspend with power loss/or not and setting
> + * CLRSUS/SETEN is harmless for the controller as the channel
> + * is enabled/disabled anyway.
> + */
> +
> + rz_dmac_ch_writel(channel, CHCTRL_CLRSUS | CHCTRL_SETEN, CHCTRL, 1);
>
> - rz_dmac_ch_writel(channel, CHCTRL_CLRSUS, CHCTRL, 1);
> ret = read_poll_timeout_atomic(rz_dmac_ch_readl, val,
> - !(val & CHSTAT_SUS), 1, 1024, false,
> - channel, CHSTAT, 1);
> + ((val & (CHSTAT_SUS | CHSTAT_EN)) == CHSTAT_EN),
> + 1, 1024, false, channel, CHSTAT, 1);
>
> channel->status &= ~clear_bitmask;
>
> @@ -994,6 +1037,16 @@ static int rz_dmac_device_resume(struct dma_chan *chan)
> return rz_dmac_device_resume_set(channel, BIT(RZ_DMAC_CHAN_STATUS_PAUSED)); }
>
> +static int rz_dmac_device_resume_internal(struct rz_dmac_chan *channel)
> +{
> + lockdep_assert_held(&channel->vc.lock);
> +
> + if (!(channel->status & BIT(RZ_DMAC_CHAN_STATUS_PAUSED_INTERNAL)))
> + return 0;
> +
> + return rz_dmac_device_resume_set(channel,
> +BIT(RZ_DMAC_CHAN_STATUS_PAUSED_INTERNAL));
> +}
> +
> /*
> * -----------------------------------------------------------------------------
> * IRQ handling
> @@ -1354,6 +1407,130 @@ static void rz_dmac_remove(struct platform_device *pdev)
> pm_runtime_disable(&pdev->dev);
> }
>
> +static int rz_dmac_suspend_prepare(struct device *dev) {
> + struct rz_dmac *dmac = dev_get_drvdata(dev);
> +
> + for (unsigned int i = 0; i < dmac->n_channels; i++) {
> + struct rz_dmac_chan *channel = &dmac->channels[i];
> +
> + guard(spinlock_irqsave)(&channel->vc.lock);
> +
> + /* Wait for transfer completion, except in cyclic case. */
> + if (channel->status & BIT(RZ_DMAC_CHAN_STATUS_CYCLIC))
> + continue;
> +
> + if (rz_dmac_chan_is_enabled(channel))
> + return -EAGAIN;
> + }
> +
> + return 0;
> +}
> +
> +static void rz_dmac_suspend_recover(struct rz_dmac *dmac) {
> + for (unsigned int i = 0; i < dmac->n_channels; i++) {
> + struct rz_dmac_chan *channel = &dmac->channels[i];
> +
> + guard(spinlock_irqsave)(&channel->vc.lock);
> +
> + if (!(channel->status & BIT(RZ_DMAC_CHAN_STATUS_CYCLIC)))
> + continue;
> +
> + rz_dmac_device_resume_internal(channel);
> + }
> +}
> +
> +static int rz_dmac_suspend(struct device *dev) {
> + struct rz_dmac *dmac = dev_get_drvdata(dev);
> + int ret;
> +
> + for (unsigned int i = 0; i < dmac->n_channels; i++) {
> + struct rz_dmac_chan *channel = &dmac->channels[i];
> +
> + guard(spinlock_irqsave)(&channel->vc.lock);
> +
> + if (!(channel->status & BIT(RZ_DMAC_CHAN_STATUS_CYCLIC)))
> + continue;
> +
> + ret = rz_dmac_device_pause_internal(channel);
> + if (ret) {
> + dev_err(dev, "Failed to suspend channel %s\n",
> + dma_chan_name(&channel->vc.chan));
> + break;
> + }
> +
> + channel->pm_state.nxla = rz_dmac_ch_readl(channel, NXLA, 1);
> + }
> +
> + if (ret) {
> + rz_dmac_suspend_recover(dmac);
> + return ret;
> + }
> +
> + pm_runtime_put_sync(dmac->dev);
> +
> + ret = reset_control_assert(dmac->rstc);
> + if (ret) {
> + pm_runtime_resume_and_get(dmac->dev);
> + rz_dmac_suspend_recover(dmac);
> + }
> +
> + return ret;
> +}
> +
> +static int rz_dmac_resume(struct device *dev) {
> + struct rz_dmac *dmac = dev_get_drvdata(dev);
> + int errors = 0, ret;
> +
> + ret = reset_control_deassert(dmac->rstc);
> + if (ret)
> + return ret;
> +
> + ret = pm_runtime_resume_and_get(dmac->dev);
If this fails for any reason, the next suspend still be called and it will decrement the counter, potentially undeflowing it.
Consider switching to pm_runtime_get_sync(), which suits better here.
Cheers,
Biju
^ permalink raw reply
* Re: [PATCH] ALSA: als4000: Fix capture trigger chip->mode race
From: Takashi Iwai @ 2026-04-20 7:08 UTC (permalink / raw)
To: Cássio Gabriel
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel
In-Reply-To: <20260417-als4000-capture-trigger-race-v1-1-daeffc2feb67@gmail.com>
On Fri, 17 Apr 2026 22:30:18 +0200,
Cássio Gabriel wrote:
>
> snd_als4000_capture_trigger() updates chip->mode under mixer_lock,
> while snd_als4000_set_rate() and snd_als4000_playback_trigger()
> serialize the same rate-lock state with reg_lock.
>
> The PCM core serializes callbacks only per acted-on substream, or for an
> explicitly linked group, so unlinked playback and capture streams can
> run concurrently.
>
> That leaves two races on ALS4000 rate-lock state:
> - playback and capture trigger callbacks can concurrently update
> chip->mode and lose one of the SB_RATE_LOCK bits
> - snd_als4000_set_rate() can observe chip->mode without the capture
> lock bit set and reprogram the shared sample rate while capture is
> being started
>
> Fix this by taking reg_lock as the outer lock in
> snd_als4000_capture_trigger() and nesting mixer_lock only for the CR1E
> write. This keeps chip->mode serialized with the rest of the ALS4000
> rate-lock users while preserving the existing CR1E programming
> sequence.
>
> Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Applied now. Thanks.
Takashi
^ permalink raw reply
* Re: [EXTERNAL] Re: [PATCH v1] ALSA: hda/tas2781: Fix sound abnormal issue on some SPI device
From: Xu, Baojun @ 2026-04-20 7:00 UTC (permalink / raw)
To: Eric Naim, tiwai@suse.de, hansg@kernel.org
Cc: ilpo.jarvinen@linux.intel.com, broonie@kernel.org,
andriy.shevchenko@linux.intel.com, Ding, Shenghao,
13916275206@139.com, platform-driver-x86@vger.kernel.org,
linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org,
letitia.tsai@hp.com, bill.yu@canonical.com
In-Reply-To: <26976b47-8886-4f3c-bdbd-c15e6ac7c44c@cachyos.org>
Hi Eric,
>
> ________________________________________
> From: Eric Naim <dnaim@cachyos.org>
> Sent: 20 April 2026 14:43
> To: Xu, Baojun; tiwai@suse.de; hansg@kernel.org
> Cc: ilpo.jarvinen@linux.intel.com; broonie@kernel.org; andriy.shevchenko@linux.intel.com; Ding, Shenghao; 13916275206@139.com; platform-driver-x86@vger.kernel.org; linux-sound@vger.kernel.org; linux-kernel@vger.kernel.org; letitia.tsai@hp.com; bill.yu@canonical.com
> Subject: [EXTERNAL] Re: [PATCH v1] ALSA: hda/tas2781: Fix sound abnormal issue on some SPI device
>
> On 4/18/26 1:50 PM, Baojun Xu wrote:
> > In the SPI driver probe, the chip ID must be set to TAS2781. Without this
> > initialization, calibration data fails to load correctly, causing audio
> > abnormalities on some devices.
> > And update the register bulk read API to handle the distinct requirements
> > of SPI and I2C devices.
> >
> > Fixes: 05ac3846ffe5 ("ALSA: hda/tas2781: A workaround solution to lower-vol issue among lower calibrated-impedance micro-speaker on TAS2781")
>> Signed-off-by: Baojun Xu <baojun.xu@ti.com>
> > ---
> Hi Baojun, will this fix
> https://urldefense.com/v3/__https://lore.kernel.org/linux-sound/20260315160939.191265-1-dnaim@cachyos.org/__;!!G3vK!X-jfopjNDyUV4Zt7eHbLDT79zxX8PrVATeyHGzNtgCNvue6wSwqHWSaaVOuqjRfYbzZ5TQzNVhRq$ ?
>
> Similarly, I noticed that something like the above was merged upstream, commit
> 0bdf27abaf894.
>
To clarify: The patch mentioned earlier was designed to register the correct project with the codec.
My patch specifically resolves the issue where calibration data fails to be applied properly.
> --
> Regards,
> Eric
>
>
Best Regards
Jim
^ permalink raw reply
* Re: [PATCH v1] ALSA: hda/tas2781: Fix sound abnormal issue on some SPI device
From: Eric Naim @ 2026-04-20 6:43 UTC (permalink / raw)
To: Baojun Xu, tiwai, hansg
Cc: ilpo.jarvinen, broonie, andriy.shevchenko, shenghao-ding,
13916275206, platform-driver-x86, linux-sound, linux-kernel,
letitia.tsai, bill.yu
In-Reply-To: <20260418055030.765-1-baojun.xu@ti.com>
On 4/18/26 1:50 PM, Baojun Xu wrote:
> In the SPI driver probe, the chip ID must be set to TAS2781. Without this
> initialization, calibration data fails to load correctly, causing audio
> abnormalities on some devices.
> And update the register bulk read API to handle the distinct requirements
> of SPI and I2C devices.
>
> Fixes: 05ac3846ffe5 ("ALSA: hda/tas2781: A workaround solution to lower-vol issue among lower calibrated-impedance micro-speaker on TAS2781")
> Signed-off-by: Baojun Xu <baojun.xu@ti.com>
> ---
> sound/hda/codecs/side-codecs/tas2781_hda_spi.c | 1 +
> sound/soc/codecs/tas2781-fmwlib.c | 7 +++----
> 2 files changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
> index f860e0eb7602..560f2385212d 100644
> --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
> +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
> @@ -788,6 +788,7 @@ static int tas2781_hda_spi_probe(struct spi_device *spi)
> }
> if (strstr(dev_name(&spi->dev), "TXNW2781")) {
> device_name = "TXNW2781";
> + tas_hda->priv->chip_id = TAS2781;
> } else {
> dev_err(tas_priv->dev, "Unmatched spi dev %s\n",
> dev_name(&spi->dev));
> diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
> index a1d86bd309f4..885e0b6fed00 100644
> --- a/sound/soc/codecs/tas2781-fmwlib.c
> +++ b/sound/soc/codecs/tas2781-fmwlib.c
> @@ -2487,7 +2487,7 @@ static int tas2781_cali_preproc(struct tasdevice_priv *priv, int i)
> if (spec == NULL)
> return -ENOMEM;
> priv->tasdevice[i].cali_specific = spec;
> - rc = tasdevice_dev_bulk_read(priv, i, p->r0_reg, r0_deflt, 4);
> + rc = priv->dev_bulk_read(priv, i, p->r0_reg, r0_deflt, 4);
> if (rc < 0) {
> dev_err(priv->dev, "invalid RE from %d = %d\n", i, rc);
> return rc;
> @@ -2511,9 +2511,8 @@ static int tas2781_cali_preproc(struct tasdevice_priv *priv, int i)
> TASDEVICE_REG(0, 0x1b, 0x34) :
> TASDEVICE_REG(0, 0x18, 0x1c);
>
> - rc = tasdevice_dev_bulk_read(priv, i,
> - spec->sin_gni_reg,
> - spec->sin_gni, 4);
> + rc = priv->dev_bulk_read(priv, i, spec->sin_gni_reg,
> + spec->sin_gni, 4);
> if (rc < 0) {
> dev_err(priv->dev, "wrong sinegaini %d = %d\n",
> i, rc);
Hi Baojun, will this fix
https://lore.kernel.org/linux-sound/20260315160939.191265-1-dnaim@cachyos.org/ ?
Similarly, I noticed that something like the above was merged upstream, commit
0bdf27abaf894.
--
Regards,
Eric
^ permalink raw reply
* [PATCH] ALSA: core: Fix potential data race at fasync handling
From: Takashi Iwai @ 2026-04-20 6:17 UTC (permalink / raw)
To: linux-sound; +Cc: linux-kernel, Jake Lamberson
In snd_fasync_work_fn(), which is the offload work for traversing and
processing the pending fasync list, the call of kill_fasync() is done
outside the snd_fasync_lock for avoiding deadlocks. The problem is
that its the references of fasync->on, fasync->signal and fasync->poll
are done there also outside the lock. Since these may be modified by
snd_kill_fasync() call concurrently from other process, inconsistent
values might be passed to kill_fasync(). Although there shouldn't be
critical UAF, it's still better to be addressed.
This patch moves the kill_fasync() argument evaluations inside the
snd_fasync_lock for avoiding the data races above. The handling in
fasync->on flag is optimized in the loop to skip directly.
Also, for more clarity, snd_fasync_free() takes the lock and unlink
the pending entry more directly instead of clearing fasync->on flag.
Reported-by: Jake Lamberson <lamberson.jake@gmail.com>
Fixes: ef34a0ae7a26 ("ALSA: core: Add async signal helpers")
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
sound/core/misc.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/sound/core/misc.c b/sound/core/misc.c
index 88d9e1f9a6e9..5aca09edf971 100644
--- a/sound/core/misc.c
+++ b/sound/core/misc.c
@@ -100,14 +100,18 @@ static LIST_HEAD(snd_fasync_list);
static void snd_fasync_work_fn(struct work_struct *work)
{
struct snd_fasync *fasync;
+ int signal, poll;
spin_lock_irq(&snd_fasync_lock);
while (!list_empty(&snd_fasync_list)) {
fasync = list_first_entry(&snd_fasync_list, struct snd_fasync, list);
list_del_init(&fasync->list);
+ if (!fasync->on)
+ continue;
+ signal = fasync->signal;
+ poll = fasync->poll;
spin_unlock_irq(&snd_fasync_lock);
- if (fasync->on)
- kill_fasync(&fasync->fasync, fasync->signal, fasync->poll);
+ kill_fasync(&fasync->fasync, signal, poll);
spin_lock_irq(&snd_fasync_lock);
}
spin_unlock_irq(&snd_fasync_lock);
@@ -158,7 +162,10 @@ void snd_fasync_free(struct snd_fasync *fasync)
{
if (!fasync)
return;
- fasync->on = 0;
+
+ scoped_guard(spinlock_irq, &snd_fasync_lock)
+ list_del_init(&fasync->list);
+
flush_work(&snd_fasync_work);
kfree(fasync);
}
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v1] ALSA: hda/tas2781: Fix sound abnormal issue on some SPI device
From: Takashi Iwai @ 2026-04-20 6:15 UTC (permalink / raw)
To: Baojun Xu
Cc: tiwai, hansg, ilpo.jarvinen, broonie, andriy.shevchenko,
shenghao-ding, 13916275206, platform-driver-x86, linux-sound,
linux-kernel, letitia.tsai, bill.yu
In-Reply-To: <20260418055030.765-1-baojun.xu@ti.com>
On Sat, 18 Apr 2026 07:50:30 +0200,
Baojun Xu wrote:
>
> In the SPI driver probe, the chip ID must be set to TAS2781. Without this
> initialization, calibration data fails to load correctly, causing audio
> abnormalities on some devices.
> And update the register bulk read API to handle the distinct requirements
> of SPI and I2C devices.
>
> Fixes: 05ac3846ffe5 ("ALSA: hda/tas2781: A workaround solution to lower-vol issue among lower calibrated-impedance micro-speaker on TAS2781")
> Signed-off-by: Baojun Xu <baojun.xu@ti.com>
Applied now. Thanks.
Takashi
^ permalink raw reply
* Re: [PATCH] ALSA: hda/realtek: add quirk for Acer Nitro 16 AN16-41
From: Takashi Iwai @ 2026-04-20 6:14 UTC (permalink / raw)
To: songxiebing; +Cc: tiwai, perex, linux-sound, linux-kernel, Yenilmez99
In-Reply-To: <20260420053351.547352-1-songxiebing@kylinos.cn>
On Mon, 20 Apr 2026 07:33:51 +0200,
songxiebing wrote:
>
> From: Bob Song <songxiebing@kylinos.cn>
>
> The combo jack microphone is not detected/working on the laptop featuring
> the Realtek ALC245 codec, and mic pincfg is the default value.
>
> So here, add quirk for it and test good.
>
> Reported-by: Yenilmez99 <mehmetcanosma77@gmail.com>
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221344
> Signed-off-by: Bob Song <songxiebing@kylinos.cn>
Thanks, applied now.
Takashi
^ permalink raw reply
* [PATCH] ALSA: hda/realtek: add quirk for Acer Nitro 16 AN16-41
From: songxiebing @ 2026-04-20 5:33 UTC (permalink / raw)
To: tiwai, perex; +Cc: linux-sound, linux-kernel, songxiebing, Yenilmez99
From: Bob Song <songxiebing@kylinos.cn>
The combo jack microphone is not detected/working on the laptop featuring
the Realtek ALC245 codec, and mic pincfg is the default value.
So here, add quirk for it and test good.
Reported-by: Yenilmez99 <mehmetcanosma77@gmail.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221344
Signed-off-by: Bob Song <songxiebing@kylinos.cn>
---
sound/hda/codecs/realtek/alc269.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index 37241a72b5af..3cd7310aee9c 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -6703,6 +6703,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED),
SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED),
SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
--
2.25.1
^ permalink raw reply related
* [PATCH v2 9/9] ARM: dts: mediatek: mt7623n-bananapi-bpi-r2: add HDMI audio machine node
From: Daniel Golle @ 2026-04-20 1:14 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Arnd Bergmann, Cyril Chao,
Daniel Golle, Nícolas F. R. A. Prado, Kuninori Morimoto,
Eugen Hristev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
In-Reply-To: <cover.1776646435.git.daniel@makrotopia.org>
Instantiate the mediatek,mt2701-hdmi-audio machine on the
BananaPi BPI-R2, binding the AFE HDMI playback path to the
on-chip HDMI transmitter acting as the generic HDMI codec.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2: no changes
arch/arm/boot/dts/mediatek/mt7623n-bananapi-bpi-r2.dts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/boot/dts/mediatek/mt7623n-bananapi-bpi-r2.dts b/arch/arm/boot/dts/mediatek/mt7623n-bananapi-bpi-r2.dts
index a37f3fa223c72..139a76764faa0 100644
--- a/arch/arm/boot/dts/mediatek/mt7623n-bananapi-bpi-r2.dts
+++ b/arch/arm/boot/dts/mediatek/mt7623n-bananapi-bpi-r2.dts
@@ -132,6 +132,13 @@ memory@80000000 {
device_type = "memory";
reg = <0 0x80000000 0 0x80000000>;
};
+
+ sound-hdmi {
+ compatible = "mediatek,mt7623n-hdmi-audio",
+ "mediatek,mt2701-hdmi-audio";
+ mediatek,platform = <&afe>;
+ mediatek,audio-codec = <&hdmi0>;
+ };
};
&bls {
--
2.53.0
^ permalink raw reply related
* [PATCH v2 8/9] ARM: dts: mediatek: mt7623: wire HDMI audio path clocks into AFE
From: Daniel Golle @ 2026-04-20 1:13 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Arnd Bergmann, Cyril Chao,
Daniel Golle, Nícolas F. R. A. Prado, Kuninori Morimoto,
Eugen Hristev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
In-Reply-To: <cover.1776646435.git.daniel@makrotopia.org>
Mirror the MT2701 change for the MT7623 SoC dtsi: add HADDS2PLL,
audio_hdmi, audio_spdf and audio_apll to the AFE clocks list and
reparent the AUDPLL mux to HADDS2PLL_98M. Required for HDMI audio
on MT7623N boards via the shared mt2701 AFE driver.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2: no changes
arch/arm/boot/dts/mediatek/mt7623.dtsi | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/arch/arm/boot/dts/mediatek/mt7623.dtsi b/arch/arm/boot/dts/mediatek/mt7623.dtsi
index 71ac2b94c6ba3..4eb028ffee6f5 100644
--- a/arch/arm/boot/dts/mediatek/mt7623.dtsi
+++ b/arch/arm/boot/dts/mediatek/mt7623.dtsi
@@ -665,7 +665,11 @@ afe: audio-controller {
<&audsys CLK_AUD_AFE_CONN>,
<&audsys CLK_AUD_A1SYS>,
<&audsys CLK_AUD_A2SYS>,
- <&audsys CLK_AUD_AFE_MRGIF>;
+ <&audsys CLK_AUD_AFE_MRGIF>,
+ <&topckgen CLK_TOP_HADDS2PLL_294M>,
+ <&audsys CLK_AUD_HDMI>,
+ <&audsys CLK_AUD_SPDF>,
+ <&audsys CLK_AUD_APLL>;
clock-names = "infra_sys_audio_clk",
"top_audio_mux1_sel",
@@ -700,15 +704,22 @@ afe: audio-controller {
"audio_afe_conn_pd",
"audio_a1sys_pd",
"audio_a2sys_pd",
- "audio_mrgif_pd";
+ "audio_mrgif_pd",
+ "hadds2pll_294m",
+ "audio_hdmi_pd",
+ "audio_spdf_pd",
+ "audio_apll_pd";
assigned-clocks = <&topckgen CLK_TOP_AUD_MUX1_SEL>,
<&topckgen CLK_TOP_AUD_MUX2_SEL>,
<&topckgen CLK_TOP_AUD_MUX1_DIV>,
- <&topckgen CLK_TOP_AUD_MUX2_DIV>;
+ <&topckgen CLK_TOP_AUD_MUX2_DIV>,
+ <&topckgen CLK_TOP_AUDPLL_MUX_SEL>;
assigned-clock-parents = <&topckgen CLK_TOP_AUD1PLL_98M>,
- <&topckgen CLK_TOP_AUD2PLL_90M>;
- assigned-clock-rates = <0>, <0>, <49152000>, <45158400>;
+ <&topckgen CLK_TOP_AUD2PLL_90M>,
+ <0>, <0>,
+ <&topckgen CLK_TOP_HADDS2PLL_98M>;
+ assigned-clock-rates = <0>, <0>, <49152000>, <45158400>, <0>;
};
};
--
2.53.0
^ permalink raw reply related
* [PATCH v2 7/9] ARM: dts: mediatek: mt2701: wire HDMI audio path clocks into AFE
From: Daniel Golle @ 2026-04-20 1:13 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Arnd Bergmann, Cyril Chao,
Daniel Golle, Nícolas F. R. A. Prado, Kuninori Morimoto,
Eugen Hristev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
In-Reply-To: <cover.1776646435.git.daniel@makrotopia.org>
Add the HADDS2 PLL 294 MHz root, the audio_hdmi and audio_spdf
interface gates and the audio_apll gate to the MT2701 AFE node,
and reparent the AUDPLL mux to HADDS2PLL_98M so the HDMI audio
serial clock path has a stable 294.912 MHz source. The clock
names match the updated mediatek,mt2701-audio binding.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2: no changes
arch/arm/boot/dts/mediatek/mt2701.dtsi | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/arch/arm/boot/dts/mediatek/mt2701.dtsi b/arch/arm/boot/dts/mediatek/mt2701.dtsi
index 128b87229f3d5..80c8c7e6a422a 100644
--- a/arch/arm/boot/dts/mediatek/mt2701.dtsi
+++ b/arch/arm/boot/dts/mediatek/mt2701.dtsi
@@ -464,7 +464,11 @@ afe: audio-controller {
<&audsys CLK_AUD_AFE_CONN>,
<&audsys CLK_AUD_A1SYS>,
<&audsys CLK_AUD_A2SYS>,
- <&audsys CLK_AUD_AFE_MRGIF>;
+ <&audsys CLK_AUD_AFE_MRGIF>,
+ <&topckgen CLK_TOP_HADDS2PLL_294M>,
+ <&audsys CLK_AUD_HDMI>,
+ <&audsys CLK_AUD_SPDF>,
+ <&audsys CLK_AUD_APLL>;
clock-names = "infra_sys_audio_clk",
"top_audio_mux1_sel",
@@ -499,15 +503,22 @@ afe: audio-controller {
"audio_afe_conn_pd",
"audio_a1sys_pd",
"audio_a2sys_pd",
- "audio_mrgif_pd";
+ "audio_mrgif_pd",
+ "hadds2pll_294m",
+ "audio_hdmi_pd",
+ "audio_spdf_pd",
+ "audio_apll_pd";
assigned-clocks = <&topckgen CLK_TOP_AUD_MUX1_SEL>,
<&topckgen CLK_TOP_AUD_MUX2_SEL>,
<&topckgen CLK_TOP_AUD_MUX1_DIV>,
- <&topckgen CLK_TOP_AUD_MUX2_DIV>;
+ <&topckgen CLK_TOP_AUD_MUX2_DIV>,
+ <&topckgen CLK_TOP_AUDPLL_MUX_SEL>;
assigned-clock-parents = <&topckgen CLK_TOP_AUD1PLL_98M>,
- <&topckgen CLK_TOP_AUD2PLL_90M>;
- assigned-clock-rates = <0>, <0>, <49152000>, <45158400>;
+ <&topckgen CLK_TOP_AUD2PLL_90M>,
+ <0>, <0>,
+ <&topckgen CLK_TOP_HADDS2PLL_98M>;
+ assigned-clock-rates = <0>, <0>, <49152000>, <45158400>, <0>;
};
};
--
2.53.0
^ permalink raw reply related
* [PATCH v2 6/9] ASoC: mediatek: mt2701: add machine driver for on-chip HDMI codec
From: Daniel Golle @ 2026-04-20 1:13 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Arnd Bergmann, Cyril Chao,
Daniel Golle, Nícolas F. R. A. Prado, Kuninori Morimoto,
Eugen Hristev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
In-Reply-To: <cover.1776646435.git.daniel@makrotopia.org>
Add a simple ASoC machine driver that wires the MT2701/MT7623N
AFE HDMI playback path to the on-chip HDMI transmitter exposed
as a generic hdmi-audio-codec "i2s-hifi" DAI.
The driver binds to "mediatek,mt2701-hdmi-audio". MT7623N device
trees carry "mediatek,mt7623n-hdmi-audio" as a board-specific
fallback, matching the dt-binding.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2: no changes
sound/soc/mediatek/Kconfig | 10 +++
sound/soc/mediatek/mt2701/Makefile | 1 +
sound/soc/mediatek/mt2701/mt2701-hdmi.c | 114 ++++++++++++++++++++++++
3 files changed, 125 insertions(+)
create mode 100644 sound/soc/mediatek/mt2701/mt2701-hdmi.c
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 3a1e1fa3fe5cc..fa076e7854adc 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -26,6 +26,16 @@ config SND_SOC_MT2701_CS42448
Select Y if you have such device.
If unsure select "N".
+config SND_SOC_MT2701_HDMI
+ tristate "ASoC Audio driver for MT2701 with on-chip HDMI codec"
+ depends on SND_SOC_MT2701
+ select SND_SOC_HDMI_CODEC
+ help
+ This adds the ASoC machine driver for MediaTek MT2701 and
+ MT7623N boards routing the AFE I2S back-end to the on-chip
+ HDMI transmitter via the generic HDMI codec.
+ If unsure select "N".
+
config SND_SOC_MT2701_WM8960
tristate "ASoc Audio driver for MT2701 with WM8960 codec"
depends on SND_SOC_MT2701 && I2C
diff --git a/sound/soc/mediatek/mt2701/Makefile b/sound/soc/mediatek/mt2701/Makefile
index 507fa26c39452..59623d3d3a038 100644
--- a/sound/soc/mediatek/mt2701/Makefile
+++ b/sound/soc/mediatek/mt2701/Makefile
@@ -5,4 +5,5 @@ obj-$(CONFIG_SND_SOC_MT2701) += snd-soc-mt2701-afe.o
# machine driver
obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701-cs42448.o
+obj-$(CONFIG_SND_SOC_MT2701_HDMI) += mt2701-hdmi.o
obj-$(CONFIG_SND_SOC_MT2701_WM8960) += mt2701-wm8960.o
diff --git a/sound/soc/mediatek/mt2701/mt2701-hdmi.c b/sound/soc/mediatek/mt2701/mt2701-hdmi.c
new file mode 100644
index 0000000000000..a84907879c04e
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-hdmi.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt2701-hdmi.c -- MT2701 HDMI ALSA SoC machine driver
+ *
+ * Copyright (c) 2026 Daniel Golle <daniel@makrotopia.org>
+ *
+ * Based on mt2701-cs42448.c
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+
+enum {
+ DAI_LINK_FE_HDMI_OUT,
+ DAI_LINK_BE_HDMI_I2S,
+};
+
+SND_SOC_DAILINK_DEFS(fe_hdmi_out,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM_HDMI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(be_hdmi_i2s,
+ DAILINK_COMP_ARRAY(COMP_CPU("HDMI I2S")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link mt2701_hdmi_dai_links[] = {
+ [DAI_LINK_FE_HDMI_OUT] = {
+ .name = "HDMI Playback",
+ .stream_name = "HDMI Playback",
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(fe_hdmi_out),
+ },
+ [DAI_LINK_BE_HDMI_I2S] = {
+ .name = "HDMI BE",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ SND_SOC_DAILINK_REG(be_hdmi_i2s),
+ },
+};
+
+static struct snd_soc_card mt2701_hdmi_soc_card = {
+ .name = "mt2701-hdmi",
+ .owner = THIS_MODULE,
+ .dai_link = mt2701_hdmi_dai_links,
+ .num_links = ARRAY_SIZE(mt2701_hdmi_dai_links),
+};
+
+static int mt2701_hdmi_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt2701_hdmi_soc_card;
+ struct device *dev = &pdev->dev;
+ struct device_node *platform_node;
+ struct device_node *codec_node;
+ struct snd_soc_dai_link *dai_link;
+ int ret;
+ int i;
+
+ platform_node = of_parse_phandle(dev->of_node, "mediatek,platform", 0);
+ if (!platform_node)
+ return dev_err_probe(dev, -EINVAL,
+ "Property 'mediatek,platform' missing\n");
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->platforms->name)
+ continue;
+ dai_link->platforms->of_node = platform_node;
+ }
+
+ codec_node = of_parse_phandle(dev->of_node, "mediatek,audio-codec", 0);
+ if (!codec_node) {
+ of_node_put(platform_node);
+ return dev_err_probe(dev, -EINVAL,
+ "Property 'mediatek,audio-codec' missing\n");
+ }
+ mt2701_hdmi_dai_links[DAI_LINK_BE_HDMI_I2S].codecs->of_node = codec_node;
+
+ card->dev = dev;
+
+ ret = devm_snd_soc_register_card(dev, card);
+
+ of_node_put(platform_node);
+ of_node_put(codec_node);
+ return ret;
+}
+
+static const struct of_device_id mt2701_hdmi_machine_dt_match[] = {
+ { .compatible = "mediatek,mt2701-hdmi-audio" },
+ { .compatible = "mediatek,mt7623n-hdmi-audio" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt2701_hdmi_machine_dt_match);
+
+static struct platform_driver mt2701_hdmi_machine = {
+ .driver = {
+ .name = "mt2701-hdmi",
+ .of_match_table = mt2701_hdmi_machine_dt_match,
+ },
+ .probe = mt2701_hdmi_machine_probe,
+};
+module_platform_driver(mt2701_hdmi_machine);
+
+MODULE_DESCRIPTION("MT2701 HDMI ALSA SoC machine driver");
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mt2701-hdmi");
--
2.53.0
^ permalink raw reply related
* [PATCH v2 5/9] ASoC: mediatek: mt2701: add HDMI audio memif, FE and BE DAIs
From: Daniel Golle @ 2026-04-20 1:13 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Arnd Bergmann, Cyril Chao,
Daniel Golle, Nícolas F. R. A. Prado, Kuninori Morimoto,
Eugen Hristev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
In-Reply-To: <cover.1776646435.git.daniel@makrotopia.org>
Extend the MT2701/MT7623N AFE driver with the HDMI playback path:
- a new HDMI DMA memif (MT2701_MEMIF_HDMI) mapped to the
AFE_HDMI_OUT_{CON0,BASE,CUR,END} registers;
- a PCM_HDMI front-end DAI (S16_LE only, 44.1k/48k) which feeds
the memif via DPCM;
- an HDMI BE DAI wrapping the AFE_8CH_I2S_OUT_CON engine that
serialises L/R samples towards the on-chip HDMI transmitter.
Sample-rate programming uses the empirically determined
HDMI_BCK_DIV = 45 * 48000 / rate - 1 formula in AUDIO_TOP_CON3,
which covers 44.1 kHz and 48 kHz within the 6-bit divider range.
The AFE_HDMI_CONN0 interconnect is programmed to route memif
output pairs to the serializer inputs with L/R in the right order
for hdmi-audio-codec.
The existing I2S engine helpers (mt2701_mclk_configuration,
mt2701_i2s_path_enable, mt2701_afe_i2s_path_disable) are reused
for the HDMI BE so that MCLK at 128*fs and the ASYS I2S3 FS field
are programmed and cleanly released across open/close cycles.
Only S16_LE and 44.1k/48k are exposed to userspace. Other rates
fall outside the 6-bit BCK divider range, and wider sample
formats require DMA BIT_WIDTH programming that the current memif
setup does not handle. These limits match what the MT8173 AFE
driver exposes for its HDMI path.
The HDMI-related AFE registers (AUDIO_TOP_CON3, AFE_HDMI_OUT_CON0,
AFE_HDMI_CONN0, AFE_8CH_I2S_OUT_CON) are added to the suspend
backup list so that the existing mtk_afe_suspend/resume framework
saves and restores them across system sleep cycles.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2:
* use named masks and explicitly set BIT_WIDTH_16 when
programming AFE_HDMI_OUT_CON0 channel count (Mark Brown)
* add AUDIO_TOP_CON3, AFE_HDMI_OUT_CON0, AFE_HDMI_CONN0,
AFE_8CH_I2S_OUT_CON to suspend/resume backup list
(Mark Brown)
sound/soc/mediatek/mt2701/mt2701-afe-common.h | 2 +
sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 293 +++++++++++++++++-
2 files changed, 294 insertions(+), 1 deletion(-)
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-common.h b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
index 7b15283d6351e..8b6f3a200048a 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-common.h
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
@@ -33,6 +33,7 @@ enum {
MT2701_MEMIF_UL5,
MT2701_MEMIF_DLBT,
MT2701_MEMIF_ULBT,
+ MT2701_MEMIF_HDMI,
MT2701_MEMIF_NUM,
MT2701_IO_I2S = MT2701_MEMIF_NUM,
MT2701_IO_2ND_I2S,
@@ -41,6 +42,7 @@ enum {
MT2701_IO_5TH_I2S,
MT2701_IO_6TH_I2S,
MT2701_IO_MRG,
+ MT2701_IO_HDMI,
};
enum {
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
index fcae38135d93f..4db16132e2c07 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -13,6 +13,7 @@
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
#include "mt2701-afe-common.h"
#include "mt2701-afe-clock-ctrl.h"
@@ -60,6 +61,7 @@ static const struct mt2701_afe_rate mt2701_afe_i2s_rates[] = {
static const unsigned int mt2701_afe_backup_list[] = {
AUDIO_TOP_CON0,
+ AUDIO_TOP_CON3,
AUDIO_TOP_CON4,
AUDIO_TOP_CON5,
ASYS_TOP_CON,
@@ -77,6 +79,9 @@ static const unsigned int mt2701_afe_backup_list[] = {
AFE_CONN22,
AFE_DAC_CON0,
AFE_MEMIF_PBUF_SIZE,
+ AFE_HDMI_OUT_CON0,
+ AFE_HDMI_CONN0,
+ AFE_8CH_I2S_OUT_CON,
};
static int mt2701_dai_num_to_i2s(struct mtk_base_afe *afe, int num)
@@ -542,6 +547,229 @@ static const struct snd_soc_dai_ops mt2701_btmrg_ops = {
.hw_params = mt2701_btmrg_hw_params,
};
+/*
+ * HDMI BE DAI -- drives the on-SoC 8-channel I2S engine whose output
+ * feeds the HDMI transmitter audio port.
+ *
+ * The HDMI audio hardware path is:
+ * HDMI memif DMA (AFE_HDMI_OUT_*) -> interconnect mux (AFE_HDMI_CONN0)
+ * -> 8-channel I2S engine (AFE_8CH_I2S_OUT_CON) -> HDMI TX audio port
+ *
+ * The I2S3 clock tree provides the bit/master clocks; we set its
+ * mclk_rate to 128*fs (matching HDMI_AUD_MCLK_128FS) and let
+ * mt2701_mclk_configuration program the PLL/divider path.
+ */
+#define MT2701_HDMI_I2S_PATH 3
+
+static int mt2701_afe_hdmi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ if (!afe_priv->hadds2pll_ck || !afe_priv->audio_hdmi_ck) {
+ dev_err(afe->dev, "HDMI audio clocks not available\n");
+ return -ENODEV;
+ }
+
+ ret = clk_prepare_enable(afe_priv->hadds2pll_ck);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(afe_priv->audio_hdmi_ck);
+ if (ret)
+ goto err_hdmi;
+
+ if (afe_priv->audio_spdf_ck) {
+ ret = clk_prepare_enable(afe_priv->audio_spdf_ck);
+ if (ret)
+ goto err_spdf;
+ }
+
+ if (afe_priv->audio_apll_ck) {
+ ret = clk_prepare_enable(afe_priv->audio_apll_ck);
+ if (ret)
+ goto err_apll;
+ }
+
+ ret = mt2701_afe_enable_mclk(afe, MT2701_HDMI_I2S_PATH);
+ if (ret)
+ goto err_mclk;
+
+ return 0;
+
+err_mclk:
+ if (afe_priv->audio_apll_ck)
+ clk_disable_unprepare(afe_priv->audio_apll_ck);
+err_apll:
+ if (afe_priv->audio_spdf_ck)
+ clk_disable_unprepare(afe_priv->audio_spdf_ck);
+err_spdf:
+ clk_disable_unprepare(afe_priv->audio_hdmi_ck);
+err_hdmi:
+ clk_disable_unprepare(afe_priv->hadds2pll_ck);
+ return ret;
+}
+
+static void mt2701_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+ mt2701_afe_disable_mclk(afe, MT2701_HDMI_I2S_PATH);
+ if (afe_priv->audio_apll_ck)
+ clk_disable_unprepare(afe_priv->audio_apll_ck);
+ if (afe_priv->audio_spdf_ck)
+ clk_disable_unprepare(afe_priv->audio_spdf_ck);
+ clk_disable_unprepare(afe_priv->audio_hdmi_ck);
+ clk_disable_unprepare(afe_priv->hadds2pll_ck);
+}
+
+static int mt2701_afe_hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ unsigned int divp1;
+ unsigned int val;
+ unsigned int i;
+ int ret;
+
+ /*
+ * Compute AUDIO_TOP_CON3.HDMI_BCK_DIV up front. The divider
+ * drives an internal reference for the HDMI transmitter's
+ * audio packet engine; it must scale with the sample rate so
+ * that the packet engine's timing matches the data flowing in
+ * from the AFE memif/I2S3 side. Empirically, with audpll_sel
+ * parented to hadds2pll_98m (98.304 MHz), the correct value at
+ * 48 kHz is div = 44 (i.e. (div+1) = 45), giving 1.0923 MHz.
+ * Scaling inversely with rate: (div + 1) = 45 * 48000 / rate.
+ * Integer rounding introduces small (<1%) errors at 32 kHz;
+ * 44.1 kHz is nearly exact via round-to-nearest. Reject rates
+ * that fall outside the 6-bit divider range before touching
+ * any hardware so no side effects are left behind on error.
+ */
+ divp1 = (45U * 48000U + rate / 2) / rate;
+ if (divp1 == 0 || divp1 > 64)
+ return -EINVAL;
+
+ /*
+ * Park the I2S3 clock tree at 128*fs -- this is the MCLK that
+ * the ASYS I2S3 engine uses to derive its BCK/LRCK. The engine
+ * outputs BCK = 64*fs (stereo, 32-bit word length).
+ */
+ afe_priv->i2s_path[MT2701_HDMI_I2S_PATH].mclk_rate = rate * 128;
+ ret = mt2701_mclk_configuration(afe, MT2701_HDMI_I2S_PATH);
+ if (ret)
+ return ret;
+
+ /* Program and start the ASYS I2S3 engine (FS, I2S mode, enable). */
+ mt2701_i2s_path_enable(afe,
+ &afe_priv->i2s_path[MT2701_HDMI_I2S_PATH],
+ SNDRV_PCM_STREAM_PLAYBACK, rate);
+
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON3,
+ AUDIO_TOP_CON3_HDMI_BCK_DIV_MASK,
+ AUDIO_TOP_CON3_HDMI_BCK_DIV(divp1 - 1));
+
+ /*
+ * HDMI output memif: set channel count and confirm 16-bit
+ * sample width. Both fields must be written together so that
+ * stale reset-default or prior-stream values in BIT_WIDTH
+ * cannot persist.
+ */
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ AFE_HDMI_OUT_CON0_CH_NUM_MASK |
+ AFE_HDMI_OUT_CON0_BIT_WIDTH_MASK,
+ AFE_HDMI_OUT_CON0_CH_NUM(channels) |
+ AFE_HDMI_OUT_CON0_BIT_WIDTH_16);
+
+ /*
+ * Interconnect mux -- map DMA input slots to HDMI output slots.
+ * Each output takes a 3-bit field at shift (i*3). Swap the first
+ * two inputs so that the DMA's interleaved L/R pair lands on the
+ * correct HDMI L/R output slots. Remaining slots are identity.
+ */
+ val = (1 << 0) | (0 << 3); /* O20 <- I21, O21 <- I20 */
+ for (i = 2; i < 8; i++)
+ val |= ((i & 0x7) << (i * 3));
+ regmap_write(afe->regmap, AFE_HDMI_CONN0, val);
+
+ /*
+ * 8-channel I2S framing: standard I2S, 32-bit slots,
+ * LRCK/BCK inverted. The wire protocol is fixed.
+ */
+ regmap_update_bits(afe->regmap, AFE_8CH_I2S_OUT_CON,
+ AFE_8CH_I2S_OUT_CON_WLEN_MASK |
+ AFE_8CH_I2S_OUT_CON_I2S_DELAY |
+ AFE_8CH_I2S_OUT_CON_LRCK_INV |
+ AFE_8CH_I2S_OUT_CON_BCK_INV,
+ AFE_8CH_I2S_OUT_CON_WLEN_32BIT |
+ AFE_8CH_I2S_OUT_CON_I2S_DELAY |
+ AFE_8CH_I2S_OUT_CON_LRCK_INV |
+ AFE_8CH_I2S_OUT_CON_BCK_INV);
+ return 0;
+}
+
+static int mt2701_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* Ungate HDMI and SPDIF power islands. */
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUDIO_TOP_CON0_PDN_HDMI_CK |
+ AUDIO_TOP_CON0_PDN_SPDIF_CK, 0);
+ /* Enable HDMI output memif. */
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1);
+ /* Enable 8-channel I2S engine. */
+ regmap_update_bits(afe->regmap, AFE_8CH_I2S_OUT_CON,
+ AFE_8CH_I2S_OUT_CON_EN,
+ AFE_8CH_I2S_OUT_CON_EN);
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ regmap_update_bits(afe->regmap, AFE_8CH_I2S_OUT_CON,
+ AFE_8CH_I2S_OUT_CON_EN, 0);
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUDIO_TOP_CON0_PDN_HDMI_CK |
+ AUDIO_TOP_CON0_PDN_SPDIF_CK,
+ AUDIO_TOP_CON0_PDN_HDMI_CK |
+ AUDIO_TOP_CON0_PDN_SPDIF_CK);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int mt2701_afe_hdmi_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+ mt2701_afe_i2s_path_disable(afe,
+ &afe_priv->i2s_path[MT2701_HDMI_I2S_PATH],
+ SNDRV_PCM_STREAM_PLAYBACK);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt2701_afe_hdmi_ops = {
+ .startup = mt2701_afe_hdmi_startup,
+ .shutdown = mt2701_afe_hdmi_shutdown,
+ .hw_params = mt2701_afe_hdmi_hw_params,
+ .hw_free = mt2701_afe_hdmi_hw_free,
+ .trigger = mt2701_afe_hdmi_trigger,
+};
+
static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
/* FE DAIs: memory intefaces to CPU */
{
@@ -628,6 +856,19 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
},
.ops = &mt2701_single_memif_dai_ops,
},
+ {
+ .name = "PCM_HDMI",
+ .id = MT2701_MEMIF_HDMI,
+ .playback = {
+ .stream_name = "HDMI Multich",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = (SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mt2701_single_memif_dai_ops,
+ },
/* BE DAIs */
{
.name = "I2S0",
@@ -748,7 +989,20 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
},
.ops = &mt2701_btmrg_ops,
.symmetric_rate = 1,
- }
+ },
+ {
+ .name = "HDMI I2S",
+ .id = MT2701_IO_HDMI,
+ .playback = {
+ .stream_name = "HDMI 8CH I2S Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = (SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mt2701_afe_hdmi_ops,
+ },
};
static const struct snd_kcontrol_new mt2701_afe_o00_mix[] = {
@@ -927,6 +1181,14 @@ static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = {
{"I16I17", "Multich I2S2 Out Switch", "DLM"},
{"I18I19", "Multich I2S3 Out Switch", "DLM"},
+ /*
+ * HDMI FE -> BE direct route. The HDMI memif has its own DMA
+ * path that feeds the 8-channel internal I2S straight into the
+ * HDMI transmitter; no mixer/interconnect selection is exposed
+ * to the user.
+ */
+ {"HDMI 8CH I2S Playback", NULL, "HDMI Multich"},
+
{ "I12", NULL, "I12I13" },
{ "I13", NULL, "I12I13" },
{ "I14", NULL, "I14I15" },
@@ -1207,6 +1469,35 @@ static const struct mtk_base_memif_data memif_data_array[MT2701_MEMIF_NUM] = {
.agent_disable_shift = 16,
.msb_reg = -1,
},
+ {
+ /*
+ * HDMI memif feeds the on-SoC 8-channel internal I2S that
+ * drives the HDMI transmitter audio port. Unlike the
+ * standard memifs, the enable bit, channel count and bit
+ * width all live in AFE_HDMI_OUT_CON0, so mono/fs/hd/agent
+ * fields are left at -1 and programmed from the BE DAI ops
+ * instead.
+ */
+ .name = "HDMI",
+ .id = MT2701_MEMIF_HDMI,
+ .reg_ofs_base = AFE_HDMI_OUT_BASE,
+ .reg_ofs_cur = AFE_HDMI_OUT_CUR,
+ .reg_ofs_end = AFE_HDMI_OUT_END,
+ .fs_reg = -1,
+ .fs_shift = -1,
+ .fs_maskbit = 0,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .enable_reg = AFE_HDMI_OUT_CON0,
+ .enable_shift = 0,
+ .hd_reg = -1,
+ .hd_shift = -1,
+ .hd_align_reg = -1,
+ .hd_align_mshift = 0,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = 0,
+ .msb_reg = -1,
+ },
};
static const struct mtk_base_irq_data irq_data[MT2701_IRQ_ASYS_END] = {
--
2.53.0
^ permalink raw reply related
* [PATCH v2 4/9] ASoC: mediatek: mt2701: add optional HDMI audio path clocks
From: Daniel Golle @ 2026-04-20 1:13 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Arnd Bergmann, Cyril Chao,
Daniel Golle, Nícolas F. R. A. Prado, Kuninori Morimoto,
Eugen Hristev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
In-Reply-To: <cover.1776646435.git.daniel@makrotopia.org>
The HDMI audio output path on MT2701/MT7623N is rooted in HADDS2PLL
and gated by the audio_hdmi, audio_spdf and audio_apll power gates.
Acquire these four clocks from device tree using devm_clk_get_optional
so that existing platforms which do not wire up HDMI audio keep
probing unchanged. Actual clock enable/prepare is deferred to the
upcoming HDMI DAI startup path.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2: no changes
.../mediatek/mt2701/mt2701-afe-clock-ctrl.c | 22 +++++++++++++++++++
sound/soc/mediatek/mt2701/mt2701-afe-common.h | 4 ++++
2 files changed, 26 insertions(+)
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
index ae620890bb3ac..5a2bcf027b4fb 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
@@ -95,6 +95,28 @@ int mt2701_init_clock(struct mtk_base_afe *afe)
afe_priv->mrgif_ck = NULL;
}
+ /*
+ * Optional HDMI audio clocks. Platforms that do not wire up the
+ * HDMI output (e.g. MT2701 devkits using only the I2S BE DAIs)
+ * may omit these; in that case the HDMI BE DAI simply cannot be
+ * enabled, but the rest of the AFE still probes.
+ */
+ afe_priv->hadds2pll_ck = devm_clk_get_optional(afe->dev, "hadds2pll_294m");
+ if (IS_ERR(afe_priv->hadds2pll_ck))
+ return PTR_ERR(afe_priv->hadds2pll_ck);
+
+ afe_priv->audio_hdmi_ck = devm_clk_get_optional(afe->dev, "audio_hdmi_pd");
+ if (IS_ERR(afe_priv->audio_hdmi_ck))
+ return PTR_ERR(afe_priv->audio_hdmi_ck);
+
+ afe_priv->audio_spdf_ck = devm_clk_get_optional(afe->dev, "audio_spdf_pd");
+ if (IS_ERR(afe_priv->audio_spdf_ck))
+ return PTR_ERR(afe_priv->audio_spdf_ck);
+
+ afe_priv->audio_apll_ck = devm_clk_get_optional(afe->dev, "audio_apll_pd");
+ if (IS_ERR(afe_priv->audio_apll_ck))
+ return PTR_ERR(afe_priv->audio_apll_ck);
+
return 0;
}
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-common.h b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
index 32bef5e2a56d9..7b15283d6351e 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-common.h
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
@@ -90,6 +90,10 @@ struct mt2701_afe_private {
struct mt2701_i2s_path *i2s_path;
struct clk *base_ck[MT2701_BASE_CLK_NUM];
struct clk *mrgif_ck;
+ struct clk *hadds2pll_ck;
+ struct clk *audio_hdmi_ck;
+ struct clk *audio_spdf_ck;
+ struct clk *audio_apll_ck;
bool mrg_enable[MTK_STREAM_NUM];
const struct mt2701_soc_variants *soc;
--
2.53.0
^ permalink raw reply related
* [PATCH v2 3/9] ASoC: mediatek: mt2701: add AFE HDMI register definitions
From: Daniel Golle @ 2026-04-20 1:13 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Arnd Bergmann, Cyril Chao,
Daniel Golle, Nícolas F. R. A. Prado, Kuninori Morimoto,
Eugen Hristev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
In-Reply-To: <cover.1776646435.git.daniel@makrotopia.org>
Add register offsets and bit defines for the MT2701/MT7623N AFE
HDMI audio output path: the HDMI BCK divider in AUDIO_TOP_CON3,
the HDMI output memif control and descriptor registers, the 8-bit
AFE_HDMI_CONN0 interconnect, and the AFE_8CH_I2S_OUT_CON engine
that drives the HDMI TX serial link. These are a prerequisite for
adding an HDMI playback path to the mt2701 AFE driver and have no
behavioural effect on their own.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2: no changes
sound/soc/mediatek/mt2701/mt2701-reg.h | 35 ++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/sound/soc/mediatek/mt2701/mt2701-reg.h b/sound/soc/mediatek/mt2701/mt2701-reg.h
index c84d14cdd7ae8..b7a25bfb58662 100644
--- a/sound/soc/mediatek/mt2701/mt2701-reg.h
+++ b/sound/soc/mediatek/mt2701/mt2701-reg.h
@@ -10,10 +10,17 @@
#define _MT2701_REG_H_
#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON3 0x000c
#define AUDIO_TOP_CON4 0x0010
#define AUDIO_TOP_CON5 0x0014
#define AFE_DAIBT_CON0 0x001c
#define AFE_MRGIF_CON 0x003c
+#define AFE_HDMI_OUT_CON0 0x0370
+#define AFE_HDMI_OUT_BASE 0x0374
+#define AFE_HDMI_OUT_CUR 0x0378
+#define AFE_HDMI_OUT_END 0x037c
+#define AFE_HDMI_CONN0 0x0390
+#define AFE_8CH_I2S_OUT_CON 0x0394
#define ASMI_TIMING_CON1 0x0100
#define ASMO_TIMING_CON1 0x0104
#define PWR1_ASM_CON1 0x0108
@@ -125,6 +132,34 @@
#define AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK (0x3 << 12)
#define AFE_MEMIF_PBUF_SIZE_DLM_32BYTES (0x1 << 12)
+/* AUDIO_TOP_CON0 (0x0000) -- HDMI audio clock gating */
+#define AUDIO_TOP_CON0_PDN_HDMI_CK (0x1 << 20)
+#define AUDIO_TOP_CON0_PDN_SPDIF_CK (0x1 << 21)
+#define AUDIO_TOP_CON0_PDN_SPDIF2_CK (0x1 << 22)
+#define AUDIO_TOP_CON0_PDN_APLL_CK (0x1 << 23)
+
+/* AUDIO_TOP_CON3 (0x000c) -- HDMI BCK divider */
+#define AUDIO_TOP_CON3_HDMI_BCK_DIV_MASK (0x3f << 8)
+#define AUDIO_TOP_CON3_HDMI_BCK_DIV(x) (((x) & 0x3f) << 8)
+
+/* AFE_HDMI_OUT_CON0 (0x0370) */
+#define AFE_HDMI_OUT_CON0_OUT_ON (0x1 << 0)
+#define AFE_HDMI_OUT_CON0_BIT_WIDTH_MASK (0x1 << 1)
+#define AFE_HDMI_OUT_CON0_BIT_WIDTH_16 (0x0 << 1)
+#define AFE_HDMI_OUT_CON0_BIT_WIDTH_32 (0x1 << 1)
+#define AFE_HDMI_OUT_CON0_CH_NUM_MASK (0xf << 4)
+#define AFE_HDMI_OUT_CON0_CH_NUM(x) (((x) & 0xf) << 4)
+
+/* AFE_8CH_I2S_OUT_CON (0x0394) -- on-SoC 8-channel I2S that feeds HDMI TX */
+#define AFE_8CH_I2S_OUT_CON_EN (0x1 << 0)
+#define AFE_8CH_I2S_OUT_CON_BCK_INV (0x1 << 1)
+#define AFE_8CH_I2S_OUT_CON_LRCK_INV (0x1 << 2)
+#define AFE_8CH_I2S_OUT_CON_I2S_DELAY (0x1 << 3)
+#define AFE_8CH_I2S_OUT_CON_WLEN_MASK (0x3 << 4)
+#define AFE_8CH_I2S_OUT_CON_WLEN_16BIT (0x1 << 4)
+#define AFE_8CH_I2S_OUT_CON_WLEN_24BIT (0x2 << 4)
+#define AFE_8CH_I2S_OUT_CON_WLEN_32BIT (0x3 << 4)
+
/* I2S in/out register bit control */
#define ASYS_I2S_CON_FS (0x1f << 8)
#define ASYS_I2S_CON_FS_SET(x) ((x) << 8)
--
2.53.0
^ permalink raw reply related
* [PATCH v2 2/9] ASoC: dt-bindings: mediatek,mt2701-hdmi-audio: add MT2701 HDMI audio
From: Daniel Golle @ 2026-04-20 1:13 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Arnd Bergmann, Cyril Chao,
Daniel Golle, Nícolas F. R. A. Prado, Kuninori Morimoto,
Eugen Hristev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
In-Reply-To: <cover.1776646435.git.daniel@makrotopia.org>
Describe the sound card node that routes the MT2701/MT7623N AFE
HDMI playback path to the on-chip HDMI transmitter. This is
separate from the AFE platform binding (mediatek,mt2701-audio)
because it represents board-level audio routing between the AFE
and the HDMI codec, not an additional IP block. MT7623N boards
carry the same IP and use the mt7623n- compatible as a fallback
to mt2701-.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2:
* Fix subject prefix to use schema filename (Krzysztof Kozlowski)
* Rewrite title and description to describe hardware, not driver
(Krzysztof Kozlowski)
* Clarify in commit message why this is a separate binding from
mediatek,mt2701-audio (Krzysztof Kozlowski)
.../sound/mediatek,mt2701-hdmi-audio.yaml | 48 +++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/mediatek,mt2701-hdmi-audio.yaml
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt2701-hdmi-audio.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt2701-hdmi-audio.yaml
new file mode 100644
index 0000000000000..9d5a8166e51ff
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt2701-hdmi-audio.yaml
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/mediatek,mt2701-hdmi-audio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek MT2701/MT7623N HDMI audio output
+
+maintainers:
+ - Daniel Golle <daniel@makrotopia.org>
+
+description:
+ Sound card routing the MT2701/MT7623N Audio Front End HDMI
+ playback path to the on-chip HDMI transmitter. The AFE
+ provides the DMA memif and internal I2S engine; the HDMI
+ transmitter acts as the audio codec on the serialised link.
+
+properties:
+ compatible:
+ oneOf:
+ - const: mediatek,mt2701-hdmi-audio
+ - items:
+ - const: mediatek,mt7623n-hdmi-audio
+ - const: mediatek,mt2701-hdmi-audio
+
+ mediatek,platform:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: Phandle of the MT2701/MT7623N AFE platform node.
+
+ mediatek,audio-codec:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: Phandle of the HDMI transmitter acting as audio codec.
+
+required:
+ - compatible
+ - mediatek,platform
+ - mediatek,audio-codec
+
+additionalProperties: false
+
+examples:
+ - |
+ sound-hdmi {
+ compatible = "mediatek,mt7623n-hdmi-audio",
+ "mediatek,mt2701-hdmi-audio";
+ mediatek,platform = <&afe>;
+ mediatek,audio-codec = <&hdmi0>;
+ };
--
2.53.0
^ permalink raw reply related
* [PATCH v2 1/9] ASoC: dt-bindings: mt2701-afe-pcm: add HDMI audio path clocks
From: Daniel Golle @ 2026-04-20 1:13 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Arnd Bergmann, Cyril Chao,
Daniel Golle, Nícolas F. R. A. Prado, Kuninori Morimoto,
Eugen Hristev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
In-Reply-To: <cover.1776646435.git.daniel@makrotopia.org>
Document four additional optional clocks feeding the HDMI audio
output path on MT2701: the HADDS2 PLL (root of the HDMI audio
clock tree), the HDMI audio and S/PDIF interface power gates,
and the audio APLL root gate. Older device trees that do not
wire these up remain valid via minItems. MT7622 does not have
HDMI audio hardware, so its compatible is restricted to the
base set of 34 clocks.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2:
* Restrict the four HDMI clocks to mediatek,mt2701-audio only
using an allOf/if/then conditional; MT7622 is capped at 34
clocks (Krzysztof Kozlowski)
.../bindings/sound/mediatek,mt2701-audio.yaml | 23 +++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt2701-audio.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt2701-audio.yaml
index 45382c4d86aa3..871bf197650b5 100644
--- a/Documentation/devicetree/bindings/sound/mediatek,mt2701-audio.yaml
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt2701-audio.yaml
@@ -32,6 +32,7 @@ properties:
maxItems: 1
clocks:
+ minItems: 34
items:
- description: audio infra sys clock
- description: top audio mux 1
@@ -67,8 +68,13 @@ properties:
- description: top audio a1 sys pd
- description: top audio a2 sys pd
- description: audio merge interface pd
+ - description: HADDS2 PLL 294 MHz (HDMI audio path root)
+ - description: HDMI audio interface pd
+ - description: S/PDIF interface pd
+ - description: audio APLL root pd
clock-names:
+ minItems: 34
items:
- const: infra_sys_audio_clk
- const: top_audio_mux1_sel
@@ -104,6 +110,10 @@ properties:
- const: audio_a1sys_pd
- const: audio_a2sys_pd
- const: audio_mrgif_pd
+ - const: hadds2pll_294m
+ - const: audio_hdmi_pd
+ - const: audio_spdf_pd
+ - const: audio_apll_pd
required:
- compatible
@@ -114,3 +124,16 @@ required:
- clock-names
additionalProperties: false
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: mediatek,mt7622-audio
+ then:
+ properties:
+ clocks:
+ maxItems: 34
+ clock-names:
+ maxItems: 34
--
2.53.0
^ permalink raw reply related
* [PATCH v2 0/9] ASoC: mediatek: mt2701: HDMI audio support
From: Daniel Golle @ 2026-04-20 1:12 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Arnd Bergmann, Cyril Chao,
Daniel Golle, Nícolas F. R. A. Prado, Kuninori Morimoto,
Eugen Hristev, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
This series wires up on-chip HDMI audio on MT2701 and MT7623N, from the
DRM bridge down through the AFE into a small machine driver that binds
the AFE HDMI BE to the HDMI TX codec already exposed by the
mediatek-drm-hdmi driver. Bindings, DT and a BananaPi R2 board node
are included.
In order to survive vblank or late hotplug of the monitor, the fix
submitted separately [1] is required as well.
Everything here was developed for and tested on a BananaPi R2
(MT7623N), which turns ten years old this year -- a nice occasion to
finally land HDMI audio for a SoC which was truly ahead of its time.
[1]: https://patchwork.kernel.org/project/linux-mediatek/patch/a3e22cbae528c9a38d854a586d1736b860998d41.1776265222.git.daniel@makrotopia.org/
Changes since v1:
* Restrict the four HDMI clocks to mediatek,mt2701-audio only
using an allOf/if/then conditional; MT7622 is capped at 34
clocks (Krzysztof Kozlowski)
* Fix subject prefix to use schema filename (Krzysztof Kozlowski)
* Rewrite title and description to describe hardware, not driver
(Krzysztof Kozlowski)
* Clarify in commit message why this is a separate binding from
mediatek,mt2701-audio (Krzysztof Kozlowski)
* use named masks and explicitly set BIT_WIDTH_16 when
programming AFE_HDMI_OUT_CON0 channel count (Mark Brown)
* add AUDIO_TOP_CON3, AFE_HDMI_OUT_CON0, AFE_HDMI_CONN0,
AFE_8CH_I2S_OUT_CON to suspend/resume backup list
(Mark Brown)
Daniel Golle (9):
ASoC: dt-bindings: mt2701-afe-pcm: add HDMI audio path clocks
ASoC: dt-bindings: mediatek,mt2701-hdmi-audio: add HDMI audio output
binding
ASoC: mediatek: mt2701: add AFE HDMI register definitions
ASoC: mediatek: mt2701: add optional HDMI audio path clocks
ASoC: mediatek: mt2701: add HDMI audio memif, FE and BE DAIs
ASoC: mediatek: mt2701: add machine driver for on-chip HDMI codec
ARM: dts: mediatek: mt2701: wire HDMI audio path clocks into AFE
ARM: dts: mediatek: mt7623: wire HDMI audio path clocks into AFE
ARM: dts: mediatek: mt7623n-bananapi-bpi-r2: add HDMI audio machine
node
.../bindings/sound/mediatek,mt2701-audio.yaml | 23 ++
.../sound/mediatek,mt2701-hdmi-audio.yaml | 48 +++
arch/arm/boot/dts/mediatek/mt2701.dtsi | 21 +-
arch/arm/boot/dts/mediatek/mt7623.dtsi | 21 +-
.../dts/mediatek/mt7623n-bananapi-bpi-r2.dts | 7 +
sound/soc/mediatek/Kconfig | 10 +
sound/soc/mediatek/mt2701/Makefile | 1 +
.../mediatek/mt2701/mt2701-afe-clock-ctrl.c | 22 ++
sound/soc/mediatek/mt2701/mt2701-afe-common.h | 6 +
sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 293 +++++++++++++++++-
sound/soc/mediatek/mt2701/mt2701-hdmi.c | 114 +++++++
sound/soc/mediatek/mt2701/mt2701-reg.h | 35 +++
12 files changed, 590 insertions(+), 11 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/mediatek,mt2701-hdmi-audio.yaml
create mode 100644 sound/soc/mediatek/mt2701/mt2701-hdmi.c
--
2.53.0
^ permalink raw reply
* RE: [PATCH] ASoC: sdw_utils: cs42l43: allow spk component names to be combined
From: Liao, Bard @ 2026-04-20 1:02 UTC (permalink / raw)
To: Maciej Strozek, Mark Brown, Jaroslav Kysela
Cc: Takashi Iwai, Bard Liao, linux-kernel@vger.kernel.org,
linux-sound@vger.kernel.org, patches@opensource.cirrus.com
In-Reply-To: <20260417144529.2385360-1-mstrozek@opensource.cirrus.com>
> -----Original Message-----
> From: Maciej Strozek <mstrozek@opensource.cirrus.com>
> Sent: Friday, April 17, 2026 10:45 PM
> To: Mark Brown <broonie@kernel.org>; Jaroslav Kysela <perex@perex.cz>
> Cc: Takashi Iwai <tiwai@suse.com>; Bard Liao <yung-
> chuan.liao@linux.intel.com>; linux-kernel@vger.kernel.org; linux-
> sound@vger.kernel.org; patches@opensource.cirrus.com; Maciej Strozek
> <mstrozek@opensource.cirrus.com>
> Subject: [PATCH] ASoC: sdw_utils: cs42l43: allow spk component names to be
> combined
>
> Move handling of cs42l43-spk component string into SOF mechanism [1]
> which will allow it to be aggregated with other speakers.
> Likewise handle the cs35l56-bridge special case which should not be
> combined to keep compatibility with UCM.
>
> Link: https://github.com/thesofproject/linux/pull/5445 [1]
> Link: https://github.com/alsa-project/alsa-ucm-conf/pull/747
> Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
> ---
> sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c | 6 ------
> sound/soc/sdw_utils/soc_sdw_cs42l43.c | 10 ----------
> sound/soc/sdw_utils/soc_sdw_utils.c | 20 ++++++++++++++++----
> 3 files changed, 16 insertions(+), 20 deletions(-)
>
> diff --git a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c
> b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c
> index 2a7109d53cbe3..e0e32a279787c 100644
> --- a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c
> +++ b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c
> @@ -40,12 +40,6 @@ static int asoc_sdw_bridge_cs35l56_asp_init(struct
> snd_soc_pcm_runtime *rtd)
> struct snd_soc_dai *codec_dai;
> struct snd_soc_dai *cpu_dai;
>
> - card->components = devm_kasprintf(card->dev, GFP_KERNEL,
> - "%s spk:cs35l56-bridge",
> - card->components);
> - if (!card->components)
> - return -ENOMEM;
> -
> ret = snd_soc_dapm_new_controls(dapm, bridge_widgets,
> ARRAY_SIZE(bridge_widgets));
> if (ret) {
> diff --git a/sound/soc/sdw_utils/soc_sdw_cs42l43.c
> b/sound/soc/sdw_utils/soc_sdw_cs42l43.c
> index 4a451b9d4f137..66aa9958ad184 100644
> --- a/sound/soc/sdw_utils/soc_sdw_cs42l43.c
> +++ b/sound/soc/sdw_utils/soc_sdw_cs42l43.c
> @@ -110,18 +110,8 @@ int asoc_sdw_cs42l43_spk_rtd_init(struct
> snd_soc_pcm_runtime *rtd, struct snd_so
> struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd,
> 0)->component;
> struct snd_soc_card *card = rtd->card;
> struct snd_soc_dapm_context *dapm =
> snd_soc_card_to_dapm(card);
> - struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
> int ret;
>
> - if (!(ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS)) {
> - /* Will be set by the bridge code in this case */
> - card->components = devm_kasprintf(card->dev, GFP_KERNEL,
> - "%s spk:cs42l43-spk",
> - card->components);
> - if (!card->components)
> - return -ENOMEM;
> - }
> -
> ret = snd_soc_limit_volume(card, "cs42l43 Speaker Digital Volume",
> CS42L43_SPK_VOLUME_0DB);
> if (ret)
> diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c
> b/sound/soc/sdw_utils/soc_sdw_utils.c
> index 2807f536eef0c..1637cc3f3d598 100644
> --- a/sound/soc/sdw_utils/soc_sdw_utils.c
> +++ b/sound/soc/sdw_utils/soc_sdw_utils.c
> @@ -758,6 +758,7 @@ struct asoc_sdw_codec_info codec_info_list[] = {
> {
> .direction = {true, false},
> .codec_name = "cs42l43-codec",
> + .component_name = "cs42l43-spk",
> .dai_name = "cs42l43-dp6",
> .dai_type = SOC_SDW_DAI_TYPE_AMP,
> .dailink = {SOC_SDW_AMP_OUT_DAI_ID,
> SOC_SDW_UNUSED_DAI_ID},
> @@ -1104,6 +1105,7 @@ static int
> asoc_sdw_find_codec_info_dai_index(const struct asoc_sdw_codec_info *
> int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd)
> {
> struct snd_soc_card *card = rtd->card;
> + struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
> struct snd_soc_dapm_context *dapm =
> snd_soc_card_to_dapm(card);
> struct asoc_sdw_codec_info *codec_info;
> struct snd_soc_dai *dai;
> @@ -1179,16 +1181,26 @@ int asoc_sdw_rtd_init(struct
> snd_soc_pcm_runtime *rtd)
> /* Generate the spk component string for card->components
> string */
> if (codec_info->dais[dai_index].dai_type ==
> SOC_SDW_DAI_TYPE_AMP &&
> codec_info->dais[dai_index].component_name) {
> + const char *component;
> +
> + /*
> + * For the special case of cs42l43 with sidecar amps,
> use only
> + * "cs35l56-bridge" as the component name in card-
> >components
> + */
> + if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS &&
> + !strcmp(codec_info-
> >dais[dai_index].component_name, "cs42l43-spk"))
> + component = "cs35l56-bridge";
> + else
> + component = codec_info-
> >dais[dai_index].component_name;
> +
> if (strlen (spk_components) == 0)
> spk_components =
> - devm_kasprintf(card->dev,
> GFP_KERNEL, "%s",
> - codec_info-
> >dais[dai_index].component_name);
> + devm_kasprintf(card->dev,
> GFP_KERNEL, "%s", component);
> else
> /* Append component name to
> spk_components */
> spk_components =
> devm_kasprintf(card->dev,
> GFP_KERNEL,
> - "%s+%s",
> spk_components,
> - codec_info-
> >dais[dai_index].component_name);
> + "%s+%s",
> spk_components, component);
> }
>
> codec_info->dais[dai_index].rtd_init_done = true;
> --
> 2.47.3
>
^ permalink raw reply
* Re: [PATCH RFC v2 02/11] ASoC: meson: aiu-encoder-i2s: use gx_iface and gx_stream structures
From: Valerio Setti @ 2026-04-19 23:17 UTC (permalink / raw)
To: Jerome Brunet
Cc: Mark Brown, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
Neil Armstrong, Kevin Hilman, Martin Blumenstingl, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-kernel, linux-sound,
linux-arm-kernel, linux-amlogic, devicetree
In-Reply-To: <1jy0ios3f9.fsf@starbuckisacylon.baylibre.com>
On 4/15/26 16:28, Jerome Brunet wrote:
>
> Huh interresting, I had not thought of that. Valerio and I discussed the
> clock part a lot for this rework. It is the crux since since the
> interface and clock setting lives in the AIU subsys but serves both the
> AIU and AUDIN subsys.
>
> Valerio maybe you could keep function above just to set the rate, but
> enabling the clocks through a DAPM supply widget ? This is kind of what
> the AXG is doing.
>
> what do you think ?
>
> (actually in the AXG the each formatter widget call CCF
> clk_prepare_enable() but a supply widget poking the register would do
> the same thing)
>
Hi Jerome,
thanks for your review and suggestion.
I took some time to investigate it and I think I have a different
proposal that reduces the amount of changes compared to the current
implementation, but gets to the same result.
The thing is in "axg-tdm-interface.c" MCLK is enabled/disabled using
"set_bias_level()" callback which works fine because in that case the
interface driver is also a standalone component. On the GX platform the
interface is a DAI which is part of the AIU component, so this callback
isn't available. To get the same behavior I would need to add a new DAPM
widget in "aiu_cpu_dapm_widgets[]" and then add it to both
"aiu_cpu_dapm_routes[]" both in "aiu.c" and in the device-tree for AUDIN.
What if instead I add the "prepare()" callback to
"aiu_encoder_i2s_dai_ops" and enable MCLK and I2S clock divider there?
This would match "hw_free()" calls and it would be a change only
internal to "aiu-encoder-i2s.c".
To support this I will:
- remove calls to "gx_stream_set_cont_clocks" which are arguably not
very useful for the GX platform;
- use the "clk_enabled" field of "struct gx_stream" (unused after
removal of "gx_stream_set_cont_clocks" calls) to track the status of
MCLK and enable/disable it only when necessary (ex: prevent multiple
calls to "prepare" to enable MCLK multiple times, etc);
- keep using "snd_soc_dai_active" in "hw_free" to know when to disable
the I2S clock divider. Or perhaps I can rely on "clk_enabled" also for
this one so that I can manage these 2 clocks uniformly.
Is this an acceptable alternative approach for you?
--
Valerio
^ permalink raw reply
* [PATCH 4/4] ALSA: usb-audio: Update US-16x08 EQ/comp shadow state after successful writes
From: Cássio Gabriel @ 2026-04-19 20:30 UTC (permalink / raw)
To: Takashi Iwai, Chris J Arges, Detlef Urban, Jaroslav Kysela
Cc: linux-sound, linux-kernel, Cássio Gabriel, stable
In-Reply-To: <20260419-usb-write-error-propagation-v1-0-5a3bd4a673ae@gmail.com>
snd_us16x08_comp_put() and snd_us16x08_eq_put() update their
software stores before sending the USB write. If the transfer
fails, later get callbacks report a value the hardware never
accepted.
Build the outgoing message from the current store plus the
pending value, then commit the store only after a successful
write.
Fixes: d2bb390a2081 ("ALSA: usb-audio: Tascam US-16x08 DSP mixer quirk")
Cc: stable@vger.kernel.org
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
sound/usb/mixer_us16x08.c | 78 +++++++++++++++++++++++++++++++----------------
1 file changed, 52 insertions(+), 26 deletions(-)
diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c
index fcf7dfa4aa84..ebff185cbd2c 100644
--- a/sound/usb/mixer_us16x08.c
+++ b/sound/usb/mixer_us16x08.c
@@ -435,6 +435,7 @@ static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol,
int index = ucontrol->id.index;
char buf[sizeof(comp_msg)];
int val_idx, val;
+ int threshold, ratio, attack, release, gain, switch_on;
int err;
val = ucontrol->value.integer.value[0];
@@ -447,36 +448,61 @@ static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol,
/* new control value incl. bias*/
val_idx = elem->head.id - SND_US16X08_ID_COMP_BASE;
- store->val[val_idx][index] = ucontrol->value.integer.value[0];
+ threshold = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)]
+ [index];
+ ratio = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index];
+ attack = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index];
+ release = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)]
+ [index];
+ gain = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index];
+ switch_on = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)]
+ [index];
+
+ switch (val_idx) {
+ case COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD):
+ threshold = val;
+ break;
+ case COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO):
+ ratio = val;
+ break;
+ case COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK):
+ attack = val;
+ break;
+ case COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE):
+ release = val;
+ break;
+ case COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN):
+ gain = val;
+ break;
+ case COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH):
+ switch_on = val;
+ break;
+ }
/* prepare compressor URB message from template */
memcpy(buf, comp_msg, sizeof(comp_msg));
/* place comp values in message buffer watch bias! */
- buf[8] = store->val[
- COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)][index]
- - SND_US16X08_COMP_THRESHOLD_BIAS;
- buf[11] = ratio_map[store->val[
- COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index]];
- buf[14] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index]
- + SND_US16X08_COMP_ATTACK_BIAS;
- buf[17] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)][index]
- + SND_US16X08_COMP_RELEASE_BIAS;
- buf[20] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index];
- buf[26] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)][index];
+ buf[8] = threshold - SND_US16X08_COMP_THRESHOLD_BIAS;
+ buf[11] = ratio_map[ratio];
+ buf[14] = attack + SND_US16X08_COMP_ATTACK_BIAS;
+ buf[17] = release + SND_US16X08_COMP_RELEASE_BIAS;
+ buf[20] = gain;
+ buf[26] = switch_on;
/* place channel selector in message buffer */
buf[5] = index + 1;
err = snd_us16x08_send_urb(chip, buf, sizeof(comp_msg));
- if (err > 0) {
- elem->cached |= 1 << index;
- elem->cache_val[index] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set compressor, err:%d\n", err);
+ return err;
}
+ store->val[val_idx][index] = val;
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
return 1;
}
@@ -578,11 +604,10 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol,
/* copy URB buffer from EQ template */
memcpy(buf, eqs_msq, sizeof(eqs_msq));
- store->val[b_idx][p_idx][index] = val;
- buf[20] = store->val[b_idx][3][index];
- buf[17] = store->val[b_idx][2][index];
- buf[14] = store->val[b_idx][1][index];
- buf[11] = store->val[b_idx][0][index];
+ buf[20] = p_idx == 3 ? val : store->val[b_idx][3][index];
+ buf[17] = p_idx == 2 ? val : store->val[b_idx][2][index];
+ buf[14] = p_idx == 1 ? val : store->val[b_idx][1][index];
+ buf[11] = p_idx == 0 ? val : store->val[b_idx][0][index];
/* place channel index in URB buffer */
buf[5] = index + 1;
@@ -592,14 +617,15 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol,
err = snd_us16x08_send_urb(chip, buf, sizeof(eqs_msq));
- if (err > 0) {
- /* store new value in EQ band cache */
- elem->cached |= 1 << index;
- elem->cache_val[index] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set eq param, err:%d\n", err);
+ return err;
}
+ store->val[b_idx][p_idx][index] = val;
+ /* store new value in EQ band cache */
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
return 1;
}
--
2.53.0
^ permalink raw reply related
* [PATCH 3/4] ALSA: usb-audio: Propagate US-16x08 write errors in route/mix EQ-switch put callbacks
From: Cássio Gabriel @ 2026-04-19 20:30 UTC (permalink / raw)
To: Takashi Iwai, Chris J Arges, Detlef Urban, Jaroslav Kysela
Cc: linux-sound, linux-kernel, Cássio Gabriel, stable
In-Reply-To: <20260419-usb-write-error-propagation-v1-0-5a3bd4a673ae@gmail.com>
Several US-16x08 mixer put callbacks log failed control URBs but
still return success to userspace. That hides device write failures
even though the requested value was not applied.
Return the negative write error instead in the route, master, bus,
channel, and EQ switch put callbacks.
Fixes: d2bb390a2081 ("ALSA: usb-audio: Tascam US-16x08 DSP mixer quirk")
Cc: stable@vger.kernel.org
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
sound/usb/mixer_us16x08.c | 49 +++++++++++++++++++++++------------------------
1 file changed, 24 insertions(+), 25 deletions(-)
diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c
index 8a02964e5d7b..fcf7dfa4aa84 100644
--- a/sound/usb/mixer_us16x08.c
+++ b/sound/usb/mixer_us16x08.c
@@ -224,14 +224,14 @@ static int snd_us16x08_route_put(struct snd_kcontrol *kcontrol,
err = snd_us16x08_send_urb(chip, buf, sizeof(route_msg));
- if (err > 0) {
- elem->cached |= 1 << index;
- elem->cache_val[index] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set routing, err:%d\n", err);
+ return err;
}
- return err > 0 ? 1 : 0;
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
+ return 1;
}
static int snd_us16x08_master_info(struct snd_kcontrol *kcontrol,
@@ -283,14 +283,14 @@ static int snd_us16x08_master_put(struct snd_kcontrol *kcontrol,
buf[5] = index + 1;
err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_out));
- if (err > 0) {
- elem->cached |= 1 << index;
- elem->cache_val[index] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set master, err:%d\n", err);
+ return err;
}
- return err > 0 ? 1 : 0;
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
+ return 1;
}
static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol,
@@ -324,14 +324,14 @@ static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol,
break;
}
- if (err > 0) {
- elem->cached |= 1;
- elem->cache_val[0] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set bus parameter, err:%d\n", err);
+ return err;
}
- return err > 0 ? 1 : 0;
+ elem->cached |= 1;
+ elem->cache_val[0] = val;
+ return 1;
}
static int snd_us16x08_bus_get(struct snd_kcontrol *kcontrol,
@@ -392,14 +392,14 @@ static int snd_us16x08_channel_put(struct snd_kcontrol *kcontrol,
err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_in));
- if (err > 0) {
- elem->cached |= 1 << index;
- elem->cache_val[index] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set channel, err:%d\n", err);
+ return err;
}
- return err > 0 ? 1 : 0;
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
+ return 1;
}
static int snd_us16x08_mix_info(struct snd_kcontrol *kcontrol,
@@ -529,13 +529,13 @@ static int snd_us16x08_eqswitch_put(struct snd_kcontrol *kcontrol,
msleep(15);
}
- if (err > 0) {
- elem->cached |= 1 << index;
- elem->cache_val[index] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set eq switch, err:%d\n", err);
+ return err;
}
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
return 1;
}
@@ -1418,4 +1418,3 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer)
return 0;
}
-
--
2.53.0
^ permalink raw reply related
* [PATCH 2/4] ALSA: usb-audio: Propagate errors in scarlett_ctl_enum_put()
From: Cássio Gabriel @ 2026-04-19 20:30 UTC (permalink / raw)
To: Takashi Iwai, Chris J Arges, Detlef Urban, Jaroslav Kysela
Cc: linux-sound, linux-kernel, Cássio Gabriel, stable
In-Reply-To: <20260419-usb-write-error-propagation-v1-0-5a3bd4a673ae@gmail.com>
scarlett_ctl_enum_put() ignores the return value from
snd_usb_set_cur_mix_value() and reports success whenever the
requested enum value differs from the current one.
If the SET_CUR request fails, the callback still returns success even
though neither the hardware state nor the cached mixer value changed.
Fixes: 76b188c4b370 ("ALSA: usb-audio: Scarlett mixer interface for 6i6, 18i6, 18i8 and 18i20")
Cc: stable@vger.kernel.org
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
sound/usb/mixer_scarlett.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c
index 1bb01e827654..673eb8d8724d 100644
--- a/sound/usb/mixer_scarlett.c
+++ b/sound/usb/mixer_scarlett.c
@@ -680,7 +680,9 @@ static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl,
val = ucontrol->value.integer.value[0];
val = val + opt->start;
if (val != oval) {
- snd_usb_set_cur_mix_value(elem, 0, 0, val);
+ err = snd_usb_set_cur_mix_value(elem, 0, 0, val);
+ if (err < 0)
+ return err;
return 1;
}
return 0;
--
2.53.0
^ permalink raw reply related
* [PATCH 1/4] ALSA: usb-audio: Propagate write errors in generic mixer put callbacks
From: Cássio Gabriel @ 2026-04-19 20:30 UTC (permalink / raw)
To: Takashi Iwai, Chris J Arges, Detlef Urban, Jaroslav Kysela
Cc: linux-sound, linux-kernel, Cássio Gabriel
In-Reply-To: <20260419-usb-write-error-propagation-v1-0-5a3bd4a673ae@gmail.com>
mixer_ctl_feature_put(), mixer_ctl_procunit_put(), and
mixer_ctl_selector_put() ignore failures from their SET_CUR helper
routines and report the control as changed whenever the requested
value differs from the current one.
If the device rejects the write, userspace still sees success although
the hardware state did not change. Propagate write failures instead,
using filter_error() so ignore_ctl_error keeps the same semantics as
the existing get paths.
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
sound/usb/mixer.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 85653112e7f3..9d9ed68166c8 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1526,7 +1526,10 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
val = get_abs_value(cval, val);
if (oval != val) {
- snd_usb_set_cur_mix_value(cval, c + 1, cnt, val);
+ err = snd_usb_set_cur_mix_value(cval, c + 1,
+ cnt, val);
+ if (err < 0)
+ return filter_error(cval, err);
changed = 1;
}
cnt++;
@@ -1541,7 +1544,9 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
val = get_abs_value(cval, val);
if (val != oval) {
- snd_usb_set_cur_mix_value(cval, 0, 0, val);
+ err = snd_usb_set_cur_mix_value(cval, 0, 0, val);
+ if (err < 0)
+ return filter_error(cval, err);
changed = 1;
}
}
@@ -2466,7 +2471,9 @@ static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
val = get_abs_value(cval, val);
if (val != oval) {
- set_cur_ctl_value(cval, cval->control << 8, val);
+ err = set_cur_ctl_value(cval, cval->control << 8, val);
+ if (err < 0)
+ return filter_error(cval, err);
return 1;
}
return 0;
@@ -2832,7 +2839,9 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
val = get_abs_value(cval, val);
if (val != oval) {
- set_cur_ctl_value(cval, cval->control << 8, val);
+ err = set_cur_ctl_value(cval, cval->control << 8, val);
+ if (err < 0)
+ return filter_error(cval, err);
return 1;
}
return 0;
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox