From 5273cb8197d8eea1ab83ae3227a8c2db94177195 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 1 Mar 2026 18:05:20 +1000 Subject: ASoC: dt-bindings: convert tdm-slot to YAML This schema was still in plaintext form. Convert to YAML format. Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260301-tdm-idle-slots-v3-1-c6ac5351489a@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/tdm-slot.txt | 29 ------------------- .../devicetree/bindings/sound/tdm-slot.yaml | 33 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 29 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/tdm-slot.txt create mode 100644 Documentation/devicetree/bindings/sound/tdm-slot.yaml diff --git a/Documentation/devicetree/bindings/sound/tdm-slot.txt b/Documentation/devicetree/bindings/sound/tdm-slot.txt deleted file mode 100644 index 4bb513ae62fc..000000000000 --- a/Documentation/devicetree/bindings/sound/tdm-slot.txt +++ /dev/null @@ -1,29 +0,0 @@ -TDM slot: - -This specifies audio DAI's TDM slot. - -TDM slot properties: -dai-tdm-slot-num : Number of slots in use. -dai-tdm-slot-width : Width in bits for each slot. -dai-tdm-slot-tx-mask : Transmit direction slot mask, optional -dai-tdm-slot-rx-mask : Receive direction slot mask, optional - -For instance: - dai-tdm-slot-num = <2>; - dai-tdm-slot-width = <8>; - dai-tdm-slot-tx-mask = <0 1>; - dai-tdm-slot-rx-mask = <1 0>; - -And for each specified driver, there could be one .of_xlate_tdm_slot_mask() -to specify an explicit mapping of the channels and the slots. If it's absent -the default snd_soc_of_xlate_tdm_slot_mask() will be used to generating the -tx and rx masks. - -For snd_soc_of_xlate_tdm_slot_mask(), the tx and rx masks will use a 1 bit -for an active slot as default, and the default active bits are at the LSB of -the masks. - -The explicit masks are given as array of integers, where the first -number presents bit-0 (LSB), second presents bit-1, etc. Any non zero -number is considered 1 and 0 is 0. snd_soc_of_xlate_tdm_slot_mask() -does not do anything, if either mask is set non zero value. diff --git a/Documentation/devicetree/bindings/sound/tdm-slot.yaml b/Documentation/devicetree/bindings/sound/tdm-slot.yaml new file mode 100644 index 000000000000..ca71e3660f62 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tdm-slot.yaml @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/tdm-slot.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Time Division Multiplexing (TDM) Slot Parameters + +maintainers: + - Liam Girdwood + +select: false + +properties: + dai-tdm-slot-num: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of slots in use + + dai-tdm-slot-width: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Width, in bits, of each slot + +patternProperties: + '^dai-tdm-slot-[rt]x-mask$': + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Slot mask for active TDM slots. Optional. Drivers may + specify .xlate_tdm_slot_mask() to generate a slot mask dynamically. If + neither this property nor a driver-specific function are specified, the + default snd_soc_xlate_tdm_slot_mask() function will be used to generate + a mask. The first element of the array is slot 0 (LSB). Any nonzero + value will be treated as 1. + +additionalProperties: true -- cgit v1.2.3 From cc74890fb484a73420e46fc7be7bff8d02a82699 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 1 Mar 2026 18:05:21 +1000 Subject: ASoC: dt-bindings: update tdm-slot.txt references to tdm-slot.yaml Ensure that all references to tdm-slot.txt have been updated to tdm-slot.yaml, and are schema-compliant. Signed-off-by: James Calligeros Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260301-tdm-idle-slots-v3-2-c6ac5351489a@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/imx-audio-card.yaml | 9 +++------ Documentation/devicetree/bindings/sound/simple-card.yaml | 14 ++------------ 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/imx-audio-card.yaml b/Documentation/devicetree/bindings/sound/imx-audio-card.yaml index 3c75c8c78987..5424d4f16f52 100644 --- a/Documentation/devicetree/bindings/sound/imx-audio-card.yaml +++ b/Documentation/devicetree/bindings/sound/imx-audio-card.yaml @@ -24,6 +24,7 @@ patternProperties: cpu/codec dais. type: object + $ref: tdm-slot.yaml# properties: link-name: @@ -38,13 +39,9 @@ patternProperties: - i2s - dsp_b - dai-tdm-slot-num: - description: see tdm-slot.txt. - $ref: /schemas/types.yaml#/definitions/uint32 + dai-tdm-slot-num: true - dai-tdm-slot-width: - description: see tdm-slot.txt. - $ref: /schemas/types.yaml#/definitions/uint32 + dai-tdm-slot-width: true playback-only: description: link is used only for playback diff --git a/Documentation/devicetree/bindings/sound/simple-card.yaml b/Documentation/devicetree/bindings/sound/simple-card.yaml index 533d0a1da56e..a14716b2732f 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.yaml +++ b/Documentation/devicetree/bindings/sound/simple-card.yaml @@ -27,14 +27,6 @@ definitions: description: dai-link uses bit clock inversion $ref: /schemas/types.yaml#/definitions/flag - dai-tdm-slot-num: - description: see tdm-slot.txt. - $ref: /schemas/types.yaml#/definitions/uint32 - - dai-tdm-slot-width: - description: see tdm-slot.txt. - $ref: /schemas/types.yaml#/definitions/uint32 - system-clock-frequency: description: | If a clock is specified and a multiplication factor is given with @@ -115,6 +107,8 @@ definitions: dai: type: object + $ref: tdm-slot.yaml# + properties: sound-dai: maxItems: 1 @@ -133,10 +127,6 @@ definitions: bitclock-master: $ref: /schemas/types.yaml#/definitions/flag - dai-tdm-slot-num: - $ref: "#/definitions/dai-tdm-slot-num" - dai-tdm-slot-width: - $ref: "#/definitions/dai-tdm-slot-width" clocks: maxItems: 1 system-clock-frequency: -- cgit v1.2.3 From 938c1ed56ab888b0715a8c7070dbb4e276c2d3fe Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 1 Mar 2026 18:05:22 +1000 Subject: ASoC: dt-bindings: add TDM slot idle mode properties Add properties to describe TDM slot idle behaviour. These properties can be used to describe how a DAI is supposed to behave when not active. For example, Apple Silicon laptops split a single I2S bus between two physical lines which are combined at an OR gate in front of the receiving port. One codec on each line is expected to transmit zeroes during the active TDM slots of the opposite line, or we will have corrupted data at the receiver. Signed-off-by: James Calligeros Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260301-tdm-idle-slots-v3-3-c6ac5351489a@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/tdm-slot.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/tdm-slot.yaml b/Documentation/devicetree/bindings/sound/tdm-slot.yaml index ca71e3660f62..457a899e8872 100644 --- a/Documentation/devicetree/bindings/sound/tdm-slot.yaml +++ b/Documentation/devicetree/bindings/sound/tdm-slot.yaml @@ -20,6 +20,20 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 description: Width, in bits, of each slot + dai-tdm-idle-mode: + $ref: /schemas/types.yaml#/definitions/string + enum: + - none + - off + - zero + - pulldown + - hiz + - pullup + - drivehigh + description: Drive mode for inactive/idle TDM slots. For hardware that + implements .set_tdm_idle(). Optional. "None" represents undefined + behaviour and is the same as not setting this property. + patternProperties: '^dai-tdm-slot-[rt]x-mask$': $ref: /schemas/types.yaml#/definitions/uint32-array @@ -30,4 +44,9 @@ patternProperties: a mask. The first element of the array is slot 0 (LSB). Any nonzero value will be treated as 1. + '^dai-tdm-slot-[rt]x-idle-mask$': + $ref: /schemas/types.yaml#/definitions/uint32 + description: Idle slot mask. Optional. A bit being set to 1 indicates + that the corresponding TDM slot is inactive/idle. + additionalProperties: true -- cgit v1.2.3 From 7d8632f1ef6c8ed0b53771c16f130f18d636931e Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 1 Mar 2026 18:05:23 +1000 Subject: ASoC: soc-dai: define possible idle TDM slot modes Some audio devices, such as certain Texas Instruments codecs, include configurable bus keepers. We currently don't have a standardised way to configure such hardware, and instead rely on the hardware initialising setting itself up into a sane state. There are situations where this is insufficient, however, and some platforms require more concrete guarantees as to the state of the bus, and being able to explicitly configure bus keepers enables this. For example, some Apple Silicon machines have an odd bus topology where the SDOUT pins of all codecs are split across two data lines, which are summed via an OR gate in front of the receiving port on the SoC's I2S peripheral. Each line must transmit 0 while a codec on the other line is actively transmitting data, or the SoC will receive garbage data. To do this, one codec on each line must be configured to transmit zeroes during the other line's active TDM slots. Thus, we define seven possible bus-keeping modes that a device can be in: NONE (UB/as initialised), OFF (explicitly disabled), ZERO (actively transmit a 0), PULLDOWN, HIZ (floating), PULLUP, and DRIVE_HIGH. These will be consumed by CODEC/CPU drivers via a common DAI op, enabling the explicit configuration of bus keepers where required. Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260301-tdm-idle-slots-v3-4-c6ac5351489a@gmail.com Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 224396927aef..44dd06add52e 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -52,6 +52,21 @@ struct snd_compr_stream; #define SND_SOC_POSSIBLE_DAIFMT_AC97 (1 << SND_SOC_DAI_FORMAT_AC97) #define SND_SOC_POSSIBLE_DAIFMT_PDM (1 << SND_SOC_DAI_FORMAT_PDM) +/* + * DAI TDM slot idle modes + * + * Describes a CODEC/CPU's behaviour when not actively receiving or + * transmitting on a given TDM slot. NONE is undefined behaviour. + * Add new modes to the end. + */ +#define SND_SOC_DAI_TDM_IDLE_NONE 0 +#define SND_SOC_DAI_TDM_IDLE_OFF 1 +#define SND_SOC_DAI_TDM_IDLE_ZERO 2 +#define SND_SOC_DAI_TDM_IDLE_PULLDOWN 3 +#define SND_SOC_DAI_TDM_IDLE_HIZ 4 +#define SND_SOC_DAI_TDM_IDLE_PULLUP 5 +#define SND_SOC_DAI_TDM_IDLE_DRIVE_HIGH 6 + /* * DAI Clock gating. * -- cgit v1.2.3 From b758d3574e88537f9089bd757a51b35cf9675179 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 1 Mar 2026 18:05:24 +1000 Subject: ASoC: soc-dai: add common operation to set TDM idle mode Some audio devices, like certain Texas Instruments codecs, integrate configurable bus keepers that dictate the codec's behaviour during idle TDM slots. Now that we have definitions for various idle modes, add a snd_soc_dai_set_tdm_idle() operation to control this in a standardised way. This is useful on Apple Silicon laptops, where a single I2S bus is comprised of two physical lines which are ORed just before the receiving port. When a codec on one line is transmitting, we must guarantee that the other line is low. We can achieve this by configuring one codec on each line to use its bus keeper to fill its line with zeroes during the active slots of the other line. Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260301-tdm-idle-slots-v3-5-c6ac5351489a@gmail.com Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 7 +++++++ sound/soc/soc-dai.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 44dd06add52e..6a42812bba8c 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -196,6 +196,10 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); +int snd_soc_dai_set_tdm_idle(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int tx_mode, int rx_mode); + int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, const unsigned int *tx_slot, unsigned int rx_num, const unsigned int *rx_slot); @@ -312,6 +316,9 @@ struct snd_soc_dai_ops { int (*set_tdm_slot)(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); + int (*set_tdm_idle)(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int tx_mode, int rx_mode); int (*set_channel_map)(struct snd_soc_dai *dai, unsigned int tx_num, const unsigned int *tx_slot, unsigned int rx_num, const unsigned int *rx_slot); diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index a1e05307067d..2f370fda1266 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -282,6 +282,46 @@ err: } EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); +/** + * snd_soc_dai_set_tdm_idle() - Configure a DAI's TDM idle mode + * @dai: The DAI to configure + * @tx_mask: bitmask representing idle TX slots. + * @rx_mask: bitmask representing idle RX slots. + * @tx_mode: idle mode to set for TX slots. + * @rx_mode: idle mode to set for RX slots. + * + * This function configures the DAI to handle idle TDM slots in the + * specified manner. @tx_mode and @rx_mode can be one of + * SND_SOC_DAI_TDM_IDLE_NONE, SND_SOC_DAI_TDM_IDLE_ZERO, + * SND_SOC_DAI_TDM_IDLE_PULLDOWN, or SND_SOC_DAI_TDM_IDLE_HIZ. + * SND_SOC_TDM_IDLE_NONE represents the DAI's default/unset idle slot + * handling state and could be any of the other modes depending on the + * hardware behind the DAI. It is therefore undefined behaviour when set + * explicitly. + * + * Mode and mask can be set independently for both the TX and RX direction. + * Some hardware may ignore both TX and RX masks depending on its + * capabilities. + */ +int snd_soc_dai_set_tdm_idle(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int tx_mode, int rx_mode) +{ + int ret = -EOPNOTSUPP; + + /* You can't write to the RX line */ + if (rx_mode == SND_SOC_DAI_TDM_IDLE_ZERO) + return soc_dai_ret(dai, -EINVAL); + + if (dai->driver->ops && + dai->driver->ops->set_tdm_idle) + ret = dai->driver->ops->set_tdm_idle(dai, tx_mask, rx_mask, + tx_mode, rx_mode); + + return soc_dai_ret(dai, ret); +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_idle); + /** * snd_soc_dai_set_channel_map - configure DAI audio channel map * @dai: DAI -- cgit v1.2.3 From 45573ee1f2badd1886eb6f4a736e60e3f76effe6 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 1 Mar 2026 18:05:25 +1000 Subject: ASoC: tas2764: expose SDOUT bus keeper via set_tdm_idle operation TAS2764, and the Apple-exclusive variant SN012776, include bus keepers on the SDOUT pin that can be configured to alter the behaviour of the SDOUT pin during specified TDM slots. The chip can either leave the bus floating (default/uninitialised behaviour) or fill the specified slots with zeroes. Expose the SDOUT bus keeper and allow it to be configured using the set_tdm_idle DAI op. The mask must be cropped to only cover slots valid for the configured BCLK ratio, so introduce a set_bclk_ratio op that properly configures this. Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260301-tdm-idle-slots-v3-6-c6ac5351489a@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2764.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tas2764.h | 11 ++++++ 2 files changed, 106 insertions(+) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 36e25e48b354..423b7073b302 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -44,6 +44,11 @@ struct tas2764_priv { bool dac_powered; bool unmuted; + + struct { + int tx_mode; + unsigned int tx_mask; + } idle_slot_config; }; #include "tas2764-quirks.h" @@ -509,11 +514,101 @@ static int tas2764_set_dai_tdm_slot(struct snd_soc_dai *dai, return 0; } +static int tas2764_write_sdout_idle_mask(struct tas2764_priv *tas2764, u32 mask) +{ + struct snd_soc_component *component = tas2764->component; + int i, ret; + + /* Hardware supports up to 64 slots, but we don't */ + for (i = 0; i < 4; i++) { + ret = snd_soc_component_write(component, + TAS2764_SDOUT_HIZ_1 + i, + (mask >> (i * 8)) & 0xff); + if (ret < 0) + return ret; + } + + return 0; +} + +static int tas2764_set_dai_tdm_idle(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int tx_mode, int rx_mode) +{ + struct snd_soc_component *component = dai->component; + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int ret; + + /* We don't support setting anything on SDIN */ + if (rx_mode) + return -EOPNOTSUPP; + + if (tas2764->idle_slot_config.tx_mask == tx_mask && + tas2764->idle_slot_config.tx_mode == tx_mode) + return 0; + + switch (tx_mode) { + case SND_SOC_DAI_TDM_IDLE_ZERO: + if (!tx_mask) + return -EINVAL; + + ret = tas2764_write_sdout_idle_mask(tas2764, tx_mask); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, + TAS2764_SDOUT_HIZ_9, + TAS2764_SDOUT_HIZ_9_FORCE_0_EN, + TAS2764_SDOUT_HIZ_9_FORCE_0_EN); + if (ret < 0) + return ret; + + tas2764->idle_slot_config.tx_mask = tx_mask; + tas2764->idle_slot_config.tx_mode = tx_mode; + break; + case SND_SOC_DAI_TDM_IDLE_HIZ: + case SND_SOC_DAI_TDM_IDLE_OFF: + /* HiZ mode does not support a slot mask */ + ret = tas2764_write_sdout_idle_mask(tas2764, 0); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, + TAS2764_SDOUT_HIZ_9, + TAS2764_SDOUT_HIZ_9_FORCE_0_EN, 0); + if (ret < 0) + return ret; + + tas2764->idle_slot_config.tx_mask = 0; + tas2764->idle_slot_config.tx_mode = tx_mode; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* The SDOUT idle slot mask must be cropped based on the BCLK ratio */ +static int tas2764_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(dai->component); + + if (!tas2764->idle_slot_config.tx_mask) + return 0; + + tas2764->idle_slot_config.tx_mask &= GENMASK((ratio / 8) - 1, 0); + + return tas2764_write_sdout_idle_mask(tas2764, tas2764->idle_slot_config.tx_mask); +} + static const struct snd_soc_dai_ops tas2764_dai_ops = { .mute_stream = tas2764_mute, .hw_params = tas2764_hw_params, .set_fmt = tas2764_set_fmt, + .set_bclk_ratio = tas2764_set_bclk_ratio, .set_tdm_slot = tas2764_set_dai_tdm_slot, + .set_tdm_idle = tas2764_set_dai_tdm_idle, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h index 538290ed3d92..4494bc4889dc 100644 --- a/sound/soc/codecs/tas2764.h +++ b/sound/soc/codecs/tas2764.h @@ -126,4 +126,15 @@ #define TAS2764_BOP_CFG0 TAS2764_REG(0X0, 0x1d) +#define TAS2764_SDOUT_HIZ_1 TAS2764_REG(0x1, 0x3d) +#define TAS2764_SDOUT_HIZ_2 TAS2764_REG(0x1, 0x3e) +#define TAS2764_SDOUT_HIZ_3 TAS2764_REG(0x1, 0x3f) +#define TAS2764_SDOUT_HIZ_4 TAS2764_REG(0x1, 0x40) +#define TAS2764_SDOUT_HIZ_5 TAS2764_REG(0x1, 0x41) +#define TAS2764_SDOUT_HIZ_6 TAS2764_REG(0x1, 0x42) +#define TAS2764_SDOUT_HIZ_7 TAS2764_REG(0x1, 0x43) +#define TAS2764_SDOUT_HIZ_8 TAS2764_REG(0x1, 0x44) +#define TAS2764_SDOUT_HIZ_9 TAS2764_REG(0x1, 0x45) +#define TAS2764_SDOUT_HIZ_9_FORCE_0_EN BIT(7) + #endif /* __TAS2764__ */ -- cgit v1.2.3 From af176d0787d219d9e07272988079ebb9be8efe6a Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 1 Mar 2026 18:05:26 +1000 Subject: ASoC: tas2770: expose SDOUT bus keeper via set_tdm_idle TAS2770 includes a bus keeper which can be used to control the behaviour of the SDOUT pin during specified TDM slots. The chip can either pull the pin to ground, actively transmit zeroes, or keep the pin floating (default/uninitialised behaviour). Expose the bus keeper via the set_tdm_idle DAI op so that it can be configured by consumers. Signed-off-by: James Calligeros Link: https://patch.msgid.link/20260301-tdm-idle-slots-v3-7-c6ac5351489a@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tas2770.h | 12 ++++++++ 2 files changed, 87 insertions(+) diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 6f878b01716f..d4d7d056141b 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -492,11 +492,86 @@ static int tas2770_set_dai_tdm_slot(struct snd_soc_dai *dai, return 0; } +static int tas2770_set_dai_tdm_idle(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int tx_mode, int rx_mode) +{ + struct snd_soc_component *component = dai->component; + struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + int ret; + + /* We don't support setting anything for SDIN */ + if (rx_mode) + return -EOPNOTSUPP; + + if (tas2770->idle_tx_mode == tx_mode) + return 0; + + switch (tx_mode) { + case SND_SOC_DAI_TDM_IDLE_PULLDOWN: + ret = snd_soc_component_update_bits(component, TAS2770_DIN_PD, + TAS2770_DIN_PD_SDOUT, + TAS2770_DIN_PD_SDOUT); + if (ret) + return ret; + + break; + case SND_SOC_DAI_TDM_IDLE_ZERO: + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, + TAS2770_TDM_CFG_REG4_TX_KEEPER, + TAS2770_TDM_CFG_REG4_TX_KEEPER); + if (ret) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, + TAS2770_TDM_CFG_REG4_TX_FILL, 0); + if (ret) + return ret; + + break; + case SND_SOC_DAI_TDM_IDLE_HIZ: + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, + TAS2770_TDM_CFG_REG4_TX_KEEPER, + TAS2770_TDM_CFG_REG4_TX_KEEPER); + if (ret) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, + TAS2770_TDM_CFG_REG4_TX_FILL, + TAS2770_TDM_CFG_REG4_TX_FILL); + if (ret) + return ret; + + break; + case SND_SOC_DAI_TDM_IDLE_OFF: + ret = snd_soc_component_update_bits(component, TAS2770_DIN_PD, + TAS2770_DIN_PD_SDOUT, 0); + if (ret) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, + TAS2770_TDM_CFG_REG4_TX_KEEPER, 0); + if (ret) + return ret; + + break; + + default: + return -EOPNOTSUPP; + } + + tas2770->idle_tx_mode = tx_mode; + + return 0; +} + static const struct snd_soc_dai_ops tas2770_dai_ops = { .mute_stream = tas2770_mute, .hw_params = tas2770_hw_params, .set_fmt = tas2770_set_fmt, .set_tdm_slot = tas2770_set_dai_tdm_slot, + .set_tdm_idle = tas2770_set_dai_tdm_idle, .no_capture_mute = 1, }; diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h index 3fd2e7003c50..102040b6bdf8 100644 --- a/sound/soc/codecs/tas2770.h +++ b/sound/soc/codecs/tas2770.h @@ -67,6 +67,14 @@ #define TAS2770_TDM_CFG_REG3_RXS_SHIFT 0x4 #define TAS2770_TDM_CFG_REG3_30_MASK GENMASK(3, 0) #define TAS2770_TDM_CFG_REG3_30_SHIFT 0 + /* TDM Configuration Reg4 */ +#define TAS2770_TDM_CFG_REG4 TAS2770_REG(0X0, 0x0E) +#define TAS2770_TDM_CFG_REG4_TX_LSB_CFG BIT(7) +#define TAS2770_TDM_CFG_REG4_TX_KEEPER_CFG BIT(6) +#define TAS2770_TDM_CFG_REG4_TX_KEEPER BIT(5) +#define TAS2770_TDM_CFG_REG4_TX_FILL BIT(4) +#define TAS2770_TDM_CFG_REG4_TX_OFFSET_MASK GENMASK(3, 1) +#define TAS2770_TDM_CFG_REG4_TX_EDGE_FALLING BIT(0) /* TDM Configuration Reg5 */ #define TAS2770_TDM_CFG_REG5 TAS2770_REG(0X0, 0x0F) #define TAS2770_TDM_CFG_REG5_VSNS_MASK BIT(6) @@ -115,6 +123,9 @@ #define TAS2770_TEMP_LSB TAS2770_REG(0X0, 0x2A) /* Interrupt Configuration */ #define TAS2770_INT_CFG TAS2770_REG(0X0, 0x30) + /* Data In Pull-Down */ +#define TAS2770_DIN_PD TAS2770_REG(0X0, 0x31) +#define TAS2770_DIN_PD_SDOUT BIT(7) /* Misc IRQ */ #define TAS2770_MISC_IRQ TAS2770_REG(0X0, 0x32) /* Clock Configuration */ @@ -146,6 +157,7 @@ struct tas2770_priv { int pdm_slot; bool dac_powered; bool unmuted; + int idle_tx_mode; }; #endif /* __TAS2770__ */ -- cgit v1.2.3