[PATCH v3 5/7] ASoC: soc-dai: add common operation to set TDM idle mode

James Calligeros posted 7 patches 1 month, 1 week ago
[PATCH v3 5/7] ASoC: soc-dai: add common operation to set TDM idle mode
Posted by James Calligeros 1 month, 1 week ago
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 <jcalligeros99@gmail.com>
---
 include/sound/soc-dai.h |  7 +++++
 sound/soc/soc-dai.c     | 40 +++++++++++++++++++++++++
 2 files changed, 47 insertions(+)

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 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
 }
 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

-- 
2.53.0
Re: [PATCH v3 5/7] ASoC: soc-dai: add common operation to set TDM idle mode
Posted by Kuninori Morimoto 1 month, 1 week ago
Hi James

> 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 <jcalligeros99@gmail.com>
> ---
(snip)
> +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);

Do we need "rx_mode" ?
Only "tx_mode" is enough if my understanding was correct.

This patch-set adds new snd_soc_dai_set_tdm_idle(), but no one is calling
it. Who use it ??

Thank you for your help !!

Best regards
---
Kuninori Morimoto
Re: [PATCH v3 5/7] ASoC: soc-dai: add common operation to set TDM idle mode
Posted by James Calligeros 1 month, 1 week ago
Hi Kuniori,

On Tuesday, 3 March 2026 12:11:50 pm Australian Eastern Standard Time Kuninori 
Morimoto wrote:
> > +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);
> 
> Do we need "rx_mode" ?
> Only "tx_mode" is enough if my understanding was correct.

For our (Asahi) purposes yes, we only require TX. However, TAS2770 also
has a bus keeper on SDIN, and I suspect other hardware may too. Rather than
break the API later on should the need to configure this arise,
I decided to add the capability now. I am happy to get rid of it if
we think it is superfluous at this time though.

> This patch-set adds new snd_soc_dai_set_tdm_idle(), but no one is calling
> it. Who use it ??

Currently no one in tree consumes this API, and it is likely that no one will
until we submit our platform driver. See the linked resources in the cover
letter for more details.

In the interests of transparency, I don't expect to have the
platform driver ready for submission any time soon; it is a bit of a mess
and given that I did not write most of it I still need to wrap my head
around why certain decisions were made during its design. However, we
are 100% committed to getting it merged at some point.

If you would prefer I can resubmit this series along with the driver
once it is ready, so that there is an example consumer for the API.

Regards,

James Calligeros
Re: [PATCH v3 5/7] ASoC: soc-dai: add common operation to set TDM idle mode
Posted by Mark Brown 1 month, 1 week ago
On Tue, Mar 03, 2026 at 07:10:49PM +1000, James Calligeros wrote:
> On Tuesday, 3 March 2026 12:11:50 pm Australian Eastern Standard Time Kuninori 
> Morimoto wrote:

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

> > Do we need "rx_mode" ?
> > Only "tx_mode" is enough if my understanding was correct.

> For our (Asahi) purposes yes, we only require TX. However, TAS2770 also
> has a bus keeper on SDIN, and I suspect other hardware may too. Rather than
> break the API later on should the need to configure this arise,
> I decided to add the capability now. I am happy to get rid of it if
> we think it is superfluous at this time though.

I do think it's reasonable to keep things symmetric, it's probably
better to have an API that's consistent even if some of the code doesn't
currently get used.