[PATCH v2] ASoC: tas2770: Configure transmit clock edge when setting DAI format

From: James Calligeros

Date: Sat Oct 31 2026 - 00:56:48 EST


On reset, TAS2770 is configured to transmit on the falling edge
of the I2S clock. If BCLK is inverted, we are also receiving on the
falling edge. This can cause data leaving the codec to be corrupted.
Transmit should happen on the rising edge of the clock when receiving
on the falling edge, and vice versa.

Fix up tas2770_set_fmt to take into account the BLCK polarity and
always transmit on the correct edge of the clock. This fixes
corrupted VISENSE data on Apple Silicon platforms at sample rates
above 48 kHz.

Fixes: 1a476abc723e ("tas2770: add tas2770 smart PA kernel driver")
Signed-off-by: James Calligeros <jcalligeros99@xxxxxxxxx>
---
Changes in v2:
- Check return value when setting REG4...
- Move configuration from VISENSE transmit to DAI format setup
- Select TX clock edge based on the requested BCLK polarity
- Link to v1: https://patch.msgid.link/20261031-tas2770-sdout-clkedge-v1-1-5266daf35f4c@xxxxxxxxx
---
sound/soc/codecs/tas2770.c | 9 +++++++++
sound/soc/codecs/tas2770.h | 4 +++-
2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c
index 882e19857697..67ff2dd949be 100644
--- a/sound/soc/codecs/tas2770.c
+++ b/sound/soc/codecs/tas2770.c
@@ -353,6 +353,7 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct tas2770_priv *tas2770 =
snd_soc_component_get_drvdata(component);
u8 tdm_rx_start_slot = 0, invert_fpol = 0, fpol_preinv = 0, asi_cfg_1 = 0;
+ u8 tx_edge;
int ret;

switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
@@ -369,12 +370,14 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
fallthrough;
case SND_SOC_DAIFMT_NB_NF:
asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_RSING;
+ tx_edge = TAS2770_TDM_CFG_REG4_TX_FALING;
break;
case SND_SOC_DAIFMT_IB_IF:
invert_fpol = 1;
fallthrough;
case SND_SOC_DAIFMT_IB_NF:
asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_FALING;
+ tx_edge = TAS2770_TDM_CFG_REG4_TX_RISING;
break;
default:
dev_err(tas2770->dev, "ASI format Inverse is not found\n");
@@ -387,6 +390,12 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
if (ret < 0)
return ret;

+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4,
+ TAS2770_TDM_CFG_REG4_TX_EDGE,
+ tx_edge);
+ if (ret < 0)
+ return ret;
+
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
tdm_rx_start_slot = 1;
diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h
index 102040b6bdf8..666a9631c65d 100644
--- a/sound/soc/codecs/tas2770.h
+++ b/sound/soc/codecs/tas2770.h
@@ -74,7 +74,9 @@
#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)
+#define TAS2770_TDM_CFG_REG4_TX_EDGE BIT(0)
+#define TAS2770_TDM_CFG_REG4_TX_RISING 0x0
+#define TAS2770_TDM_CFG_REG4_TX_FALING 0x1
/* TDM Configuration Reg5 */
#define TAS2770_TDM_CFG_REG5 TAS2770_REG(0X0, 0x0F)
#define TAS2770_TDM_CFG_REG5_VSNS_MASK BIT(6)

---
base-commit: 11ae63bd9030349a0136fb2edd1622a4a029edc8
change-id: 20261030-tas2770-sdout-clkedge-b4e39db82720

Best regards,
--
James Calligeros <jcalligeros99@xxxxxxxxx>