* [PATCH RFC 0/3] Initial TFA2 family driver for tfa98xx
@ 2026-08-02 12:42 David Heidelberg via B4 Relay
2026-08-02 12:42 ` [PATCH RFC 1/3] dt-bindings: sound: Add NXP TFA98XX TFA2 amplifier binding David Heidelberg via B4 Relay
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: David Heidelberg via B4 Relay @ 2026-08-02 12:42 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jaroslav Kysela, Takashi Iwai, Bjorn Andersson,
Konrad Dybcio
Cc: linux-sound, devicetree, linux-kernel, linux-arm-msm, phone-devel,
David Heidelberg
Introduce basic speaker support for the OnePlus 6T, using the tfa98xx
driver.
I've tested this driver only with speaker on OnePlus 6T. Please be
careful with volume levels, as the speaker protection are available only
with CoolFlux DSP enabled.
The downstream driver requires the firmware to bring up CoolFlux DSP,
but this driver completly avoids the DSP and thus operates without the
firmware.
This driver is different enough to not share the TFA1 codebase (tfa989x).
In the future, should be possible to extend this driver with other
tfa98xx TFA2 family based chips.
This RFC version mainly exist for early adopters (e.g. other phones
using tfa98xx amplifiers).
You can find the code at:
https://codeberg.org/sdm845/linux/commits/branch/b4/tfa98xx-upstream
Signed-off-by: David Heidelberg <david@ixit.cz>
---
David Heidelberg (3):
dt-bindings: sound: Add NXP TFA98XX TFA2 amplifier binding
ASoC: codecs: tfa98xx: add driver for TFA2 amplifiers
arm64: dts: qcom: sdm845-oneplus-fajita: Enable speaker codec
.../devicetree/bindings/sound/nxp,tfa98xx.yaml | 54 +++
MAINTAINERS | 7 +
arch/arm64/boot/dts/qcom/sdm845-oneplus-fajita.dts | 22 +-
sound/soc/codecs/Kconfig | 11 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tfa98xx.c | 421 +++++++++++++++++++++
6 files changed, 510 insertions(+), 7 deletions(-)
---
base-commit: 95d6a9ccef99117115e41e9adb271243bd5e985b
change-id: 20260802-tfa98xx-upstream-fa89ce77b7fb
Best regards,
--
David Heidelberg <david@ixit.cz>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH RFC 1/3] dt-bindings: sound: Add NXP TFA98XX TFA2 amplifier binding
2026-08-02 12:42 [PATCH RFC 0/3] Initial TFA2 family driver for tfa98xx David Heidelberg via B4 Relay
@ 2026-08-02 12:42 ` David Heidelberg via B4 Relay
2026-08-02 12:45 ` sashiko-bot
2026-08-02 12:42 ` [PATCH RFC 2/3] ASoC: codecs: tfa98xx: add driver for TFA2 amplifiers David Heidelberg via B4 Relay
2026-08-02 12:42 ` [PATCH RFC 3/3] arm64: dts: qcom: sdm845-oneplus-fajita: Enable speaker codec David Heidelberg via B4 Relay
2 siblings, 1 reply; 7+ messages in thread
From: David Heidelberg via B4 Relay @ 2026-08-02 12:42 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jaroslav Kysela, Takashi Iwai, Bjorn Andersson,
Konrad Dybcio
Cc: linux-sound, devicetree, linux-kernel, linux-arm-msm, phone-devel,
David Heidelberg
From: David Heidelberg <david@ixit.cz>
Add a YAML devicetree binding schema for the NXP/Goodix TFA98XX TFA2
family of I2C-controlled smart speaker amplifiers with on-chip DSP.
The binding covers supported chip variants (TFA9872 through
TFA9912) and documents the optional reset and IRQ GPIO properties.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
.../devicetree/bindings/sound/nxp,tfa98xx.yaml | 54 ++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/nxp,tfa98xx.yaml b/Documentation/devicetree/bindings/sound/nxp,tfa98xx.yaml
new file mode 100644
index 0000000000000..e80c046894454
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nxp,tfa98xx.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: (GPL-2.0-only)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nxp,tfa98xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP/Goodix TFA98xx (TFA2) smart speaker amplifiers
+
+maintainers:
+ - David Heidelberg <david@ixit.cz>
+
+description:
+ The TFA98xx (TFA2) family are I2C-controlled class-D speaker amplifiers
+ with an integrated CoolFlux DSP.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - nxp,tfa9894
+
+ reg:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - '#sound-dai-cells'
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec@34 {
+ compatible = "nxp,tfa9894";
+ reg = <0x34>;
+ reset-gpios = <&tlmm 69 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ };
+ };
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RFC 2/3] ASoC: codecs: tfa98xx: add driver for TFA2 amplifiers
2026-08-02 12:42 [PATCH RFC 0/3] Initial TFA2 family driver for tfa98xx David Heidelberg via B4 Relay
2026-08-02 12:42 ` [PATCH RFC 1/3] dt-bindings: sound: Add NXP TFA98XX TFA2 amplifier binding David Heidelberg via B4 Relay
@ 2026-08-02 12:42 ` David Heidelberg via B4 Relay
2026-08-02 12:48 ` sashiko-bot
2026-08-02 12:42 ` [PATCH RFC 3/3] arm64: dts: qcom: sdm845-oneplus-fajita: Enable speaker codec David Heidelberg via B4 Relay
2 siblings, 1 reply; 7+ messages in thread
From: David Heidelberg via B4 Relay @ 2026-08-02 12:42 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jaroslav Kysela, Takashi Iwai, Bjorn Andersson,
Konrad Dybcio
Cc: linux-sound, devicetree, linux-kernel, linux-arm-msm, phone-devel,
David Heidelberg
From: David Heidelberg <david@ixit.cz>
Add a driver for the NXP/Goodix TFA98xx (TFA2) smart speaker amplifiers,
initially supporting the TFA9894 found on the OnePlus 6T. The per-die
revision power-on correction sequences are transcribed from the vendor
driver.
Like the existing TFA1 driver, this bypasses the built-in CoolFlux DSP,
which needs an undocumented vendor firmware container to run. On TFA2 the
AMPC bit hands control over the amplifier to the DSP and is set out of
reset, so both CFE and AMPC have to be cleared for the amplifier enable
to be driven through DAPM. The amplifier input is taken straight from the
TDM interface. Speaker protection and volume control are consequently not
available.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
MAINTAINERS | 7 +
sound/soc/codecs/Kconfig | 11 ++
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tfa98xx.c | 421 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 441 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 56b337a064478..f41a7d70bab7e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19735,16 +19735,23 @@ F: drivers/nfc/nxp-nci
NXP/Goodix TFA989X (TFA1) DRIVER
M: Stephan Gerhold <stephan@gerhold.net>
L: linux-sound@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml
F: sound/soc/codecs/tfa989x.c
+NXP/GOODIX TFA98XX (TFA2) DRIVER
+M: David Heidelberg <david@ixit.cz>
+L: linux-sound@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/sound/nxp,tfa98xx.yaml
+F: sound/soc/codecs/tfa98xx.c
+
NZXT-KRAKEN2 HARDWARE MONITORING DRIVER
M: Jonas Malaco <jonas@protocubo.io>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/nzxt-kraken2.rst
F: drivers/hwmon/nzxt-kraken2.c
NZXT-KRAKEN3 HARDWARE MONITORING DRIVER
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index bfd81ca89110e..db4d519627ae1 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -275,16 +275,17 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_TAS2781_I2C
imply SND_SOC_TAS2783_SDW
imply SND_SOC_TAS5086
imply SND_SOC_TAS571X
imply SND_SOC_TAS5720
imply SND_SOC_TAS6424
imply SND_SOC_TAS675X
imply SND_SOC_TDA7419
+ imply SND_SOC_TFA98XX
imply SND_SOC_TFA9879
imply SND_SOC_TFA989X
imply SND_SOC_TLV320ADC3XXX
imply SND_SOC_TLV320ADCX140
imply SND_SOC_TLV320AIC23_I2C
imply SND_SOC_TLV320AIC23_SPI
imply SND_SOC_TLV320AIC26
imply SND_SOC_TLV320AIC31XX
@@ -2291,16 +2292,26 @@ config SND_SOC_TFA989X
depends on I2C
select REGMAP_I2C
help
Enable support for NXP (now Goodix) TFA989X (TFA1 family) speaker
amplifiers, e.g. TFA9895.
Note that the driver currently bypasses the built-in "CoolFlux DSP"
and does not support (hardware) volume control.
+config SND_SOC_TFA98XX
+ tristate "NXP/Goodix TFA98XX (TFA2) amplifiers"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for NXP (now Goodix) TFA98XX (TFA2 family) smart
+ speaker amplifiers, e.g. TFA9894.
+ Note that the driver bypasses the built-in "CoolFlux DSP" and
+ therefore provides no speaker protection or volume control.
+
config SND_SOC_TLV320ADC3XXX
tristate "Texas Instruments TLV320ADC3001/3101 audio ADC"
depends on I2C
depends on GPIOLIB
help
Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101
ADCs.
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index d2a689006d69c..b1f811452cdab 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -327,16 +327,17 @@ snd-soc-tda7419-y := tda7419.o
snd-soc-tas2770-y := tas2770.o
snd-soc-tas2781-comlib-y := tas2781-comlib.o
snd-soc-tas2781-comlib-i2c-y := tas2781-comlib-i2c.o
snd-soc-tas2781-fmwlib-y := tas2781-fmwlib.o
snd-soc-tas2781-i2c-y := tas2781-i2c.o
snd-soc-tas2783-sdw-y := tas2783-sdw.o
snd-soc-tfa9879-y := tfa9879.o
snd-soc-tfa989x-y := tfa989x.o
+snd-soc-tfa98xx-y := tfa98xx.o
snd-soc-tlv320adc3xxx-y := tlv320adc3xxx.o
snd-soc-tlv320aic23-y := tlv320aic23.o
snd-soc-tlv320aic23-i2c-y := tlv320aic23-i2c.o
snd-soc-tlv320aic23-spi-y := tlv320aic23-spi.o
snd-soc-tlv320aic26-y := tlv320aic26.o
snd-soc-tlv320aic31xx-y := tlv320aic31xx.o
snd-soc-tlv320aic32x4-y := tlv320aic32x4.o tlv320aic32x4-clk.o
snd-soc-tlv320aic32x4-i2c-y := tlv320aic32x4-i2c.o
@@ -769,16 +770,17 @@ obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
obj-$(CONFIG_SND_SOC_TAS5805M) += snd-soc-tas5805m.o
obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o
obj-$(CONFIG_SND_SOC_TAS675X) += snd-soc-tas675x.o
obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o
obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o
obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
obj-$(CONFIG_SND_SOC_TFA989X) += snd-soc-tfa989x.o
+obj-$(CONFIG_SND_SOC_TFA98XX) += snd-soc-tfa98xx.o
obj-$(CONFIG_SND_SOC_TLV320ADC3XXX) += snd-soc-tlv320adc3xxx.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC31XX) += snd-soc-tlv320aic31xx.o
obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o
obj-$(CONFIG_SND_SOC_TLV320AIC32X4_I2C) += snd-soc-tlv320aic32x4-i2c.o
diff --git a/sound/soc/codecs/tfa98xx.c b/sound/soc/codecs/tfa98xx.c
new file mode 100644
index 0000000000000..c84abb8db207a
--- /dev/null
+++ b/sound/soc/codecs/tfa98xx.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for NXP/Goodix TFA98xx (TFA2) smart speaker amplifiers.
+ *
+ * Copyright David Heidelberg <david@ixit.cz>
+ *
+ * Register sequences taken from the NXP/Goodix vendor driver:
+ * Copyright NXP Semiconductors
+ * Copyright GOODIX
+ *
+ * These amplifiers contain a CoolFlux DSP which needs a vendor-specific
+ * firmware container to run. This driver keeps the DSP disabled and feeds
+ * the TDM input straight to the amplifier, so the speaker protection
+ * (excursion and thermal modelling) provided by the DSP is not available.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define TFA98XX_SYS_CTRL0 0x00
+#define TFA98XX_SYS_CTRL0_PWDN 0 /* power down */
+#define TFA98XX_SYS_CTRL0_CFE 2 /* enable CoolFlux DSP */
+#define TFA98XX_SYS_CTRL0_AMPE 3 /* enable amplifier */
+#define TFA98XX_SYS_CTRL0_AMPC 6 /* amplifier enabled by DSP */
+
+#define TFA98XX_SYS_CTRL1 0x01
+#define TFA98XX_SYS_CTRL1_AMPINSEL_MSK GENMASK(1, 0) /* amp input select */
+#define TFA98XX_SYS_CTRL1_MANSCONF BIT(2) /* I2C settings configured */
+
+#define TFA98XX_AUDIO_CTRL 0x02
+#define TFA98XX_AUDIO_CTRL_AUDFS_MSK GENMASK(3, 0) /* sample rate */
+
+#define TFA98XX_REVISION 0x03
+#define TFA98XX_REVISION_ID_MSK GENMASK(7, 0)
+
+/* Status and live-data registers, 0x10..0x1f */
+#define TFA98XX_STATUS_FIRST 0x10
+#define TFA98XX_STATUS_LAST 0x1f
+
+#define TFA98XX_TDM_CFG0 0x20
+#define TFA98XX_TDM_CFG0_TDME BIT(0) /* enable interface */
+#define TFA98XX_TDM_CFG0_TDMSPKE BIT(1) /* enable audio sink */
+
+#define TFA98XX_TDM_CFG1 0x21
+#define TFA98XX_TDM_CFG1_NBCK_MSK GENMASK(3, 0) /* BCK to FS ratio */
+
+#define TFA98XX_TDM_CFG2 0x22
+#define TFA98XX_TDM_CFG2_SLLN_MSK GENMASK(4, 0) /* bits per slot */
+#define TFA98XX_TDM_CFG2_SSIZE_MSK GENMASK(14, 10) /* sample size */
+
+#define TFA98XX_TDM_CFG3 0x23
+#define TFA98XX_TDM_CFG3_SPKS_MSK GENMASK(3, 0) /* slot for sink 0 */
+
+#define TFA98XX_KEY1 0x0f
+#define TFA98XX_KEY1_UNHIDE 0x5a6b
+
+#define TFA9894_REVISION 0x94
+
+struct tfa98xx_rev {
+ unsigned int rev;
+ const struct reg_sequence *init;
+ unsigned int num_init;
+};
+
+struct tfa98xx_chip {
+ unsigned int id;
+ const struct tfa98xx_rev *revs;
+ unsigned int num_revs;
+};
+
+struct tfa98xx {
+ struct regmap *regmap;
+};
+
+/*
+ * Correction of the power-on defaults, taken verbatim from the vendor
+ * driver. The values differ per die revision.
+ */
+static const struct reg_sequence tfa9894_rev0a_init[] = {
+ { 0x00, 0xa245 }, { 0x02, 0x51e8 }, { 0x52, 0xbe17 },
+ { 0x57, 0x0344 }, { 0x61, 0x0033 }, { 0x71, 0x00cf },
+ { 0x72, 0x34a9 }, { 0x73, 0x3808 }, { 0x76, 0x0067 },
+ { 0x80, 0x0000 }, { 0x81, 0x5715 }, { 0x82, 0x0104 },
+};
+
+static const struct reg_sequence tfa9894_rev1a_init[] = {
+ { 0x00, 0xa245 }, { 0x01, 0x15da }, { 0x02, 0x5288 },
+ { 0x52, 0xbe17 }, { 0x53, 0x0dbe }, { 0x56, 0x05c3 },
+ { 0x57, 0x0344 }, { 0x61, 0x0032 }, { 0x71, 0x00cf },
+ { 0x72, 0x34a9 }, { 0x73, 0x38c8 }, { 0x76, 0x0067 },
+ { 0x80, 0x0000 }, { 0x81, 0x5799 }, { 0x82, 0x0104 },
+};
+
+static const struct reg_sequence tfa9894_rev2a_init[] = {
+ { 0x01, 0x15da }, { 0x02, 0x51e8 }, { 0x04, 0x0200 },
+ { 0x52, 0xbe17 }, { 0x53, 0x0dbe }, { 0x57, 0x0344 },
+ { 0x61, 0x0032 }, { 0x71, 0x6ecf }, { 0x72, 0xb4a9 },
+ { 0x73, 0x38c8 }, { 0x76, 0x0067 }, { 0x80, 0x0000 },
+ { 0x81, 0x5799 }, { 0x82, 0x0104 },
+};
+
+static const struct tfa98xx_rev tfa9894_revs[] = {
+ { 0x0a94, tfa9894_rev0a_init, ARRAY_SIZE(tfa9894_rev0a_init) },
+ { 0x1a94, tfa9894_rev1a_init, ARRAY_SIZE(tfa9894_rev1a_init) },
+ { 0x2a94, tfa9894_rev2a_init, ARRAY_SIZE(tfa9894_rev2a_init) },
+ { 0x3a94, tfa9894_rev2a_init, ARRAY_SIZE(tfa9894_rev2a_init) },
+};
+
+static const struct tfa98xx_chip tfa9894_chip = {
+ .id = TFA9894_REVISION,
+ .revs = tfa9894_revs,
+ .num_revs = ARRAY_SIZE(tfa9894_revs),
+};
+
+static bool tfa98xx_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg >= TFA98XX_STATUS_FIRST && reg <= TFA98XX_STATUS_LAST;
+}
+
+static bool tfa98xx_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return reg != TFA98XX_REVISION && !tfa98xx_volatile_reg(dev, reg);
+}
+
+static const struct regmap_config tfa98xx_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = 0xff,
+ .writeable_reg = tfa98xx_writeable_reg,
+ .volatile_reg = tfa98xx_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct snd_soc_dapm_widget tfa98xx_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_SUPPLY("POWER", TFA98XX_SYS_CTRL0,
+ TFA98XX_SYS_CTRL0_PWDN, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("AMPE", TFA98XX_SYS_CTRL0,
+ TFA98XX_SYS_CTRL0_AMPE, 0, NULL, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route tfa98xx_dapm_routes[] = {
+ { "OUT", NULL, "AMPE" },
+ { "AMPE", NULL, "POWER" },
+ { "AMPE", NULL, "AIFIN" },
+};
+
+static const struct snd_soc_component_driver tfa98xx_component = {
+ .dapm_widgets = tfa98xx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tfa98xx_dapm_widgets),
+ .dapm_routes = tfa98xx_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tfa98xx_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+/* Indexed by the AUDFS field value */
+static const unsigned int tfa98xx_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static int tfa98xx_find_sample_rate(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tfa98xx_rates); ++i)
+ if (tfa98xx_rates[i] == rate)
+ return i;
+
+ return -EINVAL;
+}
+
+static int tfa98xx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int nbck, slotlen, samplesize;
+ int sr, ret;
+
+ sr = tfa98xx_find_sample_rate(params_rate(params));
+ if (sr < 0)
+ return sr;
+
+ switch (params_width(params)) {
+ case 16:
+ /* 16-bit sample in a 16-bit slot, 32 BCK per frame */
+ nbck = 0;
+ slotlen = 16 - 1;
+ samplesize = 16 - 1;
+ break;
+ case 24:
+ case 32:
+ /* 24-bit sample in a 32-bit slot, 64 BCK per frame */
+ nbck = 2;
+ slotlen = 32 - 1;
+ samplesize = 24 - 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TFA98XX_AUDIO_CTRL,
+ TFA98XX_AUDIO_CTRL_AUDFS_MSK,
+ FIELD_PREP(TFA98XX_AUDIO_CTRL_AUDFS_MSK, sr));
+ if (ret < 0)
+ return ret;
+
+ /* The interface must be disabled while its framing is reprogrammed */
+ ret = snd_soc_component_update_bits(component, TFA98XX_TDM_CFG0,
+ TFA98XX_TDM_CFG0_TDME, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, TFA98XX_TDM_CFG1,
+ TFA98XX_TDM_CFG1_NBCK_MSK,
+ FIELD_PREP(TFA98XX_TDM_CFG1_NBCK_MSK, nbck));
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, TFA98XX_TDM_CFG2,
+ TFA98XX_TDM_CFG2_SLLN_MSK |
+ TFA98XX_TDM_CFG2_SSIZE_MSK,
+ FIELD_PREP(TFA98XX_TDM_CFG2_SLLN_MSK, slotlen) |
+ FIELD_PREP(TFA98XX_TDM_CFG2_SSIZE_MSK, samplesize));
+ if (ret < 0)
+ return ret;
+
+ return snd_soc_component_update_bits(component, TFA98XX_TDM_CFG0,
+ TFA98XX_TDM_CFG0_TDME,
+ TFA98XX_TDM_CFG0_TDME);
+}
+
+static int tfa98xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tfa98xx_dai_ops = {
+ .hw_params = tfa98xx_hw_params,
+ .set_fmt = tfa98xx_set_fmt,
+};
+
+static struct snd_soc_dai_driver tfa98xx_dai = {
+ .name = "tfa98xx-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tfa98xx_dai_ops,
+};
+
+static int tfa98xx_init(struct regmap *regmap, const struct tfa98xx_rev *rev)
+{
+ int ret;
+
+ /* The correction sequences touch registers behind the hide key */
+ ret = regmap_write(regmap, TFA98XX_KEY1, TFA98XX_KEY1_UNHIDE);
+ if (ret)
+ return ret;
+
+ ret = regmap_multi_reg_write(regmap, rev->init, rev->num_init);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(regmap, TFA98XX_KEY1, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Bypass the CoolFlux DSP: without the vendor firmware container it has
+ * nothing to run. AMPC hands control over the amplifier to the DSP and
+ * is set out of reset, so it has to be cleared as well - otherwise AMPE
+ * has no effect and the amplifier stays silent.
+ */
+ ret = regmap_clear_bits(regmap, TFA98XX_SYS_CTRL0,
+ BIT(TFA98XX_SYS_CTRL0_CFE) |
+ BIT(TFA98XX_SYS_CTRL0_AMPC));
+ if (ret)
+ return ret;
+
+ /* Take the amplifier input straight from the TDM interface */
+ ret = regmap_update_bits(regmap, TFA98XX_SYS_CTRL1,
+ TFA98XX_SYS_CTRL1_AMPINSEL_MSK, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Tell the hardware manager that the I2C configuration is complete,
+ * otherwise it never leaves the wait-for-settings state. The amplifier
+ * stays powered down until DAPM clears PWDN.
+ */
+ ret = regmap_set_bits(regmap, TFA98XX_SYS_CTRL1,
+ TFA98XX_SYS_CTRL1_MANSCONF);
+ if (ret)
+ return ret;
+
+ /* Route slot 0 of the TDM frame into the amplifier */
+ ret = regmap_update_bits(regmap, TFA98XX_TDM_CFG3,
+ TFA98XX_TDM_CFG3_SPKS_MSK,
+ FIELD_PREP(TFA98XX_TDM_CFG3_SPKS_MSK, 0));
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(regmap, TFA98XX_TDM_CFG0,
+ TFA98XX_TDM_CFG0_TDMSPKE);
+}
+
+static int tfa98xx_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ const struct tfa98xx_chip *chip;
+ struct gpio_desc *reset_gpiod;
+ struct tfa98xx *tfa98xx;
+ unsigned int rev, i;
+ int ret;
+
+ chip = i2c_get_match_data(i2c);
+ if (!chip)
+ return -EINVAL;
+
+ tfa98xx = devm_kzalloc(dev, sizeof(*tfa98xx), GFP_KERNEL);
+ if (!tfa98xx)
+ return -ENOMEM;
+
+ tfa98xx->regmap = devm_regmap_init_i2c(i2c, &tfa98xx_regmap);
+ if (IS_ERR(tfa98xx->regmap))
+ return dev_err_probe(dev, PTR_ERR(tfa98xx->regmap),
+ "Failed to initialize regmap\n");
+
+ i2c_set_clientdata(i2c, tfa98xx);
+
+ reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset_gpiod))
+ return dev_err_probe(dev, PTR_ERR(reset_gpiod),
+ "Failed to get reset GPIO\n");
+
+ if (reset_gpiod) {
+ fsleep(1000);
+ gpiod_set_value_cansleep(reset_gpiod, 0);
+ fsleep(1000);
+ }
+
+ ret = regmap_read(tfa98xx->regmap, TFA98XX_REVISION, &rev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to read revision register\n");
+
+ if (FIELD_GET(TFA98XX_REVISION_ID_MSK, rev) != chip->id)
+ return dev_err_probe(dev, -ENODEV,
+ "Unexpected device revision 0x%04x\n", rev);
+
+ for (i = 0; i < chip->num_revs; i++)
+ if (chip->revs[i].rev == rev)
+ break;
+
+ if (i == chip->num_revs)
+ return dev_err_probe(dev, -ENODEV,
+ "Unsupported die revision 0x%04x\n", rev);
+
+ ret = tfa98xx_init(tfa98xx->regmap, &chip->revs[i]);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize device\n");
+
+ return devm_snd_soc_register_component(dev, &tfa98xx_component,
+ &tfa98xx_dai, 1);
+}
+
+static const struct i2c_device_id tfa98xx_i2c_id[] = {
+ { "tfa9894", (kernel_ulong_t)&tfa9894_chip },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tfa98xx_i2c_id);
+
+static const struct of_device_id tfa98xx_of_match[] = {
+ { .compatible = "nxp,tfa9894", .data = &tfa9894_chip },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tfa98xx_of_match);
+
+static struct i2c_driver tfa98xx_i2c_driver = {
+ .driver = {
+ .name = "tfa98xx",
+ .of_match_table = tfa98xx_of_match,
+ },
+ .probe = tfa98xx_i2c_probe,
+ .id_table = tfa98xx_i2c_id,
+};
+module_i2c_driver(tfa98xx_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NXP/Goodix TFA98xx (TFA2) amplifier driver");
+MODULE_AUTHOR("David Heidelberg <david@ixit.cz>");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RFC 3/3] arm64: dts: qcom: sdm845-oneplus-fajita: Enable speaker codec
2026-08-02 12:42 [PATCH RFC 0/3] Initial TFA2 family driver for tfa98xx David Heidelberg via B4 Relay
2026-08-02 12:42 ` [PATCH RFC 1/3] dt-bindings: sound: Add NXP TFA98XX TFA2 amplifier binding David Heidelberg via B4 Relay
2026-08-02 12:42 ` [PATCH RFC 2/3] ASoC: codecs: tfa98xx: add driver for TFA2 amplifiers David Heidelberg via B4 Relay
@ 2026-08-02 12:42 ` David Heidelberg via B4 Relay
2026-08-02 12:55 ` sashiko-bot
2 siblings, 1 reply; 7+ messages in thread
From: David Heidelberg via B4 Relay @ 2026-08-02 12:42 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jaroslav Kysela, Takashi Iwai, Bjorn Andersson,
Konrad Dybcio
Cc: linux-sound, devicetree, linux-kernel, linux-arm-msm, phone-devel,
David Heidelberg
From: David Heidelberg <david@ixit.cz>
OnePlus 6 uses NXP TFA9894 audio codec. Enable it.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
arch/arm64/boot/dts/qcom/sdm845-oneplus-fajita.dts | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/sdm845-oneplus-fajita.dts b/arch/arm64/boot/dts/qcom/sdm845-oneplus-fajita.dts
index 0542333a83579..cb08aab4740a4 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-oneplus-fajita.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-oneplus-fajita.dts
@@ -35,17 +35,26 @@ &display_panel {
compatible = "samsung,s6e3fc2x01-ams641rw", "samsung,s6e3fc2x01";
};
&framebuffer {
height = <2340>;
};
&i2c4 {
- /* nxp,tfa9894 @ 0x34 */
+ clock-frequency = <400000>;
+
+ status = "okay";
+
+ tfa9894_codec: audio-codec@34 {
+ compatible = "nxp,tfa9894";
+ reg = <0x34>;
+ reset-gpios = <&tlmm 69 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ };
};
&bq27441_fg {
monitored-battery = <&battery>;
};
&sound {
model = "OnePlus 6T";
@@ -56,22 +65,21 @@ &sound {
"AMIC4", "MIC BIAS1",
"AMIC5", "MIC BIAS3";
};
&pmi8998_charger {
monitored-battery = <&battery>;
};
-/*
- * The TFA9894 codec is currently unsupported.
- * We need to delete the node to allow the soundcard
- * to probe for headphones/earpiece.
- */
-/delete-node/ &speaker_playback_dai;
+&speaker_playback_dai {
+ codec {
+ sound-dai = <&tfa9894_codec>;
+ };
+};
&rmi4_f12 {
touchscreen-y-mm = <148>;
};
&wcd9340 {
qcom,micbias1-microvolt = <2700000>;
qcom,micbias2-microvolt = <2700000>;
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH RFC 1/3] dt-bindings: sound: Add NXP TFA98XX TFA2 amplifier binding
2026-08-02 12:42 ` [PATCH RFC 1/3] dt-bindings: sound: Add NXP TFA98XX TFA2 amplifier binding David Heidelberg via B4 Relay
@ 2026-08-02 12:45 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-08-02 12:45 UTC (permalink / raw)
To: David Heidelberg; +Cc: conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The YAML schema fails to implement the compatible strings and IRQ properties promised in the commit message.
--
--- Patch [1]: [PATCH RFC 1/3] dt-bindings: sound: Add NXP TFA98XX TFA2 amplifier binding ---
commit fcdff8de986493484ef077c116e00cc54ac329f0
Author: David Heidelberg <david@ixit.cz>
dt-bindings: sound: Add NXP TFA98XX TFA2 amplifier binding
Adds a YAML devicetree binding schema for the NXP/Goodix TFA98XX TFA2
family of I2C-controlled smart speaker amplifiers with on-chip DSP.
> diff --git a/Documentation/devicetree/bindings/sound/nxp,tfa98xx.yaml b/Documentation/devicetree/bindings/sound/nxp,tfa98xx.yaml
> new file mode 100644
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/nxp,tfa98xx.yaml
[ ... ]
> +properties:
> + compatible:
> + enum:
> + - nxp,tfa9894
> +
> + reg:
> + maxItems: 1
> +
> + reset-gpios:
> + maxItems: 1
[Severity: Medium]
The commit message states this binding covers chip variants TFA9872
through TFA9912 and documents optional IRQ GPIO properties, but only
nxp,tfa9894 and reset-gpios are defined in the schema here.
Since unevaluatedProperties is set to false, won't device trees attempting
to use the other variants or IRQ functionality fail validation? Should the
additional compatible strings and IRQ properties be added to this list to
match the capabilities described in the commit message?
> +
> + '#sound-dai-cells':
> + const: 0
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-tfa98xx-upstream-v1-0-4dd55cdd658a@ixit.cz?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH RFC 2/3] ASoC: codecs: tfa98xx: add driver for TFA2 amplifiers
2026-08-02 12:42 ` [PATCH RFC 2/3] ASoC: codecs: tfa98xx: add driver for TFA2 amplifiers David Heidelberg via B4 Relay
@ 2026-08-02 12:48 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-08-02 12:48 UTC (permalink / raw)
To: David Heidelberg; +Cc: devicetree, conor+dt, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The i2c_device_id array fails to use named initializers.
--
--- Patch [2]: [PATCH RFC 2/3] ASoC: codecs: tfa98xx: add driver for TFA2 amplifiers ---
commit 4120c0a8b537c48d7609971c712e99479ca0e3ec
Author: David Heidelberg <david@ixit.cz>
ASoC: codecs: tfa98xx: add driver for TFA2 amplifiers
This commit adds a driver for the NXP/Goodix TFA98xx (TFA2) smart speaker
amplifiers, initially supporting the TFA9894 found on the OnePlus 6T.
The driver bypasses the built-in CoolFlux DSP and takes the amplifier input
straight from the TDM interface.
> diff --git a/sound/soc/codecs/tfa98xx.c b/sound/soc/codecs/tfa98xx.c
> new file mode 100644
> index 0000000000000..c84abb8db207a
> --- /dev/null
> +++ b/sound/soc/codecs/tfa98xx.c
[ ... ]
> +static const struct i2c_device_id tfa98xx_i2c_id[] = {
> + { "tfa9894", (kernel_ulong_t)&tfa9894_chip },
> + { }
> +};
[Severity: Low]
This isn't a bug, but should the tfa98xx_i2c_id array use named initializers
(such as .name = "tfa9894", .driver_data = ...) to align with the I2C
subsystem guidelines for initialized arrays of type struct i2c_device_id?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-tfa98xx-upstream-v1-0-4dd55cdd658a@ixit.cz?part=2
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH RFC 3/3] arm64: dts: qcom: sdm845-oneplus-fajita: Enable speaker codec
2026-08-02 12:42 ` [PATCH RFC 3/3] arm64: dts: qcom: sdm845-oneplus-fajita: Enable speaker codec David Heidelberg via B4 Relay
@ 2026-08-02 12:55 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-08-02 12:55 UTC (permalink / raw)
To: David Heidelberg; +Cc: conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Incorrect active polarity for the codec reset GPIO in the device tree, holding the hardware in reset.
- [Medium] The commit message incorrectly identifies the target device as 'OnePlus 6', while the patch actually modifies the 'OnePlus 6T' (Fajita) device tree.
--
--- Patch [3]: [PATCH RFC 3/3] arm64: dts: qcom: sdm845-oneplus-fajita: Enable speaker codec ---
commit 05cefbbcdc0e5b43fbfd650af4c52fe9dfdebf01
Author: David Heidelberg <david@ixit.cz>
arm64: dts: qcom: sdm845-oneplus-fajita: Enable speaker codec
This commit enables the NXP TFA9894 audio codec on the OnePlus 6T (Fajita)
device tree by adding the required I2C node and linking it to the speaker
playback DAI.
> OnePlus 6 uses NXP TFA9894 audio codec. Enable it.
[Severity: Medium]
Is the device name correct here? The patch modifies sdm845-oneplus-fajita.dts,
which corresponds to the OnePlus 6T. The OnePlus 6 (Enchilada) appears to use a
completely different codec (MAX98927). Should this specify OnePlus 6T instead?
> diff --git a/arch/arm64/boot/dts/qcom/sdm845-oneplus-fajita.dts b/arch/arm64/boot/dts/qcom/sdm845-oneplus-fajita.dts
> index 0542333a83579..cb08aab4740a4 100644
> --- a/arch/arm64/boot/dts/qcom/sdm845-oneplus-fajita.dts
> +++ b/arch/arm64/boot/dts/qcom/sdm845-oneplus-fajita.dts
[ ... ]
> @@ -40,7 +40,16 @@ &framebuffer {
> };
>
> &i2c4 {
> - /* nxp,tfa9894 @ 0x34 */
> + clock-frequency = <400000>;
> +
> + status = "okay";
> +
> + tfa9894_codec: audio-codec@34 {
> + compatible = "nxp,tfa9894";
> + reg = <0x34>;
> + reset-gpios = <&tlmm 69 GPIO_ACTIVE_HIGH>;
[Severity: High]
Does this GPIO polarity hold the codec in reset permanently?
Looking at tfa98xx_i2c_probe() in sound/soc/codecs/tfa98xx.c, the driver
clears the logical reset during boot:
sound/soc/codecs/tfa98xx.c:tfa98xx_i2c_probe() {
...
gpiod_set_value_cansleep(reset_gpiod, 0);
...
}
Because the device tree specifies GPIO_ACTIVE_HIGH, deasserting the logical
reset (setting to 0) will drive the physical pin LOW. If the hardware actually
expects a physical HIGH signal to operate, this active-high configuration will
cause the subsequent I2C probe to fail.
> + #sound-dai-cells = <0>;
> + };
> };
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-tfa98xx-upstream-v1-0-4dd55cdd658a@ixit.cz?part=3
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-08-02 12:55 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-02 12:42 [PATCH RFC 0/3] Initial TFA2 family driver for tfa98xx David Heidelberg via B4 Relay
2026-08-02 12:42 ` [PATCH RFC 1/3] dt-bindings: sound: Add NXP TFA98XX TFA2 amplifier binding David Heidelberg via B4 Relay
2026-08-02 12:45 ` sashiko-bot
2026-08-02 12:42 ` [PATCH RFC 2/3] ASoC: codecs: tfa98xx: add driver for TFA2 amplifiers David Heidelberg via B4 Relay
2026-08-02 12:48 ` sashiko-bot
2026-08-02 12:42 ` [PATCH RFC 3/3] arm64: dts: qcom: sdm845-oneplus-fajita: Enable speaker codec David Heidelberg via B4 Relay
2026-08-02 12:55 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox