* [PATCH v1 0/8] ASoC: support TI's TAC5x1x audio codec family
@ 2026-03-12 18:48 Niranjan H Y
2026-03-12 18:48 ` [PATCH v1 1/8] dt-bindings: mfd: Add bindings for TI TAC5x1x MFD core Niranjan H Y
` (7 more replies)
0 siblings, 8 replies; 18+ messages in thread
From: Niranjan H Y @ 2026-03-12 18:48 UTC (permalink / raw)
To: linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, niranjan.hy, nb, navada, v-hampiholi, sandeepk,
baojun.xu, shenghao-ding
This patch series adds support for the Texas Instruments TAC5x1x family of
audio codecs. These devices are low-power, high-performance mono/stereo
audio codecs with integrated GPIO functionality.
The TAC5x1x family includes various configurations:
- ADC-only devices (TAA5212, TAA5412)
- DAC-only devices (TAD5112, TAD5212)
- Codec solutions (TAC5111, TAC5112, TAC5211,
TAC5212 TAC5301, TAC5311, TAC5312,
TAC5411, TAC5412)
The implementation follows the Multi-Function Device (MFD) approach with
three main components:
1. MFD core driver: Handles device initialization, power management, and
common resources
2. Pinctrl driver: Manages up to 5 configurable pins that can function as
GPIO, PDM clock/data, or interrupt outputs
3. ASoC codec driver: Provides audio capture and playback functionality
with support for various formats and sample rates
The series also removes duplicate support for these devices from the
pcm6240 driver, as they are now properly supported by dedicated drivers.
Link to previous standalone implementation attempt:
* https://lore.kernel.org/all/20250626181334.1200-2-niranjan.hy@ti.com/
Changes since previous submissions:
- Restructured the driver to follow the MFD (Multi-Function Device) approach,
separating core functionality from pinctrl and codec components
- Reorganized device tree bindings into separate files for MFD core,
pinctrl, and codec to align with the driver architecture
- Reordered patches to define DT bindings before driver implementation
- Improved documentation for device tree bindings.
Niranjan H Y (8):
dt-bindings: mfd: Add bindings for TI TAC5x1x MFD core
dt-bindings: pinctrl: Add bindings for TI TAC5x1x pinctrl
dt-bindings: sound: Add bindings for TI TAC5x1x codec
dt-bindings: sound: Update ti,pcm6240.yaml to remove TAC5x1x family
mfd: tac5x1x: Add TI TAC5x1x MFD core driver
pinctrl: pinctrl-tac5x1x: Add TI TAC5x1x pinctrl driver
ASoC: tac5x1x: Add TI TAC5x1x codec driver
ASoC: pcm6240: remove support for taac5x1x family
.../devicetree/bindings/mfd/ti,tac5x1x.yaml | 247 ++
.../bindings/pinctrl/ti,tac5x1x-pinctrl.yaml | 163 ++
.../devicetree/bindings/sound/ti,pcm6240.yaml | 15 +-
.../devicetree/bindings/sound/ti,tac5x1x.yaml | 49 +
drivers/mfd/Kconfig | 8 +
drivers/mfd/Makefile | 4 +
drivers/mfd/tac5x1x-core.c | 684 ++++++
drivers/pinctrl/Kconfig | 11 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-tac5x1x.c | 889 +++++++
include/dt-bindings/pinctrl/tac5x1x.h | 44 +
include/linux/mfd/tac5x1x/core.h | 69 +
include/linux/mfd/tac5x1x/registers.h | 291 +++
sound/soc/codecs/Kconfig | 11 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/pcm6240.c | 126 +-
sound/soc/codecs/pcm6240.h | 4 -
sound/soc/codecs/tac5x1x.c | 2082 +++++++++++++++++
sound/soc/codecs/tac5x1x.h | 35 +
19 files changed, 4598 insertions(+), 137 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
create mode 100644 Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml
create mode 100644 drivers/mfd/tac5x1x-core.c
create mode 100644 drivers/pinctrl/pinctrl-tac5x1x.c
create mode 100644 include/dt-bindings/pinctrl/tac5x1x.h
create mode 100644 include/linux/mfd/tac5x1x/core.h
create mode 100644 include/linux/mfd/tac5x1x/registers.h
create mode 100644 sound/soc/codecs/tac5x1x.c
create mode 100644 sound/soc/codecs/tac5x1x.h
--
2.34.1
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v1 1/8] dt-bindings: mfd: Add bindings for TI TAC5x1x MFD core
2026-03-12 18:48 [PATCH v1 0/8] ASoC: support TI's TAC5x1x audio codec family Niranjan H Y
@ 2026-03-12 18:48 ` Niranjan H Y
2026-03-13 20:43 ` Rob Herring
2026-03-14 10:13 ` Krzysztof Kozlowski
2026-03-12 18:48 ` [PATCH v1 2/8] dt-bindings: pinctrl: Add bindings for TI TAC5x1x pinctrl Niranjan H Y
` (6 subsequent siblings)
7 siblings, 2 replies; 18+ messages in thread
From: Niranjan H Y @ 2026-03-12 18:48 UTC (permalink / raw)
To: linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, niranjan.hy, nb, navada, v-hampiholi, sandeepk,
baojun.xu, shenghao-ding
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="yes", Size: 8508 bytes --]
Add device tree bindings for the Texas Instruments TAC5x1x family of
audio codecs with integrated GPIO controller, describing the MFD core
device interface, power supplies, and clock configuration.
Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
---
.../devicetree/bindings/mfd/ti,tac5x1x.yaml | 247 ++++++++++++++++++
1 file changed, 247 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
diff --git a/Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml b/Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
new file mode 100644
index 000000000000..3d7943c0411f
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
@@ -0,0 +1,247 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/ti,tac5x1x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TAC5x1x Multi-Function Audio Device
+
+maintainers:
+ - Niranjan H Y <niranjan.hy@ti.com>
+
+description: |
+ TAC5x1x family of low-power, high-performance audio codecs with integrated
+ GPIO controller and diagnostic capabilities.
+
+ This is the parent binding. Child nodes are bound by these bindings:
+ - Pin controller: Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
+ - Audio codec: Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml
+
+ Hardware features:
+ - Audio ADC/DAC with configurable impedance and voltage references
+ - 4-pin GPIO controller with alternate functions (PDM, IRQ)
+ - Analog voltage and current monitoring circuits
+ - Diagnostic fault detection with interrupt generation
+
+ Device datasheets can be found at:
+ TAA5212: https://www.ti.com/lit/ds/symlink/taa5212.pdf
+ TAA5412-Q1: https://www.ti.com/lit/ds/symlink/taa5412-q1.pdf
+ TAC5111: https://www.ti.com/lit/ds/symlink/tac5111.pdf
+ TAC5112: https://www.ti.com/lit/ds/symlink/tac5112.pdf
+ TAC5211: https://www.ti.com/lit/ds/symlink/tac5211.pdf
+ TAC5212: https://www.ti.com/lit/ds/symlink/tac5212.pdf
+ TAC5301: https://www.ti.com/lit/ds/symlink/tac5301-q1.pdf
+ TAC5311-Q1: https://www.ti.com/lit/ds/symlink/tac5311-q1.pdf
+ TAC5312-Q1: https://www.ti.com/lit/ds/symlink/tac5312-q1.pdf
+ TAC5411-Q1: https://www.ti.com/lit/ds/symlink/tac5411-q1.pdf
+ TAC5412-Q1: https://www.ti.com/lit/ds/symlink/tac5412-q1.pdf
+ TAD5112: https://www.ti.com/lit/ds/symlink/tad5112.pdf
+ TAD5212: https://www.ti.com/lit/ds/symlink/tad5212.pdf
+
+properties:
+ compatible:
+ enum:
+ - ti,taa5212
+ - ti,taa5412
+ - ti,tac5111
+ - ti,tac5112
+ - ti,tac5211
+ - ti,tac5212
+ - ti,tac5301
+ - ti,tac5311
+ - ti,tac5312
+ - ti,tac5411
+ - ti,tac5412
+ - ti,tad5112
+ - ti,tad5212
+
+ reg:
+ maxItems: 1
+ description: I2C slave address
+
+ reset-gpios:
+ maxItems: 1
+ description: Hardware reset control pin (active low)
+
+ interrupts:
+ maxItems: 1
+ description: |
+ Interrupt from device diagnostic circuits to host processor.
+ Generated on voltage/current fault conditions and other diagnostic events.
+
+ clocks:
+ maxItems: 1
+ description: Master clock input (MCLK)
+
+ clock-names:
+ items:
+ - const: mclk
+
+ avdd-supply:
+ description: |
+ Analog supply voltage input (AVDD pin).
+ Typical voltages: 1.8V, 3.0V, 3.3V, 5.0V
+
+ iovdd-supply:
+ description: |
+ Digital I/O supply voltage input (IOVDD pin).
+ Typical voltages: 1.8V, 3.3V
+
+ ti,vref-voltage:
+ description: |
+ Internal voltage reference setting in microvolts.
+
+ Supported values:
+ - 1375000: VREF = 1.375V
+ - 2500000: VREF = 2.5V
+ - 2750000: VREF = 2.75V
+
+ Constraint: Selected VREF must be lower than AVDD supply voltage.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [1375000, 2500000, 2750000]
+ default: 2500000
+
+ ti,micbias-voltage:
+ description: |
+ Microphone bias output voltage in microvolts.
+
+ Configuration options:
+ - VREF voltage: Set to same value as ti,vref-voltage
+ - 0.5 × VREF: Set to half of ti,vref-voltage value
+ - AVDD voltage: Set to AVDD supply voltage value
+
+ Note: When set to AVDD voltage, hardware automatically overrides
+ VREF setting to 2.75V regardless of ti,vref-voltage property.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 687500
+ maximum: 3300000
+
+ ti,adc1-impedance:
+ description: |
+ ADC Channel 1 input impedance in Ohms.
+ Supported impedance values:
+ - 5000: 5k input impedance
+ - 10000: 10k input impedance
+ - 40000: 40k input impedance
+ Available only for TAC5111, TAC5211, TAC5212, and TAA5212 variants.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [5000, 10000, 40000]
+ default: 10000
+
+ ti,adc2-impedance:
+ description: |
+ ADC Channel 2 input impedance in Ohms.
+ Supported impedance values:
+ - 5000: 5k input impedance
+ - 10000: 10k input impedance
+ - 40000: 40k input impedance
+ Available on stereo variants only (TAA5212, TAC5212).
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [5000, 10000, 40000]
+ default: 10000
+
+ ti,out2x-vcom-cfg:
+ description: |
+ Channel OUT2x VCOM reference voltage selection.
+
+ Configuration options:
+ - 0: VCOM = 0.6 × VREF
+ - 1: VCOM = AVDD / 2
+
+ Available on devices with stereo DAC output.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ default: 0
+
+ ti,gpa-threshold:
+ description: |
+ General Purpose Analog voltage monitoring thresholds in millivolts.
+ Format: [low_threshold_mv, high_threshold_mv]
+
+ Monitoring range: 0mV to 6000mV, Resolution: ~24mV per step
+ Default thresholds: 200mV (low), 2600mV (high)
+
+ Generates interrupt on voltage fault conditions.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 2
+ maxItems: 2
+ items:
+ minimum: 200
+ maximum: 6000
+ default: [200, 2600]
+
+ ti,in-ch-en:
+ description: |
+ Enable input channel diagnostic monitoring circuits.
+
+ When enabled (1), activates hardware monitoring for:
+ - Input channel fault detection
+ - Micbias current monitoring (if ti,micbias-threshold configured)
+ - Input overvoltage detection
+
+ Available on: TAC5301, TAC5311, TAC5312, TAC5411, TAC5412, TAA5412
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ default: 0
+
+ ti,out-ch-en:
+ description: |
+ Enable output channel diagnostic monitoring circuits.
+
+ When enabled (1), activates hardware monitoring for:
+ - Output channel fault detection
+ - Driver fault monitoring
+ - Short circuit detection
+
+ Available on: TAC5301, TAC5311, TAC5312, TAC5411, TAC5412, TAA5412
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ default: 0
+
+ ti,incl-se-inm:
+ description: |
+ Include INxM pins in single-ended input diagnostic scan.
+
+ When enabled (1), includes negative input pins (INxM) in diagnostic
+ monitoring for single-ended input configurations.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ default: 0
+
+ ti,incl-ac-coup:
+ description: |
+ Include AC-coupled input channels in diagnostic scan.
+
+ When enabled (1), includes AC-coupled input channels in the
+ diagnostic monitoring system.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ default: 0
+
+ ti,micbias-threshold:
+ description: |
+ Microphone bias current monitoring thresholds in microamps.
+ Format: [low_threshold_ua, high_threshold_ua]
+
+ Current monitoring range: 100uA to 33000uA, Resolution: ~132uA per step
+ Default thresholds: 2600uA (low), 18000uA (high)
+
+ Hardware monitors actual current flow through MICBIAS pin.
+ Generates interrupts on fault conditions when ti,in-ch-en = <1>.
+
+ Available on: TAC5301, TAC5311, TAC5312, TAC5411, TAC5412, TAA5412
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 2
+ maxItems: 2
+ items:
+ minimum: 100
+ maximum: 33000
+ default: [2600, 18000]
+
+required:
+ - compatible
+ - reg
+ - avdd-supply
+ - iovdd-supply
+
+unevaluatedProperties: false
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v1 2/8] dt-bindings: pinctrl: Add bindings for TI TAC5x1x pinctrl
2026-03-12 18:48 [PATCH v1 0/8] ASoC: support TI's TAC5x1x audio codec family Niranjan H Y
2026-03-12 18:48 ` [PATCH v1 1/8] dt-bindings: mfd: Add bindings for TI TAC5x1x MFD core Niranjan H Y
@ 2026-03-12 18:48 ` Niranjan H Y
2026-03-14 10:10 ` Krzysztof Kozlowski
2026-03-14 10:12 ` Krzysztof Kozlowski
2026-03-12 18:48 ` [PATCH v1 3/8] dt-bindings: sound: Add bindings for TI TAC5x1x codec Niranjan H Y
` (5 subsequent siblings)
7 siblings, 2 replies; 18+ messages in thread
From: Niranjan H Y @ 2026-03-12 18:48 UTC (permalink / raw)
To: linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, niranjan.hy, nb, navada, v-hampiholi, sandeepk,
baojun.xu, shenghao-ding
Add device tree bindings for the Texas Instruments TAC5x1x family
pin controller. These bindings define the GPIO and pin control
configuration interface for the device.
Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
---
.../bindings/pinctrl/ti,tac5x1x-pinctrl.yaml | 163 ++++++++++++++++++
include/dt-bindings/pinctrl/tac5x1x.h | 44 +++++
2 files changed, 207 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
create mode 100644 include/dt-bindings/pinctrl/tac5x1x.h
diff --git a/Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
new file mode 100644
index 000000000000..3ccb262d6247
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
@@ -0,0 +1,163 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/ti,tac5x1x-pinctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI TAC5x1x Pin Controller
+
+maintainers:
+ - Niranjan H Y <niranjan.hy@ti.com>
+
+description: |
+ The TAC5x1x devices have 5 configurable pins that can be used for GPIO
+ or alternate functions like PDM (Pulse Density Modulation) and interrupt
+ generation. A subset of pins can be present in any variant of the HW.
+
+ This binding is used as a child node of the main TAC5x1x MFD device
+ described in Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
+
+ Pin capabilities:
+ - GPIO1, GPIO2: Bidirectional, configurable as GPIO, PDM clock, or IRQ output
+ - GPO1: Output only, configurable as GPIO, PDM clock, or IRQ output.
+ Some variants use different name GPO1A.
+ - GPI1: Input only, configurable as GPIO or PDM data input
+ Some variants use different name GPI1A.
+ - GPI2A: Input only, configurable as GPIO or PDM data input
+
+properties:
+ compatible:
+ const: ti,tac5x1x-pinctrl
+
+ gpio-controller: true
+
+ '#gpio-cells':
+ const: 2
+ description: |
+ First cell is the pin number (0-4 corresponding to GPIO1, GPIO2, GPO1, GPI1, GPI2A)
+ Second cell is flags (GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW)
+
+ gpio-ranges:
+ maxItems: 1
+ description: GPIO range mapping to pin controller
+
+ gpio-line-names:
+ minItems: 1
+ maxItems: 5
+ description: Names for the GPIO lines
+
+patternProperties:
+ '-state$':
+ type: object
+ description: Pin configuration state
+ $ref: /schemas/pinctrl/pinmux-node.yaml#
+
+ patternProperties:
+ '^.*$':
+ type: object
+ $ref: /schemas/pinctrl/pincfg-node.yaml#
+
+ properties:
+ groups:
+ description: Pin groups to configure
+ items:
+ enum:
+ - gpio1
+ - gpio2
+ - gpo1
+ - gpi1
+ - gpi2a
+ - pdm_gpio1_gpio2
+ - pdm_gpio1_gpi1
+ - pdm_gpio1_gpi2a
+ - pdm_gpio2_gpio1
+ - pdm_gpio2_gpi1
+ - pdm_gpio2_gpi2a
+ - pdm_gpo1_gpio1
+ - pdm_gpo1_gpio2
+ - pdm_gpo1_gpi1
+ - pdm_gpo1_gpi2a
+
+ function:
+ description: Function to assign
+ enum:
+ - gpio
+ - pdm
+ - irq
+
+ drive-push-pull:
+ type: boolean
+ description: Enable push-pull drive mode
+
+ drive-open-drain:
+ type: boolean
+ description: Enable open-drain drive mode
+
+ drive-open-source:
+ type: boolean
+ description: Enable open-source drive mode
+
+ bias-pull-up:
+ type: boolean
+ description: Enable pull-up bias
+
+ bias-pull-down:
+ type: boolean
+ description: Enable pull-down bias
+
+ bias-high-impedance:
+ type: boolean
+ description: Set pin to high impedance
+
+ input-enable:
+ type: boolean
+ description: Enable input buffer
+
+ output-enable:
+ type: boolean
+ description: Enable output buffer
+
+ required:
+ - groups
+ - function
+
+ additionalProperties: false
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - gpio-controller
+ - '#gpio-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ pinctrl {
+ compatible = "ti,tac5x1x-pinctrl";
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&pinctrl 0 0 5>;
+ gpio-line-names = "GPIO1", "GPIO2", "GPO1", "GPI1", "GPI2A";
+
+ default_state: default-state {
+ pdm_config {
+ groups = "pdm_gpo1_gpi1";
+ function = "pdm";
+ drive-push-pull;
+ };
+
+ gpio_config {
+ groups = "gpio1", "gpio2";
+ function = "gpio";
+ bias-pull-up;
+ };
+
+ irq_config {
+ groups = "gpo1";
+ function = "irq";
+ drive-open-drain;
+ };
+ };
+ };
diff --git a/include/dt-bindings/pinctrl/tac5x1x.h b/include/dt-bindings/pinctrl/tac5x1x.h
new file mode 100644
index 000000000000..8cc3fa0b7946
--- /dev/null
+++ b/include/dt-bindings/pinctrl/tac5x1x.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+/*
+ * Device Tree binding constants for TAC5x1x pinctrl
+ *
+ * Copyright (C) 2025 Texas Instruments Incorporated
+ * Author: Niranjan H Y <niranjan.hy@ti.com>
+ */
+
+#ifndef _DT_BINDINGS_PINCTRL_TAC5X1X_H
+#define _DT_BINDINGS_PINCTRL_TAC5X1X_H
+
+/* Pin IDs */
+#define TAC5X1X_PIN_GPIO1 0
+#define TAC5X1X_PIN_GPIO2 1
+#define TAC5X1X_PIN_GPO1 2
+#define TAC5X1X_PIN_GPI1 3
+#define TAC5X1X_PIN_GPI2A 4
+
+/* Pin functions */
+#define TAC5X1X_FUNC_GPIO 0
+#define TAC5X1X_FUNC_PDM 1
+#define TAC5X1X_FUNC_IRQ 2
+
+/* Pin drive modes */
+#define TAC5X1X_DRIVE_HIZ 0
+#define TAC5X1X_DRIVE_PUSH_PULL 1
+#define TAC5X1X_DRIVE_PULL_DOWN 2
+#define TAC5X1X_DRIVE_OPEN_DRAIN 3
+#define TAC5X1X_DRIVE_PULL_UP 4
+#define TAC5X1X_DRIVE_OPEN_SOURCE 5
+
+/* PDM configurations */
+#define TAC5X1X_PDM_GPIO1_GPIO2 0
+#define TAC5X1X_PDM_GPIO1_GPI1 1
+#define TAC5X1X_PDM_GPIO1_GPI2A 2
+#define TAC5X1X_PDM_GPIO2_GPIO1 3
+#define TAC5X1X_PDM_GPIO2_GPI1 4
+#define TAC5X1X_PDM_GPIO2_GPI2A 5
+#define TAC5X1X_PDM_GPO1_GPIO1 6
+#define TAC5X1X_PDM_GPO1_GPIO2 7
+#define TAC5X1X_PDM_GPO1_GPI1 8
+#define TAC5X1X_PDM_GPO1_GPI2A 9
+
+#endif /* _DT_BINDINGS_PINCTRL_TAC5X1X_H */
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v1 3/8] dt-bindings: sound: Add bindings for TI TAC5x1x codec
2026-03-12 18:48 [PATCH v1 0/8] ASoC: support TI's TAC5x1x audio codec family Niranjan H Y
2026-03-12 18:48 ` [PATCH v1 1/8] dt-bindings: mfd: Add bindings for TI TAC5x1x MFD core Niranjan H Y
2026-03-12 18:48 ` [PATCH v1 2/8] dt-bindings: pinctrl: Add bindings for TI TAC5x1x pinctrl Niranjan H Y
@ 2026-03-12 18:48 ` Niranjan H Y
2026-03-14 10:14 ` Krzysztof Kozlowski
2026-03-12 18:48 ` [PATCH v1 4/8] dt-bindings: sound: Update ti,pcm6240.yaml to remove TAC5x1x family Niranjan H Y
` (4 subsequent siblings)
7 siblings, 1 reply; 18+ messages in thread
From: Niranjan H Y @ 2026-03-12 18:48 UTC (permalink / raw)
To: linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, niranjan.hy, nb, navada, v-hampiholi, sandeepk,
baojun.xu, shenghao-ding
Add device tree bindings for the Texas Instruments TAC5x1x family
audio codec. These bindings define the ALSA audio interface and
regulator configuration.
Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
---
.../devicetree/bindings/sound/ti,tac5x1x.yaml | 49 +++++++++++++++++++
1 file changed, 49 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml
diff --git a/Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml b/Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml
new file mode 100644
index 000000000000..05fc93027fea
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,tac5x1x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI TAC5x1x Audio Codec
+
+maintainers:
+ - Niranjan H Y <niranjan.hy@ti.com>
+
+description: |
+ The TAC5x1x codec provides audio playback and capture functionality
+ with support for various audio formats and sample rates.
+
+ This binding describes the codec functionality of the TAC5x1x device,
+ which is instantiated as a child device of the main TAC5x1x MFD described
+ in Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
+
+properties:
+ compatible:
+ const: ti,tac5x1x-codec
+
+ '#sound-dai-cells':
+ const: 0
+
+ clocks:
+ maxItems: 1
+ description: Master clock for audio processing
+
+ clock-names:
+ items:
+ - const: mclk
+
+required:
+ - compatible
+ - '#sound-dai-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ codec {
+ compatible = "ti,tac5x1x-codec";
+ #sound-dai-cells = <0>;
+ clocks = <&audio_mclk>;
+ clock-names = "mclk";
+ };
+
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v1 4/8] dt-bindings: sound: Update ti,pcm6240.yaml to remove TAC5x1x family
2026-03-12 18:48 [PATCH v1 0/8] ASoC: support TI's TAC5x1x audio codec family Niranjan H Y
` (2 preceding siblings ...)
2026-03-12 18:48 ` [PATCH v1 3/8] dt-bindings: sound: Add bindings for TI TAC5x1x codec Niranjan H Y
@ 2026-03-12 18:48 ` Niranjan H Y
2026-03-14 10:15 ` Krzysztof Kozlowski
2026-03-12 18:48 ` [PATCH v1 5/8] mfd: tac5x1x: Add TI TAC5x1x MFD core driver Niranjan H Y
` (3 subsequent siblings)
7 siblings, 1 reply; 18+ messages in thread
From: Niranjan H Y @ 2026-03-12 18:48 UTC (permalink / raw)
To: linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, niranjan.hy, nb, navada, v-hampiholi, sandeepk,
baojun.xu, shenghao-ding
Remove TAC5x1x family references from the pcm6240 device tree bindings.
The TAC5x1x family (taa5212, taa5412, tad5212, tad5412) now uses a
dedicated MFD-based driver with its own device tree bindings defined in:
- Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
- Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
- Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml
Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
---
.../devicetree/bindings/sound/ti,pcm6240.yaml | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/ti,pcm6240.yaml b/Documentation/devicetree/bindings/sound/ti,pcm6240.yaml
index d89b4255b51c..0ba2032e45b0 100644
--- a/Documentation/devicetree/bindings/sound/ti,pcm6240.yaml
+++ b/Documentation/devicetree/bindings/sound/ti,pcm6240.yaml
@@ -11,6 +11,11 @@ maintainers:
- Shenghao Ding <shenghao-ding@ti.com>
description: |
+ Note: Support for TAC5x1x family (taa5212, taa5412, tad5212, tad5412)
+ has been moved to a dedicated MFD driver.
+ Please update your device tree to use the new driver as described in
+ Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
+
The PCM6240 Family is a big family of Audio ADC/DAC for
different Specifications, range from Personal Electric
to Automotive Electric, even some professional fields.
@@ -29,8 +34,6 @@ description: |
https://www.ti.com/lit/gpn/pcm9211
https://www.ti.com/lit/gpn/pcmd3140
https://www.ti.com/lit/gpn/pcmd3180
- https://www.ti.com/lit/gpn/taa5212
- https://www.ti.com/lit/gpn/tad5212
properties:
compatible:
@@ -81,10 +84,6 @@ properties:
ti,pcmd3180: Eight-channel pulse-density-modulation input to TDM or
I2S output converter.
- ti,taa5212: Low-power high-performance stereo audio ADC with 118-dB
- dynamic range.
-
- ti,tad5212: Low-power stereo audio DAC with 120-dB dynamic range.
oneOf:
- items:
- enum:
@@ -98,8 +97,6 @@ properties:
- enum:
- ti,pcmd512x
- ti,pcm9211
- - ti,taa5212
- - ti,tad5212
- const: ti,adc6120
- items:
- enum:
@@ -114,8 +111,6 @@ properties:
- ti,pcmd3140
- ti,pcmd3180
- ti,pcm1690
- - ti,taa5412
- - ti,tad5412
- const: ti,pcm6240
- enum:
- ti,adc6120
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v1 5/8] mfd: tac5x1x: Add TI TAC5x1x MFD core driver
2026-03-12 18:48 [PATCH v1 0/8] ASoC: support TI's TAC5x1x audio codec family Niranjan H Y
` (3 preceding siblings ...)
2026-03-12 18:48 ` [PATCH v1 4/8] dt-bindings: sound: Update ti,pcm6240.yaml to remove TAC5x1x family Niranjan H Y
@ 2026-03-12 18:48 ` Niranjan H Y
2026-03-14 10:20 ` Krzysztof Kozlowski
2026-03-12 18:48 ` [PATCH v1 6/8] pinctrl: pinctrl-tac5x1x: Add TI TAC5x1x pinctrl driver Niranjan H Y
` (2 subsequent siblings)
7 siblings, 1 reply; 18+ messages in thread
From: Niranjan H Y @ 2026-03-12 18:48 UTC (permalink / raw)
To: linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, niranjan.hy, nb, navada, v-hampiholi, sandeepk,
baojun.xu, shenghao-ding
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="yes", Size: 34156 bytes --]
This patch adds the core Multi-Function Device (MFD) driver for the Texas
Instruments TAC5x1x family of audio codecs. The MFD core handles device
initialization, power management, and common resources for child devices.
The TAC5x1x family includes various low-power, high-performance audio
codecs with integrated GPIO functionality:
- ADC-only devices (TAA5212, TAA5412)
- DAC-only devices (TAD5112, TAD5212)
- Codec solutions (TAC5111, TAC5112, TAC5211, TAC5212,
TAC5301, TAC5311, TAC5312, TAC5411, TAC5412)
The core driver provides:
- I2C communication and register map definitions
- Power management and regulator control
- Child device registration for pinctrl and codec components
- Interrupt handling for diagnostic functions
Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
---
drivers/mfd/Kconfig | 8 +
drivers/mfd/Makefile | 4 +
drivers/mfd/tac5x1x-core.c | 684 ++++++++++++++++++++++++++
include/linux/mfd/tac5x1x/core.h | 69 +++
include/linux/mfd/tac5x1x/registers.h | 291 +++++++++++
5 files changed, 1056 insertions(+)
create mode 100644 drivers/mfd/tac5x1x-core.c
create mode 100644 include/linux/mfd/tac5x1x/core.h
create mode 100644 include/linux/mfd/tac5x1x/registers.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 7192c9d1d268..9ed909595edb 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1551,6 +1551,14 @@ config MFD_SC27XX_PMIC
This driver provides common support for accessing the SC27xx PMICs,
and it also adds the irq_chip parts for handling the PMIC chip events.
+config MFD_TAC5X1X
+ tristate "Texas Instruments tac5x1x core driver"
+ select MFD_CORE
+ help
+ Select this option to enable tac5x1x multi function
+ diver which is required for audio codec functionality and gpio
+ pincontrol configuration for the device.
+
config RZ_MTU3
tristate "Renesas RZ/G2L MTU3a core driver"
depends on (ARCH_RZG2L && OF) || COMPILE_TEST
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e75e8045c28a..9f57a5ce4e8c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -69,6 +69,10 @@ obj-$(CONFIG_MFD_WM8350) += wm8350.o
obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
wm8994-objs := wm8994-core.o wm8994-irq.o wm8994-regmap.o
obj-$(CONFIG_MFD_WM8994) += wm8994.o
+
+tac5x1x-objs := tac5x1x-core.o
+obj-$(CONFIG_MFD_TAC5X1X) += tac5x1x.o
+
obj-$(CONFIG_MFD_WM97xx) += wm97xx-core.o
madera-objs := madera-core.o
diff --git a/drivers/mfd/tac5x1x-core.c b/drivers/mfd/tac5x1x-core.c
new file mode 100644
index 000000000000..b8dee84f7c31
--- /dev/null
+++ b/drivers/mfd/tac5x1x-core.c
@@ -0,0 +1,684 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * tac5x1x-core.c -- Device access for TAC5x1x
+ *
+ * Copyright (C) 2025 Texas Instruments Incorporated
+ *
+ * Author: Niranjan H Y <niranjan.hy@ti.com>
+ */
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tac5x1x/core.h>
+#include <linux/mfd/tac5x1x/registers.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+static const char * const tac5x1x_supply_names[TAC5X1X_NUM_SUPPLIES] = {
+ "avdd",
+ "iovdd",
+};
+
+static const struct regmap_range_cfg tac5x1x_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 12 * 128,
+ .selector_reg = TAC_PAGE_SELECT,
+ .selector_mask = GENMASK(7, 0),
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_range tac5x1x_volatile_ranges[] = {
+ regmap_reg_range(TAC5X1X_RESET, TAC5X1X_RESET),
+ regmap_reg_range(TAC5X1X_REG_INT_LTCH0, TAC5X1X_REG_INT_LTCH2),
+};
+
+static const struct regmap_access_table tac5x1x_volatile_table = {
+ .yes_ranges = tac5x1x_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(tac5x1x_volatile_ranges),
+};
+
+/* Read-only (latch) registers */
+static const struct regmap_range tac5x1x_read_only_ranges[] = {
+ regmap_reg_range(TAC5X1X_REG_INT_LTCH0, TAC5X1X_REG_INT_LTCH2),
+};
+
+static const struct regmap_access_table tac5x1x_wr_table = {
+ .no_ranges = tac5x1x_read_only_ranges,
+ .n_no_ranges = ARRAY_SIZE(tac5x1x_read_only_ranges),
+};
+
+static const struct regmap_config tac5x1x_regmap = {
+ .max_register = 12 * 128,
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .ranges = tac5x1x_ranges,
+ .num_ranges = ARRAY_SIZE(tac5x1x_ranges),
+ .volatile_table = &tac5x1x_volatile_table,
+ .wr_table = &tac5x1x_wr_table,
+};
+
+static int tac5x1x_reset(struct tac5x1x *tac5x1x)
+{
+ int ret;
+
+ ret = regmap_write(tac5x1x->regmap, TAC5X1X_RESET, 1);
+ if (ret < 0)
+ return ret;
+ /* Wait >= 10 ms after entering sleep mode. */
+ usleep_range(10000, 100000);
+ regcache_mark_dirty(tac5x1x->regmap);
+
+ return ret;
+}
+
+static int tac5x1x_suspend(struct device *dev)
+{
+ struct tac5x1x *tac5x1x = dev_get_drvdata(dev);
+
+ regcache_cache_only(tac5x1x->regmap, true);
+ regcache_mark_dirty(tac5x1x->regmap);
+
+ /* Only disable regulators if they are currently enabled */
+ if (tac5x1x->regulators_enabled) {
+ regulator_bulk_disable(TAC5X1X_NUM_SUPPLIES, tac5x1x->supplies);
+ tac5x1x->regulators_enabled = false;
+ }
+
+ return 0;
+}
+
+static int tac5x1x_resume(struct device *dev)
+{
+ struct tac5x1x *tac5x1x = dev_get_drvdata(dev);
+ int ret;
+
+ /* Only enable regulators if they are not already enabled */
+ if (!tac5x1x->regulators_enabled) {
+ ret = regulator_bulk_enable(TAC5X1X_NUM_SUPPLIES, tac5x1x->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+ tac5x1x->regulators_enabled = true;
+ }
+
+ regcache_cache_only(tac5x1x->regmap, false);
+ ret = regcache_sync(tac5x1x->regmap);
+ if (ret) {
+ dev_err(dev, "Failed to restore register map: %d\n", ret);
+ regulator_bulk_disable(TAC5X1X_NUM_SUPPLIES, tac5x1x->supplies);
+ tac5x1x->regulators_enabled = false;
+ return ret;
+ }
+
+ return 0;
+}
+
+static const int vref_uv[] = {
+ 2750000,
+ 2500000,
+ 1375000,
+};
+
+static int voltage_to_vref_cfg(int voltage_uv)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vref_uv); i++) {
+ if (vref_uv[i] == voltage_uv)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static const int adc_impedance_ohms[] = {
+ 5000, /* Index 0: 5 kΩ */
+ 10000, /* Index 1: 10 kΩ */
+ 40000, /* Index 2: 40 kΩ */
+};
+
+static int impedance_to_cfg(int impedance_ohms)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(adc_impedance_ohms); i++)
+ if (adc_impedance_ohms[i] == impedance_ohms)
+ return i;
+ return -EINVAL;
+}
+
+static int micbias_thr_current_ua_to_reg(int current_ua)
+{
+ s64 temp;
+ int n;
+
+ /*
+ * N = ((current_uA × 10 / 48461.54) + 2) × 4095 / 144
+ * x 10 to remove the decimal point on both sides
+ * Rearrange: N = (current_uA × 10 × 4095) / (48461.54 × 144) + (2 × 4095 / 144)
+ * N = (current_uA × 40950) / 6978461.76 + 56.875
+ */
+
+ /* First term: (current_uA × 40950) / 6978461.76 */
+ temp = (s64)current_ua * 40950; /* × 40950 */
+ temp = div_s64(temp, 6978462); /* / 6978461.76 ≈ 6978462 */
+ /* Second term: 56.875 ≈ 57 */
+ temp += 57;
+
+ n = (int)temp;
+ n = clamp(n, 0, 255);
+
+ return n;
+}
+
+static int tac5x1x_validate_gpa_thresholds(struct tac5x1x *tac5x1x,
+ int *thresholds_mv)
+{
+ int orig_low = thresholds_mv[0];
+ int orig_high = thresholds_mv[1];
+
+ /* Clamp thresholds to valid ranges */
+ thresholds_mv[0] = clamp(thresholds_mv[0], 0, 6000);
+ thresholds_mv[1] = clamp(thresholds_mv[1], 0, 6000);
+
+ if (thresholds_mv[0] >= thresholds_mv[1]) {
+ dev_err(tac5x1x->dev,
+ "Invalid GPA thresholds: low(%dmV) must be < high(%dmV)\n",
+ thresholds_mv[0], thresholds_mv[1]);
+ return -EINVAL;
+ }
+
+ /* Warn if values were clamped */
+ if (orig_low != thresholds_mv[0]) {
+ dev_warn(tac5x1x->dev,
+ "GPA low threshold clamped from %dmV to %dmV\n",
+ orig_low, thresholds_mv[0]);
+ }
+ if (orig_high != thresholds_mv[1]) {
+ dev_warn(tac5x1x->dev,
+ "GPA high threshold clamped from %dmV to %dmV\n",
+ orig_high, thresholds_mv[1]);
+ }
+
+ return 0;
+}
+
+/* Voltage conversion functions (from previous discussion) */
+static int voltage_mv_to_register_value(int voltage_mv)
+{
+ s64 temp;
+ int n;
+
+ /* Clamp input to reasonable range */
+ voltage_mv = clamp(voltage_mv, 0, 6000);
+
+ /*
+ * Formula: nd = ((0.9*(N*16)/4095) - 0.225) × 6 (V)
+ * Solving for N: N = ((voltage_V/6 + 0.225) × 4095) / 14.4
+ */
+
+ temp = (s64)voltage_mv * 1000000; /* Scale for precision */
+ temp = div_s64(temp, 6000); /* voltage_mV / 6000 */
+ temp += 225000; /* + 0.225 * 1000000 */
+ temp = temp * 4095; /* × 4095 */
+ temp = div_s64(temp, 14400000); /* / (14.4 * 1000000) */
+
+ n = (int)temp;
+
+ return clamp(n, 0, 255);
+}
+
+static int tac5x1x_parse_gpa_thresholds(struct tac5x1x *tac5x1x)
+{
+ int thresholds_mv[2] = {200, 2600}; /* TI datasheet defaults: 0.2V, 2.6V */
+ int ret;
+
+ /* Read thresholds from device tree */
+ ret = fwnode_property_read_u32_array(tac5x1x->dev->fwnode,
+ "ti,gpa-threshold",
+ thresholds_mv, 2);
+ if (ret)
+ dev_dbg(tac5x1x->dev,
+ "Using default GPA thresholds: [%d, %d] mV\n",
+ thresholds_mv[0], thresholds_mv[1]);
+
+ /* Validate threshold values */
+ ret = tac5x1x_validate_gpa_thresholds(tac5x1x, thresholds_mv);
+ if (ret)
+ return ret;
+
+ /* Convert mV values to register values */
+ tac5x1x->gpa_threshold[0] = voltage_mv_to_register_value(thresholds_mv[0]);
+ tac5x1x->gpa_threshold[1] = voltage_mv_to_register_value(thresholds_mv[1]);
+
+ dev_dbg(tac5x1x->dev, "GPA thresholds: %dmV->0x%02x, %dmV->0x%02x\n",
+ thresholds_mv[0], tac5x1x->gpa_threshold[0],
+ thresholds_mv[1], tac5x1x->gpa_threshold[1]);
+
+ return 0;
+}
+
+static int tac5x1x_parse_dt(struct tac5x1x *tac5x1x,
+ struct device_node *np)
+{
+ struct regulator *avdd_reg;
+ struct tac5x1x_input_diag_config input_config = {};
+ int vref_voltage_uv = 2500000; /* Default 2.5V */
+ int micbias_voltage_uv = 0;
+ int vref_cfg, micbias_cfg;
+ int ret;
+ int avdd_uv;
+ int adc_impedance_ohms;
+ int micbias_thr_uv[2];
+
+ avdd_reg = devm_regulator_get(tac5x1x->dev, "avdd");
+ if (IS_ERR(avdd_reg)) {
+ dev_err(tac5x1x->dev, "Failed to get avdd regulator: %ld\n", PTR_ERR(avdd_reg));
+ return PTR_ERR(avdd_reg);
+ }
+
+ avdd_uv = regulator_get_voltage(avdd_reg);
+ if (avdd_uv < 0) {
+ dev_err(tac5x1x->dev, "Failed to get avdd voltage: %d\n", avdd_uv);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Read VREF voltage directly */
+ ret = fwnode_property_read_u32(tac5x1x->dev->fwnode, "ti,vref-voltage",
+ &vref_voltage_uv);
+ if (ret)
+ dev_dbg(tac5x1x->dev, "Using default vref-voltage: %duV\n", vref_voltage_uv);
+
+ /* Validate VREF voltage is one of the supported values */
+ vref_cfg = voltage_to_vref_cfg(vref_voltage_uv);
+ if (vref_cfg < 0) {
+ dev_err(tac5x1x->dev, "Invalid vref-voltage %duV. Supported: 1375000, 2500000, 2750000\n",
+ vref_voltage_uv);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Validate VREF is lower than AVDD */
+ if (vref_voltage_uv >= avdd_uv) {
+ dev_err(tac5x1x->dev, "vref-voltage (%duV) must be lower than avdd-voltage (%duV)\n",
+ vref_voltage_uv, avdd_uv);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Read micbias voltage directly */
+ ret = fwnode_property_read_u32(tac5x1x->dev->fwnode,
+ "ti,micbias-voltage",
+ &micbias_voltage_uv);
+ if (ret) {
+ micbias_voltage_uv = vref_voltage_uv;
+ dev_dbg(tac5x1x->dev,
+ "Using default micbias-voltage: %duV\n",
+ micbias_voltage_uv);
+ ret = 0;
+ }
+
+ if (micbias_voltage_uv == avdd_uv) {
+ micbias_cfg = TAC5X1X_MICBIAS_AVDD;
+ vref_cfg = TAC5X1X_VERF_2_75V; /* VREF to 2.75V for AVDD */
+ dev_dbg(tac5x1x->dev,
+ "micbias set to AVDD (%duV),forcing VREF to 2.75V\n",
+ avdd_uv);
+ } else if (micbias_voltage_uv == vref_voltage_uv) {
+ micbias_cfg = TAC5X1X_MICBIAS_VREF;
+ } else if (micbias_voltage_uv == (vref_voltage_uv / 2)) {
+ micbias_cfg = TAC5X1X_MICBIAS_0_5VREF;
+ } else {
+ dev_err(tac5x1x->dev,
+ "Invalid micbias %duV. Must be %duV, %duV, or %duV\n",
+ micbias_voltage_uv,
+ vref_voltage_uv / 2,
+ vref_voltage_uv, avdd_uv);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Store the validated configurations */
+ tac5x1x->vref_vg = vref_cfg;
+ tac5x1x->micbias_vg = micbias_cfg;
+
+ dev_dbg(tac5x1x->dev,
+ "VREF: %duV, Micbias: %duV, AVDD: %duV\n",
+ vref_voltage_uv, micbias_voltage_uv, avdd_uv);
+
+ tac5x1x->adc_impedance[0] = -1;
+ tac5x1x->adc_impedance[1] = -1;
+ tac5x1x->out2x_vcom_cfg = -1;
+
+ tac5x1x->gpa_threshold[0] = TAC5X1X_GPA_LOW_THRESHOLD;
+ tac5x1x->gpa_threshold[1] = TAC5X1X_GPA_HIGH_THRESHOLD;
+
+ ret = tac5x1x_parse_gpa_thresholds(tac5x1x);
+ if (ret)
+ return ret;
+
+ fwnode_property_read_u32(tac5x1x->dev->fwnode, "ti,out2x-vcom-cfg",
+ &tac5x1x->out2x_vcom_cfg);
+
+ switch (tac5x1x->codec_type) {
+ case TAA5212:
+ case TAC5212:
+ if (!fwnode_property_read_u32(tac5x1x->dev->fwnode,
+ "ti,adc2-impedance",
+ &adc_impedance_ohms)) {
+ int adc2_cfg = impedance_to_cfg(adc_impedance_ohms);
+
+ if (adc2_cfg < 0) {
+ dev_err(tac5x1x->dev,
+ "Invalid adc2-impedance %dOhm. Supported: 5000, 10000, 40000\n",
+ adc_impedance_ohms);
+ ret = -EINVAL;
+ goto out;
+ }
+ tac5x1x->adc_impedance[1] = adc2_cfg;
+ dev_dbg(tac5x1x->dev, "ADC2 impedance: %dOhm (cfg=%d)\n",
+ adc_impedance_ohms, adc2_cfg);
+ }
+ fallthrough;
+ case TAC5211:
+ case TAC5111:
+ if (!fwnode_property_read_u32(tac5x1x->dev->fwnode,
+ "ti,adc1-impedance",
+ &adc_impedance_ohms)) {
+ int adc1_cfg = impedance_to_cfg(adc_impedance_ohms);
+
+ if (adc1_cfg < 0) {
+ dev_err(tac5x1x->dev,
+ "Invalid adc1-impedance %dOhm. Supported: 5000, 10000, 40000\n",
+ adc_impedance_ohms);
+ ret = -EINVAL;
+ goto out;
+ }
+ tac5x1x->adc_impedance[0] = adc1_cfg;
+ dev_dbg(tac5x1x->dev, "ADC1 impedance: %dOhm (cfg=%d)\n",
+ adc_impedance_ohms, adc1_cfg);
+ }
+ fallthrough;
+ case TAC5112:
+ case TAD5112:
+ case TAD5212:
+ break;
+ case TAA5412:
+ case TAC5411:
+ case TAC5412:
+ case TAC5301:
+ case TAC5311:
+ case TAC5312:
+ tac5x1x->input_diag_config.in_ch_en = 0;
+ if (fwnode_property_read_u32(tac5x1x->dev->fwnode,
+ "ti,in-ch-en",
+ &input_config.in_ch_en))
+ dev_dbg(tac5x1x->dev,
+ "Fail to get ti,in-ch-en value\n");
+ tac5x1x->input_diag_config.out_ch_en = 0;
+ if (fwnode_property_read_u32(tac5x1x->dev->fwnode,
+ "ti,out-ch-en",
+ &input_config.in_ch_en))
+ dev_dbg(tac5x1x->dev,
+ "Fail to get ti,out-ch-en value\n");
+ tac5x1x->input_diag_config.incl_se_inm = 0;
+ if (fwnode_property_read_u32(tac5x1x->dev->fwnode,
+ "ti,incl-se-inm",
+ &input_config.incl_se_inm))
+ dev_dbg(tac5x1x->dev,
+ "Fail to get ti,incl-se-inm value\n");
+ tac5x1x->input_diag_config.incl_ac_coup = 0;
+ if (fwnode_property_read_u32(tac5x1x->dev->fwnode,
+ "ti,incl-ac-coup",
+ &input_config.incl_ac_coup))
+ dev_dbg(tac5x1x->dev,
+ "Fail to get ti,incl-ac-coup value\n");
+ tac5x1x->input_diag_config = input_config;
+
+ if (fwnode_property_read_u32_array(tac5x1x->dev->fwnode,
+ "ti,micbias-threshold",
+ micbias_thr_uv, 2)) {
+ tac5x1x->micbias_thr[0] = -1;
+ tac5x1x->micbias_thr[1] = -1;
+ dev_dbg(tac5x1x->dev,
+ "ignoring micbias threshold propert read err\n");
+ } else {
+ tac5x1x->micbias_thr[0] =
+ micbias_thr_current_ua_to_reg(micbias_thr_uv[0]);
+ tac5x1x->micbias_thr[1] =
+ micbias_thr_current_ua_to_reg(micbias_thr_uv[1]);
+ }
+ break;
+ }
+out:
+ return ret;
+}
+
+static int tac5x1x_init(struct tac5x1x *tac5x1x)
+{
+ struct regmap *regmap = tac5x1x->regmap;
+
+ /* ADC Channels input coupling configuration */
+ regmap_write(regmap, TAC5X1X_ADCCH1C0, 0x04);
+ regmap_write(regmap, TAC5X1X_ADCCH2C0, 0x04);
+
+ /* Disable inputs and outputs */
+ regmap_write(regmap, TAC5X1X_CH_EN, 0x00);
+ regmap_write(regmap, TAC5X1X_PASITXCH1, 0x00);
+ regmap_write(regmap, TAC5X1X_PASITXCH2, 0x01);
+ regmap_write(regmap, TAC5X1X_PASIRXCH1, 0x00);
+ regmap_write(regmap, TAC5X1X_PASIRXCH2, 0x01);
+
+ /* clear latch irrespctive of live status */
+ regmap_write(regmap, TAC5X1X_INT, 0x11);
+
+ return 0;
+}
+
+static const struct mfd_cell tac5x1x_mfd_devs[] = {
+ {
+ .id = 0,
+ .name = "tac5x1x-pinctrl",
+ .pm_runtime_no_callbacks = true,
+ },
+ {
+ .id = 1,
+ .name = "tac5x1x-codec",
+ }
+};
+
+static int tac5x1x_setup_regulators(struct device *dev,
+ struct tac5x1x *tac5x1x)
+{
+ int i, ret;
+
+ for (i = 0; i < TAC5X1X_NUM_SUPPLIES; i++)
+ tac5x1x->supplies[i].supply = tac5x1x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, TAC5X1X_NUM_SUPPLIES,
+ tac5x1x->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
+ /* Regulators managed by PM runtime during probe */
+ tac5x1x->regulators_enabled = false;
+
+ return 0;
+}
+
+static int tac5x1x_probe(struct device *dev, struct tac5x1x *tac5x1x)
+{
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ ret = tac5x1x_setup_regulators(dev, tac5x1x);
+ if (ret)
+ return ret;
+
+ /* Initialize PM runtime before adding child devices */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_mark_last_busy(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ /* Enable regulators for device initialization */
+ ret = regulator_bulk_enable(TAC5X1X_NUM_SUPPLIES, tac5x1x->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+ tac5x1x->regulators_enabled = true;
+
+ ret = tac5x1x_reset(tac5x1x);
+ if (ret) {
+ dev_err(dev, "Failed to reset device\n");
+ goto err_disable_regulators;
+ }
+ tac5x1x_init(tac5x1x);
+
+ ret = tac5x1x_parse_dt(tac5x1x, np);
+ if (ret) {
+ dev_err(dev, "Failed to parse DT node: %d\n", ret);
+ goto err_disable_regulators;
+ }
+
+ /* update if vcom property is found */
+ if (tac5x1x->out2x_vcom_cfg != -1) {
+ regmap_update_bits(tac5x1x->regmap, TAC5X1X_OUT2CFG0,
+ TAC5X1X_OUT2CFG0_VCOM_MASK,
+ tac5x1x->out2x_vcom_cfg);
+ }
+
+ dev_dbg(dev, "%s adding mfds\n", __func__);
+
+ /* Add child devices now PM runtime is initialized */
+ ret = mfd_add_devices(dev, PLATFORM_DEVID_NONE, tac5x1x_mfd_devs,
+ ARRAY_SIZE(tac5x1x_mfd_devs), NULL, 0, NULL);
+ if (ret) {
+ dev_err(dev, "Failed to add mfd devices\n");
+ goto err_remove_mfd;
+ }
+
+ return 0;
+
+err_remove_mfd:
+ mfd_remove_devices(dev);
+err_disable_regulators:
+ if (tac5x1x->regulators_enabled) {
+ regulator_bulk_disable(TAC5X1X_NUM_SUPPLIES, tac5x1x->supplies);
+ tac5x1x->regulators_enabled = false;
+ }
+ return ret;
+}
+
+static void tac5x1x_remove(struct tac5x1x *tac5x1x)
+{
+ mfd_remove_devices(tac5x1x->dev);
+ /* Only disable regulators if they are still enabled */
+ if (tac5x1x->regulators_enabled) {
+ regulator_bulk_disable(TAC5X1X_NUM_SUPPLIES, tac5x1x->supplies);
+ tac5x1x->regulators_enabled = false;
+ }
+}
+
+const struct of_device_id tac5x1x_of_match[] = {
+ { .compatible = "ti,taa5212", .data = (void *)TAA5212 },
+ { .compatible = "ti,taa5412", .data = (void *)TAA5412 },
+ { .compatible = "ti,tac5111", .data = (void *)TAC5111 },
+ { .compatible = "ti,tac5112", .data = (void *)TAC5112 },
+ { .compatible = "ti,tac5211", .data = (void *)TAC5211 },
+ { .compatible = "ti,tac5212", .data = (void *)TAC5212 },
+ { .compatible = "ti,tac5301", .data = (void *)TAC5301 },
+ { .compatible = "ti,tac5311", .data = (void *)TAC5311 },
+ { .compatible = "ti,tac5312", .data = (void *)TAC5312 },
+ { .compatible = "ti,tac5411", .data = (void *)TAC5411 },
+ { .compatible = "ti,tac5412", .data = (void *)TAC5412 },
+ { .compatible = "ti,tad5112", .data = (void *)TAD5112 },
+ { .compatible = "ti,tad5212", .data = (void *)TAD5212 },
+ {}
+};
+MODULE_DEVICE_TABLE(of, tac5x1x_of_match);
+
+static int tac5x1x_i2c_probe(struct i2c_client *i2c)
+{
+ enum tac5x1x_type type;
+ struct tac5x1x *tac5x1x;
+ struct regmap *regmap;
+ const struct regmap_config *config = &tac5x1x_regmap;
+
+ tac5x1x = devm_kzalloc(&i2c->dev, sizeof(struct tac5x1x),
+ GFP_KERNEL);
+ if (!tac5x1x)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, tac5x1x);
+ regmap = devm_regmap_init_i2c(i2c, config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+ type = (uintptr_t)i2c_get_match_data(i2c);
+
+ tac5x1x->dev = &i2c->dev;
+ tac5x1x->codec_type = type;
+ tac5x1x->regmap = regmap;
+
+ return tac5x1x_probe(&i2c->dev, tac5x1x);
+}
+
+static void tac5x1x_i2c_remove(struct i2c_client *client)
+{
+ tac5x1x_remove(i2c_get_clientdata(client));
+}
+
+static const struct i2c_device_id tac5x1x_id[] = {
+ {"taa5212", TAA5212},
+ {"taa5412", TAA5412},
+ {"tac5111", TAC5111},
+ {"tac5112", TAC5112},
+ {"tac5211", TAC5211},
+ {"tac5212", TAC5212},
+ {"tac5301", TAC5301},
+ {"tac5311", TAC5311},
+ {"tac5312", TAC5312},
+ {"tac5411", TAC5411},
+ {"tac5412", TAC5412},
+ {"tad5112", TAD5112},
+ {"tad5212", TAD5212},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tac5x1x_id);
+
+static DEFINE_RUNTIME_DEV_PM_OPS(tac5x1x_pm_ops, tac5x1x_suspend,
+ tac5x1x_resume, NULL);
+
+static struct i2c_driver tac5x1x_i2c_driver = {
+ .driver = {
+ .name = "tac5x1x-core",
+ .pm = pm_ptr(&tac5x1x_pm_ops),
+ .of_match_table = of_match_ptr(tac5x1x_of_match),
+ },
+ .probe = tac5x1x_i2c_probe,
+ .remove = tac5x1x_i2c_remove,
+ .id_table = tac5x1x_id,
+};
+module_i2c_driver(tac5x1x_i2c_driver);
+
+MODULE_DESCRIPTION("Core support for the ASoC tac5x1x codec driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Niranjan H Y <niranjan.hy@ti.com>");
+MODULE_SOFTDEP("pre: tac5x1x_pinctrl");
diff --git a/include/linux/mfd/tac5x1x/core.h b/include/linux/mfd/tac5x1x/core.h
new file mode 100644
index 000000000000..117665e36724
--- /dev/null
+++ b/include/linux/mfd/tac5x1x/core.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * mfd/tac5x1x/core.h -- TAC5x1x Core Interface
+ *
+ * Copyright 2025 Texas Instruments Incorporated
+ *
+ * Author: Niranjan H Y <niranjan.hy@ti.com>
+ */
+#ifndef __MFD_TAC5X1X_CORE_H__
+#define __MFD_TAC5X1X_CORE_H__
+
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+enum tac5x1x_type {
+ TAA5212 = 0,
+ TAA5412,
+ TAC5111,
+ TAC5112,
+ TAC5211,
+ TAC5212,
+ TAC5301,
+ TAC5311,
+ TAC5312,
+ TAC5411,
+ TAC5412,
+ TAD5112,
+ TAD5212,
+};
+
+struct tac5x1x_input_diag_config {
+ s32 in_ch_en;
+ s32 out_ch_en;
+ s32 incl_se_inm;
+ s32 incl_ac_coup;
+};
+
+struct tac5x1x_irqinfo {
+ s32 irq_gpio;
+ s32 irq;
+ bool irq_enable;
+ u32 *latch_regs;
+ u8 *latch_data;
+};
+
+#define TAC5X1X_NUM_SUPPLIES 2
+
+struct tac5x1x {
+ enum tac5x1x_type codec_type;
+ s32 vref_vg;
+ s32 micbias_vg;
+ s32 uad_en;
+ s32 vad_en;
+ s32 uag_en;
+ s32 micbias_thr[2];
+ s32 gpa_threshold[2];
+ s32 adc_impedance[2];
+ s32 out2x_vcom_cfg;
+ bool pdm_enabled;
+ bool regulators_enabled;
+ struct tac5x1x_input_diag_config input_diag_config;
+ struct regulator_bulk_data supplies[TAC5X1X_NUM_SUPPLIES];
+ struct device *dev;
+ struct regmap *regmap;
+};
+
+#endif
diff --git a/include/linux/mfd/tac5x1x/registers.h b/include/linux/mfd/tac5x1x/registers.h
new file mode 100644
index 000000000000..49c327316e82
--- /dev/null
+++ b/include/linux/mfd/tac5x1x/registers.h
@@ -0,0 +1,291 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Register definitions for TAC5x1x
+ *
+ * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com
+ * Author: Niranjan H Y <niranjan.hy@ti.com>
+ */
+
+#ifndef __TAC5X1X_REGISTERS_H__
+#define __TAC5X1X_REGISTERS_H__
+
+#define TAC_PAGE_SELECT 0x00
+#define TAC_PAGE_ID(reg) ((reg) / 128)
+#define TAC_PAGE_REG(reg) ((reg) % 128)
+#define TAC5X1X_REG(page, reg) (((page) * 128) + (reg))
+
+#define TAC5X1X_RESET TAC5X1X_REG(0, 0x1)
+#define TAC5X1X_VREF TAC5X1X_REG(0, 0x2)
+#define TAC5X1X_VDDSTS TAC5X1X_REG(0, 0x3)
+#define TAC5X1X_MISC TAC5X1X_REG(0, 0x4)
+#define TAC5X1X_MISC1 TAC5X1X_REG(0, 0x5)
+#define TAC5X1X_DACA0 TAC5X1X_REG(0, 0x6)
+#define TAC5X1X_MISC0 TAC5X1X_REG(0, 0x7)
+/* gpio control registers */
+#define TAC5X1X_GPIO1 TAC5X1X_REG(0, 0xa)
+#define TAC5X1X_GPIO2 TAC5X1X_REG(0, 0xb)
+#define TAC5X1X_GPO1 TAC5X1X_REG(0, 0xc)
+#define TAC5X1X_GPI1 TAC5X1X_REG(0, 0xd)
+
+#define TAC5X1X_GPIOVAL TAC5X1X_REG(0, 0xe)
+#define TAC5X1X_INTF0 TAC5X1X_REG(0, 0xf)
+#define TAC5X1X_INTF1 TAC5X1X_REG(0, 0x10)
+#define TAC5X1X_INTF2 TAC5X1X_REG(0, 0x11)
+#define TAC5X1X_INTF3 TAC5X1X_REG(0, 0x12)
+#define TAC5X1X_INTF4 TAC5X1X_REG(0, 0x13)
+#define TAC5X1X_INTF5 TAC5X1X_REG(0, 0x14)
+#define TAC5X1X_INTF6 TAC5X1X_REG(0, 0x15)
+#define TAC5X1X_ASI0 TAC5X1X_REG(0, 0x18)
+#define TAC5X1X_ASI1 TAC5X1X_REG(0, 0x19)
+#define TAC5X1X_PASI0 TAC5X1X_REG(0, 0x1a)
+#define TAC5X1X_PASITX0 TAC5X1X_REG(0, 0x1b)
+#define TAC5X1X_PASITX1 TAC5X1X_REG(0, 0x1c)
+#define TAC5X1X_PASITX2 TAC5X1X_REG(0, 0x1d)
+#define TAC5X1X_PASITXCH1 TAC5X1X_REG(0, 0x1e)
+#define TAC5X1X_PASITXCH2 TAC5X1X_REG(0, 0x1f)
+#define TAC5X1X_PASITXCH3 TAC5X1X_REG(0, 0x20)
+#define TAC5X1X_PASITXCH4 TAC5X1X_REG(0, 0x21)
+#define TAC5X1X_PASITXCH5 TAC5X1X_REG(0, 0x22)
+#define TAC5X1X_PASITXCH6 TAC5X1X_REG(0, 0x23)
+#define TAC5X1X_PASITXCH7 TAC5X1X_REG(0, 0x24)
+#define TAC5X1X_PASITXCH8 TAC5X1X_REG(0, 0x25)
+#define TAC5X1X_PASIRX0 TAC5X1X_REG(0, 0x26)
+#define TAC5X1X_PASIRX1 TAC5X1X_REG(0, 0x27)
+#define TAC5X1X_PASIRXCH1 TAC5X1X_REG(0, 0x28)
+#define TAC5X1X_PASIRXCH2 TAC5X1X_REG(0, 0x29)
+#define TAC5X1X_PASIRXCH3 TAC5X1X_REG(0, 0x2a)
+#define TAC5X1X_PASIRXCH4 TAC5X1X_REG(0, 0x2b)
+#define TAC5X1X_PASIRXCH5 TAC5X1X_REG(0, 0x2c)
+#define TAC5X1X_PASIRXCH6 TAC5X1X_REG(0, 0x2d)
+#define TAC5X1X_PASIRXCH7 TAC5X1X_REG(0, 0x2e)
+#define TAC5X1X_PASIRXCH8 TAC5X1X_REG(0, 0x2f)
+#define TAC5X1X_CLK0 TAC5X1X_REG(0, 0x32)
+#define TAC5X1X_CLK1 TAC5X1X_REG(0, 0x33)
+#define TAC5X1X_CLK2 TAC5X1X_REG(0, 0x34)
+#define TAC5X1X_CNTCLK0 TAC5X1X_REG(0, 0x35)
+#define TAC5X1X_CNTCLK1 TAC5X1X_REG(0, 0x36)
+#define TAC5X1X_CNTCLK2 TAC5X1X_REG(0, 0x37)
+#define TAC5X1X_CNTCLK3 TAC5X1X_REG(0, 0x38)
+#define TAC5X1X_CNTCLK4 TAC5X1X_REG(0, 0x39)
+#define TAC5X1X_CNTCLK5 TAC5X1X_REG(0, 0x3a)
+#define TAC5X1X_CNTCLK6 TAC5X1X_REG(0, 0x3b)
+#define TAC5X1X_CLKERRSTS0 TAC5X1X_REG(0, 0x3c)
+#define TAC5X1X_CLKERRSTS1 TAC5X1X_REG(0, 0x3d)
+#define TAC5X1X_CLKDETSTS0 TAC5X1X_REG(0, 0x3e)
+#define TAC5X1X_CLKDETSTS1 TAC5X1X_REG(0, 0x3f)
+#define TAC5X1X_CLKDETSTS2 TAC5X1X_REG(0, 0x40)
+#define TAC5X1X_CLKDETSTS3 TAC5X1X_REG(0, 0x41)
+#define TAC5X1X_INT TAC5X1X_REG(0, 0x42)
+#define TAC5X1X_DAC_FLT TAC5X1X_REG(0, 0x43)
+#define TAC5X1X_ADCDACMISC TAC5X1X_REG(0, 0x4b)
+#define TAC5X1X_IADC TAC5X1X_REG(0, 0x4c)
+#define TAC5X1X_VREFCFG TAC5X1X_REG(0, 0x4d)
+#define TAC5X1X_PWRTUNE0 TAC5X1X_REG(0, 0x4e)
+#define TAC5X1X_PWRTUNE1 TAC5X1X_REG(0, 0x4f)
+#define TAC5X1X_ADCCH1C0 TAC5X1X_REG(0, 0x50)
+#define TAC5X1X_ADCCH TAC5X1X_REG(0, 0x51)
+#define TAC5X1X_ADCCH1C2 TAC5X1X_REG(0, 0x52)
+#define TAC5X1X_ADCCH1C3 TAC5X1X_REG(0, 0x53)
+#define TAC5X1X_ADCCH1C4 TAC5X1X_REG(0, 0x54)
+#define TAC5X1X_ADCCH2C0 TAC5X1X_REG(0, 0x55)
+#define TAC5X1X_ADCCH2C2 TAC5X1X_REG(0, 0x57)
+#define TAC5X1X_ADCCH2C3 TAC5X1X_REG(0, 0x58)
+#define TAC5X1X_ADCCH2C4 TAC5X1X_REG(0, 0x59)
+#define TAC5X1X_ADCCH3C0 TAC5X1X_REG(0, 0x5a)
+#define TAC5X1X_ADCCH3C2 TAC5X1X_REG(0, 0x5b)
+#define TAC5X1X_ADCCH3C3 TAC5X1X_REG(0, 0x5c)
+#define TAC5X1X_ADCCH3C4 TAC5X1X_REG(0, 0x5d)
+#define TAC5X1X_ADCCH4C0 TAC5X1X_REG(0, 0x5e)
+#define TAC5X1X_ADCCH4C2 TAC5X1X_REG(0, 0x5f)
+#define TAC5X1X_ADCCH4C3 TAC5X1X_REG(0, 0x60)
+#define TAC5X1X_ADCCH4C4 TAC5X1X_REG(0, 0x61)
+#define TAC5X1X_ADCCFG1 TAC5X1X_REG(0, 0x62)
+#define TAC5X1X_OUT1CFG0 TAC5X1X_REG(0, 0x64)
+#define TAC5X1X_OUT1CFG1 TAC5X1X_REG(0, 0x65)
+#define TAC5X1X_OUT1CFG2 TAC5X1X_REG(0, 0x66)
+#define TAC5X1X_DACCH1A0 TAC5X1X_REG(0, 0x67)
+#define TAC5X1X_DACCH1A1 TAC5X1X_REG(0, 0x68)
+#define TAC5X1X_DACCH1B0 TAC5X1X_REG(0, 0x69)
+#define TAC5X1X_DACCH1B1 TAC5X1X_REG(0, 0x6a)
+#define TAC5X1X_OUT2CFG0 TAC5X1X_REG(0, 0x6b)
+#define TAC5X1X_OUT2CFG1 TAC5X1X_REG(0, 0x6c)
+#define TAC5X1X_OUT2CFG2 TAC5X1X_REG(0, 0x6d)
+#define TAC5X1X_DACCH2A0 TAC5X1X_REG(0, 0x6e)
+#define TAC5X1X_DACCH2A1 TAC5X1X_REG(0, 0x6f)
+#define TAC5X1X_DACCH2B0 TAC5X1X_REG(0, 0x70)
+#define TAC5X1X_DACCH2B1 TAC5X1X_REG(0, 0x71)
+#define TAC5X1X_DSP0 TAC5X1X_REG(0, 0x72)
+#define TAC5X1X_DSP1 TAC5X1X_REG(0, 0x73)
+#define TAC5X1X_CH_EN TAC5X1X_REG(0, 0x76)
+#define TAC5X1X_DYN_PUPD TAC5X1X_REG(0, 0x77)
+#define TAC5X1X_PWR_CFG TAC5X1X_REG(0, 0x78)
+#define TAC5X1X_DEVSTS0 TAC5X1X_REG(0, 0x79)
+#define TAC5X1X_DEVSTS1 TAC5X1X_REG(0, 0x7a)
+
+#define TAC5X1X_CLKCFG0 TAC5X1X_REG(1, 0xd)
+#define TAC5X1X_MICBIAS1 TAC5X1X_REG(1, 0x16)
+#define TAC5X1X_AGC_DRC TAC5X1X_REG(1, 0x24)
+#define TAC5X1X_PLIM TAC5X1X_REG(1, 0x2b)
+#define TAC5X1X_MIXER TAC5X1X_REG(1, 0x2c)
+
+#define TAC5X1X_DIAG_CFG0 TAC5X1X_REG(1, 0x46)
+#define TAC5X1X_DIAG_CFG1 TAC5X1X_REG(1, 0x47)
+#define TAC5X1X_DIAG_CFG2 TAC5X1X_REG(1, 0x48)
+#define TAC5X1X_DIAG_CFG6 TAC5X1X_REG(1, 0x4c)
+#define TAC5X1X_DIAG_CFG7 TAC5X1X_REG(1, 0x4d)
+#define TAC5X1X_DIAG_CFG8 TAC5X1X_REG(1, 0x4e)
+#define TAC5X1X_DIAG_CFG9 TAC5X1X_REG(1, 0x4f)
+
+/* interrupt latches */
+#define TAC5X1X_REG_INT_LTCH0 TAC5X1X_REG(0x1, 0x34)
+#define TAC5X1X_REG_CHX_LTCH TAC5X1X_REG(0x1, 0x35)
+#define TAC5X1X_REG_IN_CH1_LTCH TAC5X1X_REG(0x1, 0x36)
+#define TAC5X1X_REG_IN_CH2_LTCH TAC5X1X_REG(0x1, 0x37)
+#define TAC5X1X_REG_OUT_CH1_LTCH TAC5X1X_REG(0x1, 0x38)
+#define TAC5X1X_REG_OUT_CH2_LTCH TAC5X1X_REG(0x1, 0x39)
+#define TAC5X1X_REG_INT_LTCH1 TAC5X1X_REG(0x1, 0x3A)
+#define TAC5X1X_REG_INT_LTCH2 TAC5X1X_REG(0x1, 0x3B)
+
+/* Bits, masks, and shifts */
+/* TAC5X1X_CH_EN */
+#define TAC5X1X_CH_EN_ADC_MASK GENMASK(7, 4)
+#define TAC5X1X_CH_EN_ADC_CH1 BIT(7)
+#define TAC5X1X_CH_EN_ADC_CH2 BIT(6)
+#define TAC5X1X_CH_EN_ADC_CH3 BIT(5)
+#define TAC5X1X_CH_EN_ADC_CH4 BIT(4)
+
+#define TAC5X1X_CH_EN_DAC_MASK GENMASK(3, 0)
+#define TAC5X1X_CH_EN_DAC_CH1 BIT(3)
+#define TAC5X1X_CH_EN_DAC_CH2 BIT(2)
+#define TAC5X1X_CH_EN_DAC_CH3 BIT(1)
+#define TAC5X1X_CH_EN_DAC_CH4 BIT(0)
+
+/* TAC5X1X_GPIOVAL */
+#define TAC5X1X_GPIO1_VAL BIT(7)
+#define TAC5X1X_GPIO2_VAL BIT(6)
+#define TAC5X1X_GPO1_VAL BIT(5)
+#define TAC5X1X_GPIO1_MON BIT(3)
+#define TAC5X1X_GPIO2_MON BIT(2)
+#define TAC5X1X_GPI1_MON BIT(1)
+
+/* TAC5X1X_DIAG_CFG0 */
+#define TAC5X1X_IN_CH_DIAG_EN_MASK 0xc0
+#define TAC5X1X_INCL_SE_INM_MASK 0x20
+#define TAC5X1X_INCL_AC_COUP_MASK 0x10
+#define TAC5X1X_OUT1P_DIAG_EN_MASK 0x0f
+#define TAC5X1X_MICBIAS_LOW_THRESHOLD 0x48
+#define TAC5X1X_MICBIAS_HIGH_THRESHOLD 0xa2
+#define TAC5X1X_GPA_LOW_THRESHOLD 0x4b
+#define TAC5X1X_GPA_HIGH_THRESHOLD 0xba
+
+/* TAC5X1X_PASI0 */
+#define TAC5X1X_PASI_SAMP_RATE_MASK GENMASK(7, 2)
+#define TAC5X1X_PASI_FMT_MASK GENMASK(7, 6)
+#define TAC5X1X_PASI_FMT_TDM 0x00
+#define TAC5X1X_PASI_FMT_I2S 0x40
+#define TAC5X1X_PASI_FMT_LJ 0x80
+
+#define TAC5X1X_PASI_DATALEN_MASK GENMASK(5, 4)
+
+#define TAC5X1X_PASI_FSYNC_POL BIT(3)
+#define TAC5X1X_PASI_BCLK_POL BIT(2)
+#define TAC5X1X_WORD_LEN_16BITS 0x00
+#define TAC5X1X_WORD_LEN_20BITS 0x10
+#define TAC5X1X_WORD_LEN_24BITS 0x20
+#define TAC5X1X_WORD_LEN_32BITS 0x30
+
+/* TAC5X1X_CNTCLK2 */
+#define TAC5X1X_PASI_MODE_MASK 0x10
+#define TAC5X1X_SASI_MODE_MASK 0x08
+#define TAC5X1X_ASI_RATE_MASK 0x01
+
+#define TAC5X1X_PASI_RATE_48000 0x00
+#define TAC5X1X_PASI_RATE_44100 0x01
+
+/* TAC5X1X_PASITX0 */
+#define TAC5X1X_PASITX_OFFSET_MASK 0x1f
+
+/* TAC5X1X_PASIRX0 */
+#define TAC5X1X_PASIRX_OFFSET_MASK 0x1f
+
+/* TAC5X1X_VREF */
+#define TAC5X1X_VREF_SLEEP_EXIT_VREF_EN 0x80
+#define TAC5X1X_VREF_SLEEP_ACTIVE_MASK 0x01
+
+/* TAC5X1X_PWRCFG */
+#define TAC5X1X_PWR_CFG_ADC_PDZ BIT(7)
+#define TAC5X1X_PWR_CFG_DAC_PDZ BIT(6)
+#define TAC5X1X_PWR_CFG_MICBIAS BIT(5)
+#define TAC5X1X_PWR_CFG_UAD_EN BIT(3)
+#define TAC5X1X_PWR_CFG_VAD_EN BIT(2)
+#define TAC5X1X_PWR_CFG_UAG_EN BIT(1)
+
+/* TAC5X1X_GPIOx */
+#define TAC5X1X_GPIO1_DEFAULT_VAL 0x32
+#define TAC5X1X_GPIO2_DEFAULT_VAL 0x00
+#define TAC5X1X_GPI1_DEFAULT_VAL 0x00
+#define TAC5X1X_GPO1_DEFAULT_VAL 0x00
+
+#define TAC5X1X_GPIOX_CFG_MASK 0xf0
+#define TAC5X1X_GPI1_EN_MASK BIT(1)
+#define TAC5X1X_GPI2_EN_MASK BIT(2)
+#define TAC5X1X_GPIOX_CFG_SHFT 0x4
+#define TAC5X1X_GPIOX_DRV_MASK 0x07
+#define TAC5X1X_GPIOX_DRV_SHFT 0x0
+
+/* pdm related mask, shift values*/
+#define TAC5X1X_PDM_DIN12_SEL_MASK 0x0c
+#define TAC5X1X_PDM_DIN12_SEL_SHFT 0x2
+#define TAC5X1X_PDM_DIN34_SEL_MASK 0x03
+#define TAC5X1X_PDM_DIN34_SEL_SHFT 0x0
+/* pdm data in values */
+#define TAC5X1X_PDM_DIN_GPI01 1
+#define TAC5X1X_PDM_DIN_GPI02 2
+#define TAC5X1X_PDM_DIN_GPI2A 2
+#define TAC5X1X_PDM_DIN_GPI1 3
+
+#define TAC5X1X_GPIO_DISABLE 0
+#define TAC5X1X_GPIO_GPI 1
+#define TAC5X1X_GPIO_GPO 2
+#define TAC5X1X_GPIO_IRQ 3
+#define TAC5X1X_GPIO_PDMCLK 4
+#define TAC5X1X_GPIO_P_DOUT 5
+#define TAC5X1X_GPIO_P_DOUT2 6
+#define TAC5X1X_GPIO_S_DOUT 7
+#define TAC5X1X_GPIO_S_DOUT2 8
+#define TAC5X1X_GPIO_S_BCLK 9
+#define TAC5X1X_GPIO_S_FSYNC 10
+#define TAC5X1X_GPIO_CLKOUT 11
+#define TAC5X1X_GPIO_DOUT_MUX 12
+#define TAC5X1X_GPIO_DAISY_OUT 13
+
+#define TAC5X1X_GPIO_DRV_HIZ 0
+#define TAC5X1X_GPIO_DRV_ALAH 1
+#define TAC5X1X_GPIO_DRV_ALWH 2
+#define TAC5X1X_GPIO_DRV_ALHIZ 3
+#define TAC5X1X_GPIO_DRV_WLAH 4
+#define TAC5X1X_GPIO_DRV_HIZAH 5
+
+/* TAC5X1X_GPI1 */
+#define TAC5X1X_GPI1_CFG_MASK BIT(1)
+#define TAC5X1X_GPI2A_CFG_MASK BIT(0)
+#define TAC5X1X_GPA_CFG_MASK BIT(0)
+
+/* TAC5X1X_VREFCFG */
+#define TAC5X1X_VREFCFG_MICBIAS_VAL_MASK GENMASK(3, 2)
+#define TAC5X1X_VREFCFG_VREF_FSCALE_MASK GENMASK(1, 0)
+
+#define TAC5X1X_ADCCH1C0_IMPEDANCE_MASK GENMASK(5, 4)
+#define TAC5X1X_ADCCH2C0_IMPEDANCE_MASK GENMASK(5, 4)
+
+#define TAC5X1X_OUT2CFG0_VCOM_MASK BIT(1)
+
+#define TAC5X1X_MICBIAS_VREF 0
+#define TAC5X1X_MICBIAS_0_5VREF 1
+#define TAC5X1X_MICBIAS_AVDD 3
+
+#define TAC5X1X_VERF_2_75V 0
+#define TAC5X1X_VERF_2_5V 1
+#define TAC5X1X_VERF_1_375V 2
+
+#endif /* __TAC5X1X_REGISTERS_H__ */
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v1 6/8] pinctrl: pinctrl-tac5x1x: Add TI TAC5x1x pinctrl driver
2026-03-12 18:48 [PATCH v1 0/8] ASoC: support TI's TAC5x1x audio codec family Niranjan H Y
` (4 preceding siblings ...)
2026-03-12 18:48 ` [PATCH v1 5/8] mfd: tac5x1x: Add TI TAC5x1x MFD core driver Niranjan H Y
@ 2026-03-12 18:48 ` Niranjan H Y
2026-03-14 10:11 ` Krzysztof Kozlowski
2026-03-12 18:48 ` [PATCH v1 7/8] ASoC: tac5x1x: Add TI TAC5x1x codec driver Niranjan H Y
2026-03-12 18:48 ` [PATCH v1 8/8] ASoC: pcm6240: remove support for taac5x1x family Niranjan H Y
7 siblings, 1 reply; 18+ messages in thread
From: Niranjan H Y @ 2026-03-12 18:48 UTC (permalink / raw)
To: linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, niranjan.hy, nb, navada, v-hampiholi, sandeepk,
baojun.xu, shenghao-ding
Add the Texas Instruments TAC5x1x pin controller driver. This driver
provides GPIO control for 5 configurable pins (GPIO1, GPIO2, GPO1, GPI1,
GPI2A) with support for GPIO, PDM, and IRQ functions.
The pinctrl driver handles:
- GPIO input/output operations
- Pin multiplexing between GPIO, PDM, and IRQ functions
Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
---
drivers/pinctrl/Kconfig | 11 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-tac5x1x.c | 889 ++++++++++++++++++++++++++++++
3 files changed, 901 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-tac5x1x.c
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index afecd9407f53..2054e9998880 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -585,6 +585,17 @@ config PINCTRL_SX150X
- 8 bits: sx1508q, sx1502q
- 16 bits: sx1509q, sx1506q
+config PINCTRL_TI_TAC5X1X
+ tristate "TAC5X1X pin control driver"
+ depends on OF && (MFD_TAC5X1X || COMPILE_TEST)
+ select GENERIC_PINCTRL_GROUPS
+ select GENERIC_PINMUX_FUNCTIONS
+ select GENERIC_PINCONF
+ help
+ TAC5X1X family may have 1 or more configurable GPIO pins
+ which can be grouped and configured to function as PDM, GPIOs
+ or interrupt outputs.
+
config PINCTRL_TB10X
bool "Pinctrl for TB10X" if COMPILE_TEST
depends on OF && ARC_PLAT_TB10X || COMPILE_TEST
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f7d5d5f76d0c..1fe6b0e8a666 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o
obj-$(CONFIG_PINCTRL_SX150X) += pinctrl-sx150x.o
+obj-$(CONFIG_PINCTRL_TI_TAC5X1X)+= pinctrl-tac5x1x.o
obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o
obj-$(CONFIG_PINCTRL_TPS6594) += pinctrl-tps6594.o
obj-$(CONFIG_PINCTRL_TH1520) += pinctrl-th1520.o
diff --git a/drivers/pinctrl/pinctrl-tac5x1x.c b/drivers/pinctrl/pinctrl-tac5x1x.c
new file mode 100644
index 000000000000..4e59501f3f78
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tac5x1x.c
@@ -0,0 +1,889 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TAC5X1X Pinctrl and GPIO driver
+ *
+ * Copyright (C) 2023-2025 Texas Instruments Incorporated - https://www.ti.com
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/build_bug.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/mfd/tac5x1x/registers.h>
+#include <linux/mfd/tac5x1x/core.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/string_choices.h>
+
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include "pinctrl-utils.h"
+
+/* 2 pins can be gpio */
+#define TAC5X1X_NUM_GPIO_PINS 5
+#define TAC5X1X_MAX_PINS 5
+
+struct tac5x1x_pin {
+ struct gpio_chip gpio_chip;
+ struct regmap *regmap;
+ struct device *dev;
+ struct tac5x1x *parent;
+};
+
+static const struct pinctrl_pin_desc tac5x1x_pin_pins[] = {
+ /* naming as per data sheet */
+ PINCTRL_PIN(0, "GPIO_1"),
+ PINCTRL_PIN(1, "GPIO_2"),
+ PINCTRL_PIN(2, "GPO_1"), /* same as GPO1A*/
+ PINCTRL_PIN(3, "GPI_1"), /* same as GPI1A*/
+ PINCTRL_PIN(4, "GPI_2A")
+};
+
+/* pin control registers */
+static const u32 pin_to_reg[] = {
+ TAC5X1X_GPIO1,
+ TAC5X1X_GPIO2,
+ TAC5X1X_GPO1,
+ TAC5X1X_GPI1,
+ TAC5X1X_GPI1,
+};
+
+static_assert(ARRAY_SIZE(tac5x1x_pin_pins) == ARRAY_SIZE(pin_to_reg));
+
+enum tac5x1x_gpio_pins {
+ PIN_GPIO1 = 0,
+ PIN_GPIO2,
+ PIN_GPO1,
+ PIN_GPI1,
+ PIN_GPI2A,
+};
+
+enum tac5x1x_pin_funcs {
+ TAC5X1X_FUNC_GPIO,
+ TAC5X1X_FUNC_PDM,
+ TAC5X1X_FUNC_IRQ,
+ TAC5X1X_FUNC_MAX
+};
+
+struct tac5x1x_config {
+ u32 reg;
+ u32 mask;
+ u32 val;
+};
+
+static const unsigned int tac5x1x_pin_gpio1_pins[] = { PIN_GPIO1 };
+static const unsigned int tac5x1x_pin_gpio2_pins[] = { PIN_GPIO2 };
+static const unsigned int tac5x1x_pin_gpo1_pins[] = { PIN_GPO1 };
+static const unsigned int tac5x1x_pin_gpi1_pins[] = { PIN_GPI1 };
+static const unsigned int tac5x1x_pin_gpi2a_pins[] = { PIN_GPI2A };
+
+/* pdm pins for - clk, data in */
+static const unsigned int tac5x1x_pin_pdm01_pins[] = { PIN_GPIO1, PIN_GPIO2 };
+static const unsigned int tac5x1x_pin_pdm03_pins[] = { PIN_GPIO1, PIN_GPI1 };
+static const unsigned int tac5x1x_pin_pdm04_pins[] = { PIN_GPIO1, PIN_GPI2A };
+static const unsigned int tac5x1x_pin_pdm10_pins[] = { PIN_GPIO2, PIN_GPIO1 };
+static const unsigned int tac5x1x_pin_pdm13_pins[] = { PIN_GPIO2, PIN_GPI1 };
+static const unsigned int tac5x1x_pin_pdm14_pins[] = { PIN_GPIO2, PIN_GPI2A };
+static const unsigned int tac5x1x_pin_pdm20_pins[] = { PIN_GPO1, PIN_GPIO1 };
+static const unsigned int tac5x1x_pin_pdm21_pins[] = { PIN_GPO1, PIN_GPIO2 };
+static const unsigned int tac5x1x_pin_pdm23_pins[] = { PIN_GPO1, PIN_GPI1 };
+static const unsigned int tac5x1x_pin_pdm24_pins[] = { PIN_GPO1, PIN_GPI2A };
+
+#define TAC5X1X_CFG_ENTRY(_reg, _mask, _val) \
+ { \
+ .reg = TAC5X1X_##_reg, \
+ .mask = TAC5X1X_##_mask,\
+ .val = TAC5X1X_##_val \
+ }
+
+static const struct tac5x1x_config pdm_cfgs[] = {
+ /* pdm01 */
+ TAC5X1X_CFG_ENTRY(GPIO1, GPIOX_CFG_MASK, GPIO_PDMCLK),
+ TAC5X1X_CFG_ENTRY(GPIO2, GPIOX_CFG_MASK, GPIO_GPI),
+ TAC5X1X_CFG_ENTRY(INTF4, PDM_DIN12_SEL_MASK, PDM_DIN_GPI02),
+
+ /* pdm03 */
+ TAC5X1X_CFG_ENTRY(GPIO1, GPIOX_CFG_MASK, GPIO_PDMCLK),
+ TAC5X1X_CFG_ENTRY(GPI1, GPI1_CFG_MASK, GPIO_GPI),
+ TAC5X1X_CFG_ENTRY(INTF4, PDM_DIN12_SEL_MASK, PDM_DIN_GPI1),
+
+ /* pdm04 */
+ TAC5X1X_CFG_ENTRY(GPIO1, GPIOX_CFG_MASK, GPIO_PDMCLK),
+ TAC5X1X_CFG_ENTRY(GPI1, GPI2A_CFG_MASK, GPIO_GPI),
+ TAC5X1X_CFG_ENTRY(INTF4, PDM_DIN12_SEL_MASK, PDM_DIN_GPI2A),
+
+ /* pdm10 */
+ TAC5X1X_CFG_ENTRY(GPIO2, GPIOX_CFG_MASK, GPIO_PDMCLK),
+ TAC5X1X_CFG_ENTRY(GPIO1, GPIOX_CFG_MASK, GPIO_GPI),
+ TAC5X1X_CFG_ENTRY(INTF4, PDM_DIN12_SEL_MASK, PDM_DIN_GPI01),
+
+ /* pdm13 */
+ TAC5X1X_CFG_ENTRY(GPIO2, GPIOX_CFG_MASK, GPIO_PDMCLK),
+ TAC5X1X_CFG_ENTRY(GPI1, GPI1_CFG_MASK, GPIO_GPI),
+ TAC5X1X_CFG_ENTRY(INTF4, PDM_DIN12_SEL_MASK, PDM_DIN_GPI1),
+
+ /* pdm14 */
+ TAC5X1X_CFG_ENTRY(GPIO2, GPIOX_CFG_MASK, GPIO_PDMCLK),
+ TAC5X1X_CFG_ENTRY(GPI1, GPI2A_CFG_MASK, GPIO_GPI),
+ TAC5X1X_CFG_ENTRY(INTF4, PDM_DIN12_SEL_MASK, PDM_DIN_GPI2A),
+
+ /* pdm20 */
+ TAC5X1X_CFG_ENTRY(GPO1, GPIOX_CFG_MASK, GPIO_PDMCLK),
+ TAC5X1X_CFG_ENTRY(GPIO1, GPIOX_CFG_MASK, GPIO_GPI),
+ TAC5X1X_CFG_ENTRY(INTF4, PDM_DIN12_SEL_MASK, PDM_DIN_GPI01),
+
+ /* pdm21 */
+ TAC5X1X_CFG_ENTRY(GPO1, GPIOX_CFG_MASK, GPIO_PDMCLK),
+ TAC5X1X_CFG_ENTRY(GPIO2, GPIOX_CFG_MASK, GPIO_GPI),
+ TAC5X1X_CFG_ENTRY(INTF4, PDM_DIN12_SEL_MASK, PDM_DIN_GPI02),
+
+ /* pdm23 */
+ TAC5X1X_CFG_ENTRY(GPO1, GPIOX_CFG_MASK, GPIO_PDMCLK),
+ TAC5X1X_CFG_ENTRY(GPI1, GPI1_CFG_MASK, GPIO_GPI),
+ TAC5X1X_CFG_ENTRY(INTF4, PDM_DIN12_SEL_MASK, PDM_DIN_GPI1),
+
+ /* pdm24 */
+ TAC5X1X_CFG_ENTRY(GPO1, GPIOX_CFG_MASK, GPIO_PDMCLK),
+ TAC5X1X_CFG_ENTRY(GPI1, GPI2A_CFG_MASK, GPIO_GPI),
+ TAC5X1X_CFG_ENTRY(INTF4, PDM_DIN12_SEL_MASK, PDM_DIN_GPI2A),
+};
+
+static const struct tac5x1x_config irq_cfgs[] = {
+ TAC5X1X_CFG_ENTRY(GPIO1, GPIOX_CFG_MASK, GPIO_IRQ),
+ TAC5X1X_CFG_ENTRY(GPIO2, GPIOX_CFG_MASK, GPIO_IRQ),
+ TAC5X1X_CFG_ENTRY(GPO1, GPIOX_CFG_MASK, GPIO_IRQ),
+};
+
+struct tac5x1x_pingroup {
+ const char *name;
+ const unsigned int *pins;
+ const struct tac5x1x_config *regdata;
+ size_t npins; /* number of pins */
+ size_t nregdata; /* number of regdata */
+};
+
+#define TAC5X1X_PINCTRL_PINGROUP(_name, _pins, _npins, _regdata, _nregdata) \
+((struct tac5x1x_pingroup) { \
+ .name = (_name), \
+ .pins = (_pins), \
+ .npins = (_npins), \
+ .regdata = (_regdata), \
+ .nregdata = (_nregdata), \
+})
+
+static const struct tac5x1x_pingroup tac5x1x_pin_groups[] = {
+ TAC5X1X_PINCTRL_PINGROUP("gpio1", tac5x1x_pin_gpio1_pins,
+ ARRAY_SIZE(tac5x1x_pin_gpio1_pins), 0, 0),
+ TAC5X1X_PINCTRL_PINGROUP("gpio2", tac5x1x_pin_gpio2_pins,
+ ARRAY_SIZE(tac5x1x_pin_gpio2_pins), 0, 0),
+ TAC5X1X_PINCTRL_PINGROUP("gpo1", tac5x1x_pin_gpo1_pins,
+ ARRAY_SIZE(tac5x1x_pin_gpo1_pins), 0, 0),
+ TAC5X1X_PINCTRL_PINGROUP("gpi1", tac5x1x_pin_gpi1_pins,
+ ARRAY_SIZE(tac5x1x_pin_gpi1_pins), 0, 0),
+ TAC5X1X_PINCTRL_PINGROUP("gpi2a", tac5x1x_pin_gpi2a_pins,
+ ARRAY_SIZE(tac5x1x_pin_gpi2a_pins), 0, 0),
+ TAC5X1X_PINCTRL_PINGROUP("pdm_gpio1_gpio2", tac5x1x_pin_pdm01_pins,
+ ARRAY_SIZE(tac5x1x_pin_pdm01_pins),
+ &pdm_cfgs[0], 3),
+ TAC5X1X_PINCTRL_PINGROUP("pdm_gpio1_gpi1", tac5x1x_pin_pdm03_pins,
+ ARRAY_SIZE(tac5x1x_pin_pdm03_pins),
+ &pdm_cfgs[3], 3),
+ TAC5X1X_PINCTRL_PINGROUP("pdm_gpio1_gpi2a", tac5x1x_pin_pdm04_pins,
+ ARRAY_SIZE(tac5x1x_pin_pdm04_pins),
+ &pdm_cfgs[6], 3),
+ TAC5X1X_PINCTRL_PINGROUP("pdm_gpio2_gpio1", tac5x1x_pin_pdm10_pins,
+ ARRAY_SIZE(tac5x1x_pin_pdm10_pins),
+ &pdm_cfgs[9], 3),
+ TAC5X1X_PINCTRL_PINGROUP("pdm_gpio2_gpi1", tac5x1x_pin_pdm13_pins,
+ ARRAY_SIZE(tac5x1x_pin_pdm13_pins),
+ &pdm_cfgs[12], 3),
+ TAC5X1X_PINCTRL_PINGROUP("pdm_gpio2_gpi2a", tac5x1x_pin_pdm14_pins,
+ ARRAY_SIZE(tac5x1x_pin_pdm14_pins),
+ &pdm_cfgs[15], 3),
+ TAC5X1X_PINCTRL_PINGROUP("pdm_gpo1_gpio1", tac5x1x_pin_pdm20_pins,
+ ARRAY_SIZE(tac5x1x_pin_pdm20_pins),
+ &pdm_cfgs[18], 3),
+ TAC5X1X_PINCTRL_PINGROUP("pdm_gpo1_gpio2", tac5x1x_pin_pdm21_pins,
+ ARRAY_SIZE(tac5x1x_pin_pdm21_pins),
+ &pdm_cfgs[21], 3),
+ TAC5X1X_PINCTRL_PINGROUP("pdm_gpo1_gpi1", tac5x1x_pin_pdm23_pins,
+ ARRAY_SIZE(tac5x1x_pin_pdm23_pins),
+ &pdm_cfgs[24], 3),
+ TAC5X1X_PINCTRL_PINGROUP("pdm_gpo1_gpi2a", tac5x1x_pin_pdm24_pins,
+ ARRAY_SIZE(tac5x1x_pin_pdm24_pins),
+ &pdm_cfgs[27], 3),
+};
+
+static int tac5x1x_pin_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ return ARRAY_SIZE(tac5x1x_pin_groups);
+}
+
+static const char *tac5x1x_pin_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned int group)
+{
+ return tac5x1x_pin_groups[group].name;
+}
+
+static int tac5x1x_pin_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned int group,
+ const unsigned int **pins,
+ unsigned int *num_pins)
+{
+ *pins = tac5x1x_pin_groups[group].pins;
+ *num_pins = tac5x1x_pin_groups[group].npins;
+ return 0;
+}
+
+static const struct pinctrl_ops tac5x1x_pin_group_ops = {
+ .get_groups_count = tac5x1x_pin_get_groups_count,
+ .get_group_name = tac5x1x_pin_get_group_name,
+ .get_group_pins = tac5x1x_pin_get_group_pins,
+#if IS_ENABLED(CONFIG_OF)
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+ .dt_free_map = pinconf_generic_dt_free_map,
+#endif
+};
+
+static const char *const tac5x1x_pin_funcs[] = {
+ "gpio", "pdm", "irq"
+};
+
+static const char * const tac5x1x_pin_gpio_groups[] = {
+ "gpio1", "gpio2", "gpo1", "gpi1", "gpi2a"
+};
+
+static const char * const tac5x1x_pin_pdm_groups[] = {
+ "pdm_gpio1_gpio2",
+ "pdm_gpio1_gpi1",
+ "pdm_gpio1_gpi2a",
+ "pdm_gpio2_gpio1",
+ "pdm_gpio2_gpi1",
+ "pdm_gpio2_gpi2a",
+ "pdm_gpo1_gpio1",
+ "pdm_gpo1_gpio2",
+ "pdm_gpo1_gpi1",
+ "pdm_gpo1_gpi2a"
+};
+
+static const char * const tac5x1x_pin_irq_groups[] = {
+ "gpio1", "gpio2", "gpo1"
+};
+
+static const struct pinfunction tac5x1x_pin_func_groups[] = {
+ PINCTRL_PINFUNCTION("gpio", tac5x1x_pin_gpio_groups,
+ ARRAY_SIZE(tac5x1x_pin_gpio_groups)),
+ PINCTRL_PINFUNCTION("pdm", tac5x1x_pin_pdm_groups,
+ ARRAY_SIZE(tac5x1x_pin_pdm_groups)),
+ PINCTRL_PINFUNCTION("irq", tac5x1x_pin_irq_groups,
+ ARRAY_SIZE(tac5x1x_pin_irq_groups)),
+};
+
+static_assert(ARRAY_SIZE(tac5x1x_pin_funcs) == TAC5X1X_FUNC_MAX);
+static_assert(ARRAY_SIZE(tac5x1x_pin_func_groups) == TAC5X1X_FUNC_MAX);
+
+static int tac5x1x_pin_get_func_count(struct pinctrl_dev *pctldev)
+{
+ return ARRAY_SIZE(tac5x1x_pin_funcs);
+}
+
+static const char *tac5x1x_pin_get_func_name(struct pinctrl_dev *pctldev,
+ unsigned int func_idx)
+{
+ return tac5x1x_pin_funcs[func_idx];
+}
+
+static int tac5x1x_pin_get_func_groups(struct pinctrl_dev *pctldev,
+ unsigned int func_idx,
+ const char * const **groups,
+ unsigned int * const num_groups)
+{
+ *groups = tac5x1x_pin_func_groups[func_idx].groups;
+ *num_groups = tac5x1x_pin_func_groups[func_idx].ngroups;
+
+ return 0;
+}
+
+static int tac5x1x_setup_pdm(struct pinctrl_dev *pinctrl, u32 group)
+{
+ int ret = 0, i;
+ u32 val;
+ size_t reg_count;
+ const struct tac5x1x_config *regdata;
+ struct tac5x1x_pin *priv;
+ const struct tac5x1x_pingroup *grp = &tac5x1x_pin_groups[group];
+
+ regdata = grp->regdata;
+ reg_count = grp->nregdata;
+ priv = pinctrl_dev_get_drvdata(pinctrl);
+
+ /*
+ * PDM pin groups use naming convention: pdm_<clk_pin>_<data_pin>
+ * First pin in the group is PDM Clock (output)
+ * Second pin in the group is PDM Data (input)
+ */
+ if (grp->npins >= 2) {
+ dev_dbg(priv->dev, "PDM config: %s CLK=%s DATA=%s\n", grp->name,
+ tac5x1x_pin_pins[grp->pins[0]].name,
+ tac5x1x_pin_pins[grp->pins[1]].name);
+ }
+
+ for (i = 0; i < reg_count; i++) {
+ val = (regdata[i].val << (ffs(regdata[i].mask) - 1)) & regdata[i].mask;
+ ret = regmap_update_bits(priv->regmap, regdata[i].reg,
+ regdata[i].mask, val);
+ if (ret) {
+ dev_err(priv->dev, "pdm setup failed, reg=%x err=%d",
+ regdata[i].reg, ret);
+ break;
+ }
+ }
+
+ /* Mark PDM as enabled in parent structure for codec driver */
+ if (!ret)
+ priv->parent->pdm_enabled = true;
+
+ return ret;
+}
+
+/* configure a gpio pin to generate HW interrupt */
+static int tac5x1x_setup_irq(struct pinctrl_dev *pinctrl, u32 group)
+{
+ struct tac5x1x_pin *priv = pinctrl_dev_get_drvdata(pinctrl);
+ const struct tac5x1x_config *const cfg = &irq_cfgs[group];
+ u32 val;
+
+ dev_dbg(priv->dev, "Configuring %s as IRQ output\n",
+ tac5x1x_pin_groups[group].name);
+ val = (cfg->val << (ffs(cfg->mask) - 1)) & cfg->mask;
+ return regmap_update_bits(priv->regmap, cfg->reg, cfg->mask, val);
+}
+
+static int tac5x1x_pin_set_mux(struct pinctrl_dev *pinctrl,
+ unsigned int func_idx, unsigned int group)
+{
+ int ret;
+ struct tac5x1x_pin *priv = pinctrl_dev_get_drvdata(pinctrl);
+
+ dev_dbg(priv->dev, "%s Setting %s(%d) to %s(%d)\n", __func__,
+ tac5x1x_pin_groups[group].name, group,
+ tac5x1x_pin_funcs[func_idx], func_idx);
+
+ switch (func_idx) {
+ case TAC5X1X_FUNC_GPIO:
+ ret = 0;
+ break;
+ case TAC5X1X_FUNC_PDM:
+ ret = tac5x1x_setup_pdm(pinctrl, group);
+ break;
+ case TAC5X1X_FUNC_IRQ:
+ ret = tac5x1x_setup_irq(pinctrl, group);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int tac5x1x_gpio_set_direction(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int offset, bool input)
+{
+ int ret;
+ u32 val;
+ struct tac5x1x_pin *priv;
+ struct device *parent;
+
+ priv = pinctrl_dev_get_drvdata(pctldev);
+ parent = priv->dev->parent;
+
+ dev_dbg(priv->dev, "Setting gpio offset=%s to %s\n",
+ tac5x1x_pin_pins[offset].name, input ? "input" : "output");
+
+ ret = pm_runtime_resume_and_get(parent);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for direction: %d\n", ret);
+ return ret;
+ }
+
+ val = (input ? TAC5X1X_GPIO_GPI : TAC5X1X_GPIO_GPO) <<
+ TAC5X1X_GPIOX_CFG_SHFT;
+
+ switch (offset) {
+ case PIN_GPIO1:
+ case PIN_GPIO2:
+ ret = regmap_update_bits(priv->regmap,
+ pin_to_reg[offset],
+ TAC5X1X_GPIOX_CFG_MASK,
+ val);
+ break;
+ case PIN_GPO1:
+ if (input) {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ ret = regmap_update_bits(priv->regmap, pin_to_reg[offset],
+ TAC5X1X_GPIOX_CFG_MASK, val);
+ break;
+ case PIN_GPI1:
+ if (!input) {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ ret = regmap_update_bits(priv->regmap, pin_to_reg[offset],
+ TAC5X1X_GPI1_EN_MASK, TAC5X1X_GPI1_EN_MASK);
+ break;
+ case PIN_GPI2A:
+ if (!input) {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ ret = regmap_update_bits(priv->regmap, pin_to_reg[offset],
+ TAC5X1X_GPI2_EN_MASK, TAC5X1X_GPI2_EN_MASK);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ if (ret)
+ dev_err(priv->dev, "Failed to set gpio%d direction: %d\n",
+ offset + 1, ret);
+
+ pm_runtime_mark_last_busy(parent);
+ pm_runtime_put_autosuspend(parent);
+
+ return ret;
+}
+
+static int tac5x1x_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int offset)
+{
+ return tac5x1x_pin_set_mux(pctldev, TAC5X1X_FUNC_GPIO, offset);
+}
+
+static void tac5x1x_gpio_disable_free(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int offset)
+{
+ struct tac5x1x_pin *priv = pinctrl_dev_get_drvdata(pctldev);
+ struct device *parent = priv->dev->parent;
+ int ret;
+
+ /* Ensure parent device is resumed before register access */
+ ret = pm_runtime_resume_and_get(parent);
+ if (ret) {
+ dev_warn(priv->dev, "PM resume failed for gpio_disable_free: %d (ignoring)\n", ret);
+ return;
+ }
+
+ regmap_update_bits(priv->regmap, pin_to_reg[offset],
+ TAC5X1X_GPIOX_CFG_MASK, 0);
+
+ pm_runtime_mark_last_busy(parent);
+ pm_runtime_put_autosuspend(parent);
+}
+
+static const struct pinmux_ops tac5x1x_pin_mux_ops = {
+ .get_functions_count = tac5x1x_pin_get_func_count,
+ .get_function_name = tac5x1x_pin_get_func_name,
+ .get_function_groups = tac5x1x_pin_get_func_groups,
+
+ .set_mux = tac5x1x_pin_set_mux,
+
+ .gpio_request_enable = tac5x1x_gpio_request_enable,
+ .gpio_disable_free = tac5x1x_gpio_disable_free,
+ .gpio_set_direction = tac5x1x_gpio_set_direction,
+
+ .strict = true,
+};
+
+static int tac5x1x_pin_config_get(struct pinctrl_dev *pctldev,
+ u32 pin, unsigned long *config)
+{
+ struct tac5x1x_pin *priv = pinctrl_dev_get_drvdata(pctldev);
+ struct device *parent = priv->dev->parent;
+ u32 param = pinconf_to_config_param(*config);
+ u32 reg, value, drive_mode;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(parent);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for config: %d\n", ret);
+ return ret;
+ }
+
+ if (pin >= TAC5X1X_MAX_PINS) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dev_dbg(priv->dev, "Get config for %s, param=%d\n",
+ tac5x1x_pin_pins[pin].name, param);
+
+ reg = pin_to_reg[pin];
+ ret = regmap_read(priv->regmap, reg, &value);
+ if (ret) {
+ dev_err(priv->dev, "Failed to get config: %d\n", ret);
+ goto out;
+ }
+
+ drive_mode = (value & TAC5X1X_GPIOX_DRV_MASK) >> TAC5X1X_GPIOX_DRV_SHFT;
+ switch (drive_mode) {
+ case TAC5X1X_GPIO_DRV_HIZ:
+ *config = pinconf_to_config_packed(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 1);
+ break;
+ case TAC5X1X_GPIO_DRV_ALAH:
+ *config = pinconf_to_config_packed(PIN_CONFIG_DRIVE_PUSH_PULL, 1);
+ break;
+ case TAC5X1X_GPIO_DRV_ALWH:
+ *config = pinconf_to_config_packed(PIN_CONFIG_BIAS_PULL_UP, 1);
+ break;
+ case TAC5X1X_GPIO_DRV_ALHIZ:
+ *config = pinconf_to_config_packed(PIN_CONFIG_DRIVE_OPEN_DRAIN, 1);
+ break;
+ case TAC5X1X_GPIO_DRV_WLAH:
+ *config = pinconf_to_config_packed(PIN_CONFIG_BIAS_PULL_DOWN, 1);
+ break;
+ case TAC5X1X_GPIO_DRV_HIZAH:
+ *config = pinconf_to_config_packed(PIN_CONFIG_DRIVE_OPEN_SOURCE, 1);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+ ret = 0;
+out:
+ pm_runtime_mark_last_busy(parent);
+ pm_runtime_put_autosuspend(parent);
+ return ret;
+}
+
+static int tac5x1x_pin_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long *configs, unsigned int num_configs)
+{
+ struct tac5x1x_pin *priv = pinctrl_dev_get_drvdata(pctldev);
+ unsigned int val, param;
+ int ret = 0;
+
+ /*
+ * push-pull -> active low, active high
+ * pull up -> active low, weak high (on-chip pullup)
+ * pull down -> weak low, active high (on-chip pulldown)
+ * open-drain -> active low, hiz
+ * open-source -> hiz active low
+ */
+ while (num_configs) {
+ val = pinconf_to_config_argument(*configs);
+ param = pinconf_to_config_param(*configs);
+ dev_dbg(priv->dev,
+ "set config for name=%s (pin=%d), param=%u val=%u\n",
+ tac5x1x_pin_pins[pin].name, pin, param, val);
+
+ switch (param) {
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ val = TAC5X1X_GPIO_DRV_ALAH << TAC5X1X_GPIOX_DRV_SHFT;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ val = TAC5X1X_GPIO_DRV_ALWH << TAC5X1X_GPIOX_DRV_SHFT;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ val = TAC5X1X_GPIO_DRV_WLAH << TAC5X1X_GPIOX_DRV_SHFT;
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ val = TAC5X1X_GPIO_DRV_ALHIZ << TAC5X1X_GPIOX_DRV_SHFT;
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+ val = TAC5X1X_GPIO_DRV_HIZAH << TAC5X1X_GPIOX_DRV_SHFT;
+ break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ val = TAC5X1X_GPIO_DRV_HIZ << TAC5X1X_GPIOX_DRV_SHFT;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ /* input pin can't have driver strength */
+ if (pin != PIN_GPI1 && pin != PIN_GPI2A) {
+ ret = regmap_update_bits(priv->regmap, pin_to_reg[pin],
+ TAC5X1X_GPIOX_DRV_MASK, val);
+ if (ret)
+ dev_err(priv->dev,
+ "pinconfig error for %s(%x), err=%d",
+ tac5x1x_pin_pins[pin].name,
+ pin_to_reg[pin], ret);
+ }
+
+ if (ret)
+ break;
+
+ configs++;
+ num_configs--;
+ }
+
+ return ret;
+}
+
+static int tac5x1x_pin_config_group_get(struct pinctrl_dev *pctldev,
+ unsigned int selector, unsigned long *config)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < tac5x1x_pin_groups[selector].npins; ++i) {
+ ret = tac5x1x_pin_config_get(pctldev,
+ tac5x1x_pin_groups[selector].pins[i],
+ config);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int tac5x1x_pin_config_group_set(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < tac5x1x_pin_groups[selector].npins; ++i) {
+ ret = tac5x1x_pin_config_set(pctldev,
+ tac5x1x_pin_groups[selector].pins[i],
+ configs, num_configs);
+ if (ret)
+ break;
+ }
+
+ return 0;
+}
+
+static const struct pinconf_ops tac5x1x_pin_conf_ops = {
+ .is_generic = true,
+
+ .pin_config_get = tac5x1x_pin_config_get,
+ .pin_config_set = tac5x1x_pin_config_set,
+ .pin_config_group_get = tac5x1x_pin_config_group_get,
+ .pin_config_group_set = tac5x1x_pin_config_group_set,
+};
+
+static const struct pinctrl_desc tac5x1x_pin_desc = {
+ .name = "tac5x1x-pinctrl",
+ .owner = THIS_MODULE,
+
+ .pins = tac5x1x_pin_pins,
+ .npins = ARRAY_SIZE(tac5x1x_pin_pins),
+
+ .pctlops = &tac5x1x_pin_group_ops,
+ .pmxops = &tac5x1x_pin_mux_ops,
+ .confops = &tac5x1x_pin_conf_ops,
+};
+
+static int tac5x1x_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tac5x1x_pin *priv = gpiochip_get_data(chip);
+ struct device *parent = priv->dev->parent;
+ unsigned int val;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(parent);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for get: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(priv->regmap, TAC5X1X_GPIOVAL, &val);
+ if (ret) {
+ dev_err(priv->dev, "Failed to get gpio%d: %d\n", offset + 1, ret);
+ goto done_gpio_get;
+ }
+
+ switch (offset) {
+ case PIN_GPIO1:
+ case PIN_GPIO2:
+ ret = !!(val & BIT(3 - offset));
+ break;
+ case PIN_GPI1:
+ ret = !!(val & BIT(1));
+ break;
+ case PIN_GPI2A:
+ ret = !!(val & BIT(2));
+ break;
+ case PIN_GPO1:
+ default:
+ ret = -EINVAL;
+ }
+
+done_gpio_get:
+ pm_runtime_mark_last_busy(parent);
+ pm_runtime_put_autosuspend(parent);
+
+ return ret;
+}
+
+static int tac5x1x_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct tac5x1x_pin *priv = gpiochip_get_data(chip);
+ struct device *parent = priv->dev->parent;
+ u32 mask, val;
+ int ret;
+
+ dev_dbg(priv->dev, "setting %s to %d\n",
+ tac5x1x_pin_pins[offset].name, value);
+
+ ret = pm_runtime_resume_and_get(parent);
+ if (ret)
+ return ret;
+
+ switch (offset) {
+ case PIN_GPIO1:
+ case PIN_GPIO2:
+ case PIN_GPO1:
+ mask = BIT(7 - offset);
+ val = value ? mask : 0;
+ ret = regmap_update_bits(priv->regmap, TAC5X1X_GPIOVAL, mask,
+ val);
+ break;
+ case PIN_GPI1:
+ case PIN_GPI2A:
+ dev_err(priv->dev, "gpi1/2a are input only, set not supported\n");
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ pm_runtime_mark_last_busy(parent);
+ pm_runtime_put_autosuspend(parent);
+
+ return ret;
+}
+
+static int tac5x1x_gpio_direction_in(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct tac5x1x_pin *priv = gpiochip_get_data(chip);
+
+ dev_dbg(priv->dev, "setting %s to input",
+ tac5x1x_pin_pins[offset].name);
+
+ /* input is not supported in GPO1 pin */
+ if (offset == PIN_GPO1) {
+ dev_err(priv->dev, "gpo1 is output only, read is not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ return pinctrl_gpio_direction_input(chip, offset);
+}
+
+static int tac5x1x_gpio_direction_out(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ int ret;
+
+ ret = tac5x1x_gpio_set(chip, offset, value);
+ if (ret)
+ return ret;
+
+ return pinctrl_gpio_direction_output(chip, offset);
+}
+
+static int tac5x1x_gpio_add_pin_ranges(struct gpio_chip *chip)
+{
+ struct tac5x1x_pin *priv = gpiochip_get_data(chip);
+ int ret;
+
+ dev_dbg(priv->dev, "gpio add range");
+ ret = gpiochip_add_pin_range(&priv->gpio_chip, priv->gpio_chip.label,
+ 0, 0, TAC5X1X_NUM_GPIO_PINS);
+ if (ret)
+ dev_err(priv->dev, "Failed to add GPIO pin range: %d\n", ret);
+
+ return ret;
+}
+
+static int tac5x1x_pin_probe(struct platform_device *pdev)
+{
+ struct tac5x1x *tac5x1x = dev_get_drvdata(pdev->dev.parent);
+ struct tac5x1x_pin *priv;
+ struct pinctrl_dev *pctldev;
+ struct fwnode_handle *fwnode = dev_fwnode(tac5x1x->dev);
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &pdev->dev;
+ priv->regmap = tac5x1x->regmap;
+ priv->parent = tac5x1x;
+
+ priv->gpio_chip.request = gpiochip_generic_request;
+ priv->gpio_chip.free = gpiochip_generic_free;
+ priv->gpio_chip.direction_input = tac5x1x_gpio_direction_in;
+ priv->gpio_chip.direction_output = tac5x1x_gpio_direction_out;
+ priv->gpio_chip.add_pin_ranges = tac5x1x_gpio_add_pin_ranges;
+ priv->gpio_chip.get = tac5x1x_gpio_get;
+ priv->gpio_chip.set = tac5x1x_gpio_set;
+ priv->gpio_chip.label = dev_name(priv->dev);
+ priv->gpio_chip.parent = priv->dev;
+ priv->gpio_chip.can_sleep = true;
+ priv->gpio_chip.base = -1; /* no base*/
+ priv->gpio_chip.ngpio = TAC5X1X_NUM_GPIO_PINS;
+
+ if (is_of_node(fwnode)) {
+ fwnode = fwnode_get_named_child_node(fwnode, "pinctrl");
+
+ if (fwnode && !fwnode->dev)
+ fwnode->dev = priv->dev;
+ }
+
+ priv->gpio_chip.fwnode = fwnode;
+
+ device_set_node(priv->dev, fwnode);
+
+ pctldev = devm_pinctrl_register(priv->dev, &tac5x1x_pin_desc, priv);
+ if (IS_ERR(pctldev))
+ return dev_err_probe(priv->dev, PTR_ERR(pctldev),
+ "Failed to register pinctrl\n");
+
+ ret = devm_gpiochip_add_data(priv->dev, &priv->gpio_chip, priv);
+ if (ret)
+ return dev_err_probe(priv->dev, ret,
+ "Failed to register gpiochip\n");
+
+ return 0;
+}
+
+static const struct platform_device_id tac5x1x_pin_id_table[] = {
+ { "tac5x1x-pinctrl", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, tac5x1x_pin_id_table);
+
+static struct platform_driver tac5x1x_pin_driver = {
+ .driver = {
+ .name = "tac5x1x-pinctrl",
+ },
+ .probe = tac5x1x_pin_probe,
+ .id_table = tac5x1x_pin_id_table,
+};
+module_platform_driver(tac5x1x_pin_driver);
+
+MODULE_DESCRIPTION("TAC5X1X Pinctrl Driver");
+MODULE_AUTHOR("Niranjan H Y <niranjan.hy@ti.com>");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v1 7/8] ASoC: tac5x1x: Add TI TAC5x1x codec driver
2026-03-12 18:48 [PATCH v1 0/8] ASoC: support TI's TAC5x1x audio codec family Niranjan H Y
` (5 preceding siblings ...)
2026-03-12 18:48 ` [PATCH v1 6/8] pinctrl: pinctrl-tac5x1x: Add TI TAC5x1x pinctrl driver Niranjan H Y
@ 2026-03-12 18:48 ` Niranjan H Y
2026-03-12 19:56 ` Mark Brown
2026-03-12 18:48 ` [PATCH v1 8/8] ASoC: pcm6240: remove support for taac5x1x family Niranjan H Y
7 siblings, 1 reply; 18+ messages in thread
From: Niranjan H Y @ 2026-03-12 18:48 UTC (permalink / raw)
To: linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, niranjan.hy, nb, navada, v-hampiholi, sandeepk,
baojun.xu, shenghao-ding
Add the Texas Instruments TAC5x1x ALSA SoC codec driver. This driver
provides audio capture and playback functionality for the TAC5x1x family
of audio codecs.
The codec driver implements:
- ADC for audio capture
- DAC for audio playback
- PDM input support
- DAPM with optimized power gating
- Multiple sample rates and audio formats
- Device-specific routing for TAC/TAA/TAD variants
- MICBIAS and reference voltage control
- Mixer controls for volume, source selection, and configuration
Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
---
sound/soc/codecs/Kconfig | 11 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tac5x1x.c | 2082 ++++++++++++++++++++++++++++++++++++
sound/soc/codecs/tac5x1x.h | 35 +
4 files changed, 2130 insertions(+)
create mode 100644 sound/soc/codecs/tac5x1x.c
create mode 100644 sound/soc/codecs/tac5x1x.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index adb3fb923be3..cdfbd9105bb5 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -264,6 +264,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_STA529
imply SND_SOC_STAC9766
imply SND_SOC_STI_SAS
+ imply SND_SOC_TAC5X1X
imply SND_SOC_TAS2552
imply SND_SOC_TAS2562
imply SND_SOC_TAS2764
@@ -2130,6 +2131,16 @@ config SND_SOC_STAC9766
config SND_SOC_STI_SAS
tristate "codec Audio support for STI SAS codec"
+config SND_SOC_TAC5X1X
+ tristate "Texas Instruments TAC5X1X family driver based on I2C"
+ depends on MFD_TAC5X1X
+ help
+ Enable support for Texas Instruments TAC5X1X family Audio chips.
+ The family consists mono/stereo audio codecs, DACs and ADCs.
+ Includes support for TAC5311-Q1, TAC5411-Q1, TAC5111, TAC5211,
+ TAA5212, TAA5412-Q1, TAD5112, TAD5212, TAC5312, TAC5412-Q1,
+ TAC5112, TAC5212, TAC5301
+
config SND_SOC_TAS2552
tristate "Texas Instruments TAS2552 Mono Audio amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 3ddee5298721..64c99cb61cd1 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -313,6 +313,7 @@ snd-soc-sta350-y := sta350.o
snd-soc-sta529-y := sta529.o
snd-soc-stac9766-y := stac9766.o
snd-soc-sti-sas-y := sti-sas.o
+snd-soc-tac5x1x-y := tac5x1x.o
snd-soc-tas5086-y := tas5086.o
snd-soc-tas571x-y := tas571x.o
snd-soc-tas5720-y := tas5720.o
@@ -745,6 +746,7 @@ obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o
obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
+obj-$(CONFIG_SND_SOC_TAC5X1X) += snd-soc-tac5x1x.o
obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o
obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o
diff --git a/sound/soc/codecs/tac5x1x.c b/sound/soc/codecs/tac5x1x.c
new file mode 100644
index 000000000000..f3ce21464f47
--- /dev/null
+++ b/sound/soc/codecs/tac5x1x.c
@@ -0,0 +1,2082 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tac5x1x.c -- Codec driver for TAC5x1x
+ *
+ * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Author: Niranjan H Y <niranjan.hy@ti.com>
+ */
+#include <linux/types.h>
+#include <linux/of_irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/i2c.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <linux/mfd/tac5x1x/registers.h>
+#include "tac5x1x.h"
+
+struct mask_to_txt {
+ u8 mask;
+ const char *const name;
+};
+
+struct interrupt_info {
+ u32 reg;
+ u32 count;
+ const struct mask_to_txt *mask_str_map;
+};
+
+#define TAC5X1X_EVENT(bit, evt_txt) ( \
+ (struct mask_to_txt){ \
+ .mask = BIT((bit)), \
+ .name = evt_txt})
+
+static const struct mask_to_txt int_chx_latch[] = {
+ TAC5X1X_EVENT(7, "Input Channel1 fault"),
+ TAC5X1X_EVENT(6, "Input Channel2 fault"),
+ TAC5X1X_EVENT(5, "Output Channel1 fault"),
+ TAC5X1X_EVENT(4, "Output Channel2 fault"),
+ TAC5X1X_EVENT(3, "Short to VBAT_IN"),
+};
+
+static const struct mask_to_txt in_ch1_latch[] = {
+ TAC5X1X_EVENT(7, "IN_CH1 open Input"),
+ TAC5X1X_EVENT(6, "IN_CH1 Input shorted"),
+ TAC5X1X_EVENT(5, "IN_CH1 INP shorted to GND"),
+ TAC5X1X_EVENT(4, "IN_CH1 INM shorted to GND"),
+ TAC5X1X_EVENT(3, "IN_CH1 INP shorted to MICBIAS"),
+ TAC5X1X_EVENT(2, "IN_CH1 INM shorted to MICBIAS"),
+ TAC5X1X_EVENT(1, "IN_CH1 INP shorted to VBAT_IN"),
+ TAC5X1X_EVENT(0, "IN_CH1 INM shorted to VBAT_IN"),
+};
+
+static const struct mask_to_txt in_ch2_latch[] = {
+ TAC5X1X_EVENT(7, "IN_CH2 open Input"),
+ TAC5X1X_EVENT(6, "IN_CH2 Input shorted"),
+ TAC5X1X_EVENT(5, "IN_CH2 INP shorted to GND"),
+ TAC5X1X_EVENT(4, "IN_CH2 INM shorted to GND"),
+ TAC5X1X_EVENT(3, "IN_CH2 INP shorted to MICBIAS"),
+ TAC5X1X_EVENT(2, "IN_CH2 INM shorted to MICBIAS"),
+ TAC5X1X_EVENT(1, "IN_CH2 INP shorted to VBAT_IN"),
+ TAC5X1X_EVENT(0, "IN_CH2 INM shorted to VBAT_IN"),
+};
+
+static const struct mask_to_txt out_ch1_latch[] = {
+ TAC5X1X_EVENT(7, "OUT_CH1 OUT1P Short circuit Fault"),
+ TAC5X1X_EVENT(6, "OUT_CH1 OUT1M Short circuit Fault"),
+ TAC5X1X_EVENT(5, "OUT_CH1 DRVRP Virtual Ground Fault"),
+ TAC5X1X_EVENT(4, "OUT_CH1 DRVRM Virtual ground Fault"),
+ /* masks */
+ TAC5X1X_EVENT(3, "OUT_CH1 ADC CH1 Mask"),
+ TAC5X1X_EVENT(2, "OUT_CH1 ADC CH2 MASK"),
+};
+
+static const struct mask_to_txt out_ch2_latch[] = {
+ TAC5X1X_EVENT(7, "OUT_CH2 OUT2P Short circuit Fault"),
+ TAC5X1X_EVENT(6, "OUT_CH2 OUT2M Short circuit Fault"),
+ TAC5X1X_EVENT(5, "OUT_CH2 DRVRP Virtual Ground Fault"),
+ TAC5X1X_EVENT(4, "OUT_CH2 DRVRM Virtual ground Fault"),
+ /* mask */
+ TAC5X1X_EVENT(1, "AREG SC Fault Mask"),
+ TAC5X1X_EVENT(0, "AREG SC Fault"),
+};
+
+static const struct mask_to_txt int_latch1[] = {
+ TAC5X1X_EVENT(7, "CH1 INP Over Voltage"),
+ TAC5X1X_EVENT(6, "CH1 INM Over Voltage"),
+ TAC5X1X_EVENT(5, "CH2 INP over Voltage"),
+ TAC5X1X_EVENT(4, "CH2 INM Over Voltage"),
+ TAC5X1X_EVENT(3, "Headset Insert Detection"),
+ TAC5X1X_EVENT(2, "Headset Remove Detection"),
+ TAC5X1X_EVENT(1, "Headset Hook"),
+ TAC5X1X_EVENT(0, "MIPS Overload"),
+};
+
+static const struct mask_to_txt int_latch2[] = {
+ TAC5X1X_EVENT(7, "GPA Up threashold Fault"),
+ TAC5X1X_EVENT(6, "GPA low threashold Fault"),
+ TAC5X1X_EVENT(5, "VAD Power up detect"),
+ TAC5X1X_EVENT(4, "VAD power down detect"),
+ TAC5X1X_EVENT(3, "Micbias short circuit"),
+ TAC5X1X_EVENT(2, "Micbias high current fault"),
+ TAC5X1X_EVENT(1, "Micbias low current fault"),
+ TAC5X1X_EVENT(0, "Micbias Over voltage fault"),
+};
+
+static const struct mask_to_txt int_latch_0[] = {
+ TAC5X1X_EVENT(7, "Clock Error"),
+ TAC5X1X_EVENT(6, "PLL Lock"),
+ TAC5X1X_EVENT(5, "Boost Over Temperature"),
+ TAC5X1X_EVENT(4, "Boost Over Current"),
+ TAC5X1X_EVENT(3, "Boost MO"),
+};
+
+#define LTCH_TO_MASK_STR_MAP(latch_reg, str_map, map_size) ( \
+ (struct interrupt_info){ \
+ .reg = (latch_reg), \
+ .count = (map_size), \
+ .mask_str_map = (str_map), \
+})
+
+static const struct interrupt_info intr_info_list[] = {
+ LTCH_TO_MASK_STR_MAP(TAC5X1X_REG_CHX_LTCH, int_chx_latch,
+ ARRAY_SIZE(int_chx_latch)),
+ LTCH_TO_MASK_STR_MAP(TAC5X1X_REG_IN_CH1_LTCH, in_ch1_latch,
+ ARRAY_SIZE(in_ch1_latch)),
+ LTCH_TO_MASK_STR_MAP(TAC5X1X_REG_IN_CH2_LTCH, in_ch2_latch,
+ ARRAY_SIZE(in_ch2_latch)),
+ LTCH_TO_MASK_STR_MAP(TAC5X1X_REG_OUT_CH1_LTCH, out_ch1_latch,
+ ARRAY_SIZE(out_ch1_latch)),
+ LTCH_TO_MASK_STR_MAP(TAC5X1X_REG_OUT_CH2_LTCH, out_ch2_latch,
+ ARRAY_SIZE(out_ch2_latch)),
+ LTCH_TO_MASK_STR_MAP(TAC5X1X_REG_INT_LTCH1, int_latch1,
+ ARRAY_SIZE(int_latch1)),
+ LTCH_TO_MASK_STR_MAP(TAC5X1X_REG_INT_LTCH2, int_latch2,
+ ARRAY_SIZE(int_latch2)),
+ /* This should be the last entry */
+ LTCH_TO_MASK_STR_MAP(TAC5X1X_REG_INT_LTCH0, int_latch_0,
+ ARRAY_SIZE(int_latch_0)),
+};
+
+static void process_one_interrupt(struct tac5x1x_priv *tac5x1x, s32 index,
+ s32 value)
+{
+ u32 map_count, i;
+ const struct mask_to_txt *map_items;
+ struct device *dev = tac5x1x->tac5x1x->dev;
+
+ map_count = intr_info_list[index].count;
+ map_items = intr_info_list[index].mask_str_map;
+
+ for (i = 0; i < map_count; i++) {
+ if (value & map_items[i].mask)
+ dev_dbg(dev, "Interrupt %s detected\n",
+ map_items[i].name);
+ }
+}
+
+static irqreturn_t irq_thread_func(s32 irq, void *dev_id)
+{
+ u8 latch_set = 0;
+ u32 latch_count;
+ s32 i, ret;
+ struct tac5x1x_priv *tac5x1x = (struct tac5x1x_priv *)dev_id;
+ struct device *dev = tac5x1x->tac5x1x->dev;
+
+ latch_count = ARRAY_SIZE(intr_info_list);
+
+ ret = regmap_multi_reg_read(tac5x1x->tac5x1x->regmap,
+ tac5x1x->irqinfo.latch_regs,
+ tac5x1x->irqinfo.latch_data, latch_count);
+ if (ret) {
+ dev_err(dev,
+ "interrupt: latch register read failed");
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < latch_count; i++) {
+ dev_dbg(dev, "reg=0x%0x, val=0x%02x",
+ tac5x1x->irqinfo.latch_regs[i],
+ tac5x1x->irqinfo.latch_data[i]);
+ latch_set |= tac5x1x->irqinfo.latch_data[i] & 0xff;
+ }
+
+ if (!latch_set)
+ return IRQ_NONE;
+
+ for (i = 0; i < latch_count; i++) {
+ if (!tac5x1x->irqinfo.latch_data[i])
+ continue;
+ process_one_interrupt(tac5x1x, i,
+ tac5x1x->irqinfo.latch_data[i]);
+ tac5x1x->irqinfo.latch_data[i] = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static s32 tac5x1x_register_interrupt(struct tac5x1x_priv *tac5x1x)
+{
+ struct device_node *np;
+ s32 ret, latch_count, i;
+ u32 *latch_regs;
+ u8 *latch_data;
+ struct device *dev;
+
+ dev = tac5x1x->tac5x1x->dev;
+ np = dev->of_node;
+ latch_count = ARRAY_SIZE(intr_info_list);
+
+ tac5x1x->irqinfo.irq = of_irq_get(np, 0);
+ if (tac5x1x->irqinfo.irq < 0) {
+ dev_dbg(dev, "No IRQ configured, running without interrupts\n");
+ return 0; /* Not an error - interrupts are optional */
+ }
+
+ latch_regs = devm_kzalloc(dev, latch_count * sizeof(u32),
+ GFP_KERNEL);
+ latch_data = devm_kzalloc(dev, latch_count * sizeof(u8),
+ GFP_KERNEL);
+ if (!latch_data || !latch_regs)
+ return -ENOMEM;
+
+ for (i = 0; i < latch_count; i++)
+ latch_regs[i] = intr_info_list[i].reg;
+
+ tac5x1x->irqinfo.latch_regs = latch_regs;
+ tac5x1x->irqinfo.latch_data = latch_data;
+ /* Clear any pending interrupts before enabling */
+ regmap_write(tac5x1x->tac5x1x->regmap, TAC5X1X_INT, 0x11);
+
+ ret = devm_request_threaded_irq(dev, tac5x1x->irqinfo.irq,
+ NULL, irq_thread_func,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "tac5x1x-irq", tac5x1x);
+ if (ret)
+ dev_err(dev, "Failed to request IRQ %d: %d\n",
+ tac5x1x->irqinfo.irq, ret);
+
+ return ret;
+}
+
+#define IS_DAC_CH(ch) ((ch) & 0x0f)
+#define IS_ADC_CH(ch) ((ch) & 0xf0)
+
+/* Delay in ms before adjusting power state after last channel change */
+#define TAC5X1X_POWER_SETTLE_MS 100
+
+/*
+ * Delayed work function that handles power state adjustments.
+ *
+ * This function is scheduled after any channel enable/disable event.
+ * It waits for the hardware to settle, then adjusts power state based
+ * on which channels are currently enabled. Using mod_delayed_work()
+ * ensures the timer is reset on each new event.
+ */
+static void post_powerup_work(struct work_struct *work)
+{
+ u8 mask;
+ u8 pwr_cfg;
+ u32 input_mode = 0;
+ bool ch1_is_pdm, ch2_is_pdm;
+ struct tac5x1x_priv *tac5x1x =
+ container_of(work, struct tac5x1x_priv, powerup_work.work);
+ struct snd_soc_component *component = tac5x1x->component;
+
+ dev_dbg(component->dev, "POST_POWERUP_WORK ch_enabled=0x%02x\n",
+ tac5x1x->ch_enabled);
+
+ mutex_lock(&tac5x1x->ch_lock);
+
+ /* Clear the power-up flag so next power event can write PWR_CFG */
+ tac5x1x->pwr_up_done = false;
+
+ /* Write the current channel enable state */
+ snd_soc_component_write(component, TAC5X1X_CH_EN, tac5x1x->ch_enabled);
+
+ /* Check if PDM mode is selected via INTF4 register */
+ regmap_read(tac5x1x->tac5x1x->regmap, TAC5X1X_INTF4, &input_mode);
+ ch1_is_pdm = !!(input_mode & BIT(7));
+ ch2_is_pdm = !!(input_mode & BIT(6));
+
+ dev_dbg(component->dev, "INTF4=0x%02x ch1_pdm=%d ch2_pdm=%d\n",
+ input_mode, ch1_is_pdm, ch2_is_pdm);
+
+ mask = TAC5X1X_PWR_CFG_ADC_PDZ | TAC5X1X_PWR_CFG_MICBIAS |
+ TAC5X1X_PWR_CFG_DAC_PDZ;
+ pwr_cfg = mask;
+
+ if (IS_DAC_CH(tac5x1x->ch_enabled) == 0)
+ pwr_cfg &= ~TAC5X1X_PWR_CFG_DAC_PDZ;
+
+ /*
+ * Only disable MICBIAS if ALL enabled ADC channels are in PDM mode
+ */
+ bool ch1_enabled = !!(tac5x1x->ch_enabled &
+ (TAC5X1X_CH_EN_ADC_CH1 | TAC5X1X_CH_EN_DAC_CH1));
+ bool ch2_enabled = !!(tac5x1x->ch_enabled &
+ (TAC5X1X_CH_EN_ADC_CH2 | TAC5X1X_CH_EN_DAC_CH2));
+ bool need_micbias = false;
+
+ if ((tac5x1x->ch_enabled & TAC5X1X_CH_EN_ADC_CH1) && !ch1_is_pdm)
+ need_micbias = true;
+ if ((tac5x1x->ch_enabled & TAC5X1X_CH_EN_ADC_CH2) && !ch2_is_pdm)
+ need_micbias = true;
+
+ if (!need_micbias && ((ch1_is_pdm && ch1_enabled) || (ch2_is_pdm && ch2_enabled))) {
+ /* PDM mode for enabled channels - no MICBIAS needed */
+ pwr_cfg &= ~TAC5X1X_PWR_CFG_MICBIAS;
+ }
+ if (IS_ADC_CH(tac5x1x->ch_enabled) == 0) {
+ /* No ADC channels enabled - disable ADC power and MICBIAS */
+ pwr_cfg &= ~TAC5X1X_PWR_CFG_ADC_PDZ;
+ pwr_cfg &= ~TAC5X1X_PWR_CFG_MICBIAS;
+ }
+
+ dev_dbg(component->dev, "PWR_CFG mask=0x%02x val=0x%02x\n", mask, pwr_cfg);
+ snd_soc_component_update_bits(component, TAC5X1X_PWR_CFG, mask, pwr_cfg);
+ mutex_unlock(&tac5x1x->ch_lock);
+}
+
+static int tac5x1x_enable_channel_unlocked(struct snd_soc_component *comp,
+ bool is_adc, s32 right)
+{
+ s32 ret;
+ u8 mask_dev;
+ u8 mask;
+ struct tac5x1x_priv *tac5x1x;
+
+ tac5x1x = snd_soc_component_get_drvdata(comp);
+ if (right) {
+ mask_dev = TAC5X1X_CH_EN_ADC_CH2 | TAC5X1X_CH_EN_DAC_CH2;
+ mask = is_adc ? TAC5X1X_CH_EN_ADC_CH2 : TAC5X1X_CH_EN_DAC_CH2;
+ } else {
+ mask_dev = TAC5X1X_CH_EN_ADC_CH1 | TAC5X1X_CH_EN_DAC_CH1;
+ mask = is_adc ? TAC5X1X_CH_EN_ADC_CH1 : TAC5X1X_CH_EN_DAC_CH1;
+ }
+ tac5x1x->ch_enabled |= mask;
+ ret = snd_soc_component_update_bits(comp, TAC5X1X_CH_EN,
+ mask_dev, mask_dev);
+
+ return ret;
+}
+
+static int tac5x1x_disable_channel_unlocked(struct snd_soc_component *comp,
+ bool is_adc, s32 right)
+{
+ u8 mask;
+ s32 ret;
+ struct tac5x1x_priv *tac5x1x;
+
+ tac5x1x = snd_soc_component_get_drvdata(comp);
+ mask = is_adc ?
+ (right ? TAC5X1X_CH_EN_ADC_CH2 : TAC5X1X_CH_EN_ADC_CH1) :
+ (right ? TAC5X1X_CH_EN_DAC_CH2 : TAC5X1X_CH_EN_DAC_CH1);
+ tac5x1x->ch_enabled &= ~mask;
+ ret = snd_soc_component_update_bits(comp, TAC5X1X_CH_EN, mask, 0);
+
+ return ret;
+}
+
+/*
+ * When ADC and DAC are enabled with time delay between them
+ * the one which is started latter doesn't work because of HW bug.
+ * So DAC and ADC events with delayed work is added to follow a
+ * particular powerup sequence.
+ *
+ * Power-up: Immediately power up both ADC and DAC blocks together.
+ * Power-down: Defer to delayed work to avoid races with quick on/off cycles.
+ *
+ * Using mod_delayed_work() ensures the timer is reset on each event,
+ * so rapid on/off cycles are handled correctly.
+ */
+static int tac5x1x_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ u8 pwr_cfg;
+ int right = w->shift;
+ int ret = 0;
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct tac5x1x_priv *tac5x1x = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "DAC_EVENT event=%d right=%d\n", event, right);
+
+ mutex_lock(&tac5x1x->ch_lock);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = tac5x1x_enable_channel_unlocked(component, false, right);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to update enable DAC channels\n");
+ break;
+ }
+
+ /* Power up both ADC and DAC together due to HW bug */
+ if (!tac5x1x->pwr_up_done) {
+ pwr_cfg = TAC5X1X_PWR_CFG_DAC_PDZ |
+ TAC5X1X_PWR_CFG_ADC_PDZ |
+ TAC5X1X_PWR_CFG_MICBIAS;
+ ret = snd_soc_component_write(component,
+ TAC5X1X_PWR_CFG,
+ pwr_cfg);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to power up DAC\n");
+ tac5x1x_disable_channel_unlocked(component,
+ false, right);
+ /* Mark done even on error to prevent retry loop */
+ tac5x1x->pwr_up_done = true;
+ break;
+ }
+ tac5x1x->pwr_up_done = true;
+ }
+ mod_delayed_work(system_wq, &tac5x1x->powerup_work,
+ msecs_to_jiffies(TAC5X1X_POWER_SETTLE_MS));
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ tac5x1x_disable_channel_unlocked(component, false, right);
+ tac5x1x->pwr_up_done = false;
+ /*
+ * Don't power down immediately - schedule delayed work to
+ * handle power state. This avoids races with quick on/off.
+ */
+ mod_delayed_work(system_wq, &tac5x1x->powerup_work,
+ msecs_to_jiffies(TAC5X1X_POWER_SETTLE_MS));
+ break;
+ }
+ mutex_unlock(&tac5x1x->ch_lock);
+
+ return ret;
+}
+
+static int tac5x1x_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ u8 pwr_cfg;
+ u32 right = w->shift;
+ s32 ret = 0;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct tac5x1x_priv *tac5x1x = snd_soc_component_get_drvdata(c);
+
+ dev_dbg(c->dev, "ADC_EVENT event=%d right=%d\n", event, right);
+
+ mutex_lock(&tac5x1x->ch_lock);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = tac5x1x_enable_channel_unlocked(c, true, right);
+ if (ret < 0) {
+ dev_err(c->dev,
+ "Failed to enable ADC/PDM channel\n");
+ break;
+ }
+
+ /* Power up ADC, MICBIAS, and DAC together */
+ /* Only write if not already done by a previous event */
+ if (!tac5x1x->pwr_up_done) {
+ pwr_cfg = TAC5X1X_PWR_CFG_ADC_PDZ |
+ TAC5X1X_PWR_CFG_MICBIAS |
+ TAC5X1X_PWR_CFG_DAC_PDZ;
+ ret = snd_soc_component_write(c, TAC5X1X_PWR_CFG, pwr_cfg);
+ if (ret) {
+ dev_err(c->dev, "Failed to power up\n");
+ tac5x1x_disable_channel_unlocked(c, true, right);
+ /* Mark done even on error to prevent retry loop */
+ tac5x1x->pwr_up_done = true;
+ break;
+ }
+ tac5x1x->pwr_up_done = true;
+ }
+
+ mod_delayed_work(system_wq, &tac5x1x->powerup_work,
+ msecs_to_jiffies(TAC5X1X_POWER_SETTLE_MS));
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ tac5x1x_disable_channel_unlocked(c, true, right);
+ /* Clear power-up flag when disabling channels */
+ tac5x1x->pwr_up_done = false;
+ /* Power down ADC if no ADC channels active */
+ if (IS_ADC_CH(tac5x1x->ch_enabled) == 0)
+ snd_soc_component_update_bits(c, TAC5X1X_PWR_CFG,
+ TAC5X1X_PWR_CFG_ADC_PDZ |
+ TAC5X1X_PWR_CFG_MICBIAS,
+ 0);
+ break;
+ }
+ mutex_unlock(&tac5x1x->ch_lock);
+
+ return ret;
+}
+
+/*
+ * ADC full-scale selection
+ * 2/10-VRMS is for TAX52xx/TAX51xx devices
+ * 4/5-VRMS is for TAX54xx/TAX53xx devices
+ */
+static const char *const tac5x1x_adc_fscale_text[] = {"2/10-VRMS",
+ "4/5-VRMS"};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc1_fscale_enum, TAC5X1X_ADCCH1C0, 1,
+ tac5x1x_adc_fscale_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc2_fscale_enum, TAC5X1X_ADCCH2C0, 1,
+ tac5x1x_adc_fscale_text);
+
+static const struct snd_kcontrol_new tac5x1x_dapm_adc1_fscale_control[] = {
+ SOC_DAPM_ENUM("ADC1 FSCALE MUX", tac5x1x_adc1_fscale_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_dapm_adc2_fscale_control[] = {
+ SOC_DAPM_ENUM("ADC2 FSCALE MUX", tac5x1x_adc2_fscale_enum),
+};
+
+static const char *const pdmclk_text[] = {
+ "2.8224 MHz or 3.072 MHz", "1.4112 MHz or 1.536 MHz",
+ "705.6 kHz or 768 kHz", "5.6448 MHz or 6.144 MHz"};
+
+static SOC_ENUM_SINGLE_DECL(pdmclk_select_enum, TAC5X1X_CNTCLK0, 6,
+ pdmclk_text);
+
+/* Digital Volume control. From -80 to 47 dB in 0.5 dB steps */
+static DECLARE_TLV_DB_SCALE(record_dig_vol_tlv, -8000, 50, 0);
+
+/* Gain Calibration control. From -0.8db to 0.7db dB in 0.1 dB steps */
+static DECLARE_TLV_DB_MINMAX(record_gain_cali_tlv, -80, 70);
+
+/* Analog Level control. From -12 to 24 dB in 6 dB steps */
+static DECLARE_TLV_DB_SCALE(playback_analog_level_tlv, -1200, 600, 0);
+
+/* Digital Volume control. From -100 to 27 dB in 0.5 dB steps */
+static DECLARE_TLV_DB_SCALE(dac_dig_vol_tlv, -10000, 50, 0); // mute ?
+
+/* Gain Calibration control. From -0.8db to 0.7db dB in 0.1 dB steps */
+static DECLARE_TLV_DB_MINMAX(playback_gain_cali_tlv, -80, 70);
+
+/* Output Source Selection */
+static const char *const tac5x1x_output_source_text[] = {
+ "Disabled",
+ "DAC Input",
+ "Analog Bypass",
+ "DAC + Analog Bypass Mix",
+ "DAC -> OUTxP, INxP -> OUTxM",
+ "INxM -> OUTxP, DAC -> OUTxM",
+};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_out1_source_enum, TAC5X1X_OUT1CFG0, 5,
+ tac5x1x_output_source_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_out2_source_enum, TAC5X1X_OUT2CFG0, 5,
+ tac5x1x_output_source_text);
+
+static const struct snd_kcontrol_new tac5x1x_dapm_out1_source_control[] = {
+ SOC_DAPM_ENUM("OUT1X MUX", tac5x1x_out1_source_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_dapm_out2_source_control[] = {
+ SOC_DAPM_ENUM("OUT2X MUX", tac5x1x_out2_source_enum),
+};
+
+/* Output Config Selection */
+static const char *const tac5x1x_output_config_text[] = {
+ "Differential",
+ "Stereo Single-ended",
+ "Mono Single-ended at OUTxP only",
+ "Mono Single-ended at OUTxM only",
+ "Pseudo differential with OUTxM as VCOM",
+ "Pseudo differential with OUTxM as external sensing",
+ "Pseudo differential with OUTxP as VCOM",
+};
+
+static const char *const tac5x1x_output2_config_text[] = {
+ "Differential",
+ "Stereo Single-ended",
+ "Mono Single-ended at OUTxP only",
+ "Mono Single-ended at OUTxM only",
+ "Pseudo differential with OUTxM as VCOM",
+ "Pseudo differential with OUTxP as VCOM",
+};
+
+static const s32 tac5x1x_output2_config_values[] = {
+ 0, 1, 2, 3, 4, 6
+};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_out1_config_enum, TAC5X1X_OUT1CFG0, 2,
+ tac5x1x_output_config_text);
+static SOC_VALUE_ENUM_SINGLE_DECL(tac5x1x_out2_config_enum,
+ TAC5X1X_OUT2CFG0, 2, 0x7,
+ tac5x1x_output2_config_text,
+ tac5x1x_output2_config_values);
+
+static const struct snd_kcontrol_new tac5x1x_dapm_out1_config_control[] = {
+ SOC_DAPM_ENUM("OUT1X Config MUX", tac5x1x_out1_config_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_dapm_out2_config_control[] = {
+ SOC_DAPM_ENUM("OUT2X Config MUX", tac5x1x_out2_config_enum),
+};
+
+static const char *const tac5x1x_wideband_text[] = {
+ "Audio BW 24-kHz",
+ "Wide BW 96-kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc1_wideband_enum, TAC5X1X_ADCCH1C0, 0,
+ tac5x1x_wideband_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc2_wideband_enum, TAC5X1X_ADCCH2C0, 0,
+ tac5x1x_wideband_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_dac1_wideband_enum, TAC5X1X_OUT1CFG1, 0,
+ tac5x1x_wideband_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_dac2_wideband_enum, TAC5X1X_OUT2CFG1, 0,
+ tac5x1x_wideband_text);
+
+static const char *const tac5x1x_tolerance_text[] = {
+ "AC Coupled with 100mVpp",
+ "AC/DC Coupled with 1Vpp",
+ "AC/DC Coupled with Rail-to-rail",
+};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc1_tolerance_enum, TAC5X1X_ADCCH1C0, 2,
+ tac5x1x_tolerance_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc2_tolerance_enum, TAC5X1X_ADCCH2C0, 2,
+ tac5x1x_tolerance_text);
+
+/* Output Drive Selection */
+static const char *const tac5x1x_output_driver_text[] = {
+ "Line-out",
+ "Headphone",
+ "4 ohm",
+ "FD Receiver/Debug",
+};
+
+static SOC_ENUM_SINGLE_DECL(out1p_driver_enum, TAC5X1X_OUT1CFG1, 6,
+ tac5x1x_output_driver_text);
+
+static SOC_ENUM_SINGLE_DECL(out2p_driver_enum, TAC5X1X_OUT2CFG1, 6,
+ tac5x1x_output_driver_text);
+
+static const struct snd_kcontrol_new tac5x1x_dapm_out1_driver_control[] = {
+ SOC_DAPM_ENUM("OUT1 driver MUX", out1p_driver_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_dapm_out2_driver_control[] = {
+ SOC_DAPM_ENUM("OUT2 driver MUX", out2p_driver_enum),
+};
+
+/* Decimation Filter Selection */
+static const char *const decimation_filter_text[] = {
+ "Linear Phase", "Low Latency", "Ultra-low Latency"};
+
+static SOC_ENUM_SINGLE_DECL(decimation_filter_record_enum, TAC5X1X_DSP0, 6,
+ decimation_filter_text);
+static SOC_ENUM_SINGLE_DECL(decimation_filter_playback_enum, TAC5X1X_DSP1, 6,
+ decimation_filter_text);
+
+static const struct snd_kcontrol_new tx_ch1_asi_switch =
+ SOC_DAPM_SINGLE("Capture Switch", TAC5X1X_PASITXCH1, 5, 1, 0);
+static const struct snd_kcontrol_new tx_ch2_asi_switch =
+ SOC_DAPM_SINGLE("Capture Switch", TAC5X1X_PASITXCH2, 5, 1, 0);
+static const struct snd_kcontrol_new tx_ch3_asi_switch =
+ SOC_DAPM_SINGLE("Capture Switch", TAC5X1X_PASITXCH3, 5, 1, 0);
+static const struct snd_kcontrol_new tx_ch4_asi_switch =
+ SOC_DAPM_SINGLE("Capture Switch", TAC5X1X_PASITXCH4, 5, 1, 0);
+
+static const struct snd_kcontrol_new rx_ch1_asi_switch =
+ SOC_DAPM_SINGLE("Switch", TAC5X1X_PASIRXCH1, 5, 1, 0);
+static const struct snd_kcontrol_new rx_ch2_asi_switch =
+ SOC_DAPM_SINGLE("Switch", TAC5X1X_PASIRXCH2, 5, 1, 0);
+
+static const char *const rx_ch5_asi_cfg_text[] = {
+ "Disable",
+ "DAC channel data",
+ "ADC channel output loopback",
+};
+
+static const char *const rx_ch6_asi_cfg_text[] = {
+ "Disable",
+ "DAC channel data",
+ "ADC channel output loopback",
+ "Channel Input to ICLA device",
+};
+
+static const char *const tx_ch5_asi_cfg_text[] = {
+ "Tristate",
+ "Input Channel Loopback data",
+ "Echo reference Channel data",
+};
+
+static const char *const tx_ch7_asi_cfg_text[] = {
+ "Tristate",
+ "Vbat_Wlby2,Temp_Wlby2",
+ "echo_ref_ch1,echo_ref_ch2",
+};
+
+static const char *const tx_ch8_asi_cfg_text[] = {
+ "Tristate",
+ "ICLA data",
+};
+
+static const char *const diag_cfg_text[] = {
+ "0mv", "30mv", "60mv", "90mv",
+ "120mv", "150mv", "180mv", "210mv",
+ "240mv", "270mv", "300mv", "330mv",
+ "360mv", "390mv", "420mv", "450mv",
+};
+
+static const char *const diag_cfg_gnd_text[] = {
+ "0mv", "60mv", "120mv", "180mv",
+ "240mv", "300mv", "360mv", "420mv",
+ "480mv", "540mv", "600mv", "660mv",
+ "720mv", "780mv", "840mv", "900mv",
+};
+
+static const char *const tac5x1x_tdm_slot_text[] = {
+ "Slot0", "Slot1", "Slot2", "Slot3",
+ "Slot4", "Slot5", "Slot6", "Slot7",
+ "Slot8", "Slot9", "Slot10", "Slot11",
+ "Slot12", "Slot13", "Slot14", "Slot15",
+ "Slot16", "Slot17", "Slot18", "Slot19",
+ "Slot20", "Slot21", "Slot22", "Slot23",
+ "Slot24", "Slot25", "Slot26", "Slot27",
+ "Slot28", "Slot29", "Slot30", "Slot31",
+};
+
+static SOC_ENUM_SINGLE_DECL(tx_ch5_asi_cfg_enum, TAC5X1X_PASITXCH5, 5,
+ tx_ch5_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch6_asi_cfg_enum, TAC5X1X_PASITXCH6, 5,
+ tx_ch5_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch7_asi_cfg_enum, TAC5X1X_PASITXCH7, 5,
+ tx_ch7_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch8_asi_cfg_enum, TAC5X1X_PASITXCH8, 5,
+ tx_ch8_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch5_asi_cfg_enum, TAC5X1X_PASIRXCH5, 5,
+ rx_ch5_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch6_asi_cfg_enum, TAC5X1X_PASIRXCH6, 5,
+ rx_ch6_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch7_asi_cfg_enum, TAC5X1X_PASIRXCH7, 5,
+ rx_ch6_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch8_asi_cfg_enum, TAC5X1X_PASIRXCH8, 5,
+ rx_ch6_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(diag_cfg1_sht_term_enum, TAC5X1X_DIAG_CFG1, 4,
+ diag_cfg_text);
+static SOC_ENUM_SINGLE_DECL(diag_cfg1_vbat_in_enum, TAC5X1X_DIAG_CFG1, 0,
+ diag_cfg_text);
+static SOC_ENUM_SINGLE_DECL(diag_cfg2_sht_gnd_enum, TAC5X1X_DIAG_CFG2, 4,
+ diag_cfg_gnd_text);
+static SOC_ENUM_SINGLE_DECL(diag_cfg2_micbias, TAC5X1X_DIAG_CFG2, 0,
+ diag_cfg_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_ch1_slot_enum, TAC5X1X_PASIRXCH1, 0,
+ tac5x1x_tdm_slot_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch2_slot_enum, TAC5X1X_PASIRXCH2, 0,
+ tac5x1x_tdm_slot_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch3_slot_enum, TAC5X1X_PASIRXCH3, 0,
+ tac5x1x_tdm_slot_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch4_slot_enum, TAC5X1X_PASIRXCH4, 0,
+ tac5x1x_tdm_slot_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch1_slot_enum, TAC5X1X_PASITXCH1, 0,
+ tac5x1x_tdm_slot_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch2_slot_enum, TAC5X1X_PASITXCH2, 0,
+ tac5x1x_tdm_slot_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch3_slot_enum, TAC5X1X_PASITXCH3, 0,
+ tac5x1x_tdm_slot_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch4_slot_enum, TAC5X1X_PASITXCH4, 0,
+ tac5x1x_tdm_slot_text);
+
+/* Record */
+/* ADC Analog/PDM Selection */
+static const char *const tac5x1x_input_source_text[] = {"Analog", "PDM"};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_in1_source_enum, TAC5X1X_INTF4, 7,
+ tac5x1x_input_source_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_in2_source_enum, TAC5X1X_INTF4, 6,
+ tac5x1x_input_source_text);
+
+static const struct snd_kcontrol_new tac5x1x_dapm_in1_source_control[] = {
+ SOC_DAPM_ENUM("CH1 Source MUX", tac5x1x_in1_source_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_dapm_in2_source_control[] = {
+ SOC_DAPM_ENUM("CH2 Source MUX", tac5x1x_in2_source_enum),
+};
+
+static const char *const tad5x1x_input_source_text[] = {"Disable", "PDM"};
+static SOC_ENUM_SINGLE_DECL(tad5x1x_in1_source_enum, TAC5X1X_INTF4, 7,
+ tad5x1x_input_source_text);
+static SOC_ENUM_SINGLE_DECL(tad5x1x_in2_source_enum, TAC5X1X_INTF4, 6,
+ tad5x1x_input_source_text);
+
+static const struct snd_kcontrol_new tad5x1x_dapm_in1_source_control[] = {
+ SOC_DAPM_ENUM("CH1 Source MUX", tad5x1x_in1_source_enum),
+};
+
+static const struct snd_kcontrol_new tad5x1x_dapm_in2_source_control[] = {
+ SOC_DAPM_ENUM("CH2 Source MUX", tad5x1x_in2_source_enum),
+};
+
+/* ADC Analog source Selection */
+static const char *const tac5x1x_input_analog_sel_text[] = {
+ "Differential",
+ "Single-ended",
+ "Single-ended mux INxP",
+ "Single-ended mux INxM",
+};
+
+static const char *const tac5x1x_input_analog2_sel_text[] = {
+ "Differential",
+ "Single-ended",
+};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc1_config_enum, TAC5X1X_ADCCH1C0, 6,
+ tac5x1x_input_analog_sel_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc2_config_enum, TAC5X1X_ADCCH2C0, 6,
+ tac5x1x_input_analog2_sel_text);
+
+static const struct snd_kcontrol_new tac5x1x_dapm_adc1_config_control[] = {
+ SOC_DAPM_ENUM("ADC1 Analog MUX", tac5x1x_adc1_config_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_dapm_adc2_config_control[] = {
+ SOC_DAPM_ENUM("ADC2 Analog MUX", tac5x1x_adc2_config_enum),
+};
+
+static const struct snd_kcontrol_new taa5x1x_controls[] = {
+ SOC_ENUM("Record Decimation Filter",
+ decimation_filter_record_enum),
+ SOC_ENUM("ADC1 Audio BW", tac5x1x_adc1_wideband_enum),
+
+ SOC_SINGLE_TLV("ADC1 Digital Capture Volume", TAC5X1X_ADCCH1C2,
+ 0, 0xff, 0, record_dig_vol_tlv),
+
+ SOC_SINGLE_TLV("ADC1 Fine Capture Volume", TAC5X1X_ADCCH1C3,
+ 0, 0xff, 0, record_gain_cali_tlv),
+
+ SOC_SINGLE_RANGE("ADC1 Phase Capture Volume", TAC5X1X_ADCCH1C4,
+ 2, 0, 63, 0),
+
+ SOC_ENUM("ASI_TX_CH5_CFG", tx_ch5_asi_cfg_enum),
+ SOC_ENUM("ASI_TX_CH6_CFG", tx_ch6_asi_cfg_enum),
+ SOC_ENUM("ASI_TX_CH7_CFG", tx_ch7_asi_cfg_enum),
+ SOC_ENUM("ASI_TX_CH8_CFG", tx_ch8_asi_cfg_enum),
+ SOC_SINGLE("IN_CH1_EN Capture Switch", TAC5X1X_CH_EN, 7, 1, 0),
+ SOC_SINGLE("IN_CH2_EN Capture Switch", TAC5X1X_CH_EN, 6, 1, 0),
+ SOC_SINGLE("IN_CH3_EN Capture Switch", TAC5X1X_CH_EN, 5, 1, 0),
+ SOC_SINGLE("IN_CH4_EN Capture Switch", TAC5X1X_CH_EN, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new tac5x1x_tdm_slot_controls[] = {
+ SOC_ENUM("ASI_RX_CH1 Slot", rx_ch1_slot_enum),
+ SOC_ENUM("ASI_RX_CH2 Slot", rx_ch2_slot_enum),
+ SOC_ENUM("ASI_RX_CH3 Slot", rx_ch3_slot_enum),
+ SOC_ENUM("ASI_RX_CH4 Slot", rx_ch4_slot_enum),
+ SOC_ENUM("ASI_TX_CH1 Slot", tx_ch1_slot_enum),
+ SOC_ENUM("ASI_TX_CH2 Slot", tx_ch2_slot_enum),
+ SOC_ENUM("ASI_TX_CH3 Slot", tx_ch3_slot_enum),
+ SOC_ENUM("ASI_TX_CH4 Slot", tx_ch4_slot_enum),
+};
+
+static const struct snd_kcontrol_new tad5x1x_controls[] = {
+ SOC_ENUM("Playback Decimation Filter",
+ decimation_filter_playback_enum),
+ SOC_ENUM("DAC1 Audio BW", tac5x1x_dac1_wideband_enum),
+ SOC_SINGLE_TLV("OUT1P Analog Level Playback Volume", TAC5X1X_OUT1CFG1,
+ 3, 6, 1, playback_analog_level_tlv),
+ SOC_SINGLE_TLV("OUT1M Analog Level Playback Volume", TAC5X1X_OUT1CFG2,
+ 3, 6, 1, playback_analog_level_tlv),
+ SOC_SINGLE_TLV("DAC1 CHA Digital Playback Volume", TAC5X1X_DACCH1A0,
+ 0, 0xff, 0, dac_dig_vol_tlv),
+ SOC_SINGLE_TLV("DAC1 CHB Digital Playback Volume", TAC5X1X_DACCH1B0,
+ 0, 0xff, 0, dac_dig_vol_tlv),
+ SOC_SINGLE_TLV("DAC1 CHA Gain Calibration Playback Volume",
+ TAC5X1X_DACCH1A1, 4, 0xf, 0,
+ playback_gain_cali_tlv),
+ SOC_SINGLE_TLV("DAC1 CHB Gain Calibration Playback Volume",
+ TAC5X1X_DACCH1B1, 4, 0xf, 0,
+ playback_gain_cali_tlv),
+
+ SOC_SINGLE("ASI_RX_CH3_EN Playback Switch",
+ TAC5X1X_PASIRXCH3, 5, 1, 0),
+ SOC_SINGLE("ASI_RX_CH4_EN Playback Switch",
+ TAC5X1X_PASIRXCH4, 5, 1, 0),
+ SOC_ENUM("ASI_RX_CH5_EN Playback", rx_ch5_asi_cfg_enum),
+ SOC_ENUM("ASI_RX_CH6_EN Playback", rx_ch6_asi_cfg_enum),
+ SOC_ENUM("ASI_RX_CH7_EN Playback", rx_ch7_asi_cfg_enum),
+ SOC_ENUM("ASI_RX_CH8_EN Playback", rx_ch8_asi_cfg_enum),
+ SOC_SINGLE("OUT_CH1_EN Playback Switch", TAC5X1X_CH_EN, 3, 1, 0),
+ SOC_SINGLE("OUT_CH2_EN Playback Switch", TAC5X1X_CH_EN, 2, 1, 0),
+ SOC_SINGLE("OUT_CH3_EN Playback Switch", TAC5X1X_CH_EN, 1, 1, 0),
+ SOC_SINGLE("OUT_CH4_EN Playback Switch", TAC5X1X_CH_EN, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new tac5x11_controls[] = {
+ SOC_ENUM("ADC1 Common-mode Tolerance", tac5x1x_adc1_tolerance_enum),
+};
+
+static const struct snd_kcontrol_new tad5x12_controls[] = {
+ SOC_SINGLE_TLV("OUT2P Analog Level Playback Volume", TAC5X1X_OUT2CFG1,
+ 3, 6, 1, playback_analog_level_tlv),
+ SOC_SINGLE_TLV("OUT2M Analog Level Playback Volume", TAC5X1X_OUT2CFG2,
+ 3, 6, 1, playback_analog_level_tlv),
+ SOC_SINGLE_TLV("DAC2 CHA Digital Playback Volume", TAC5X1X_DACCH2A0,
+ 0, 0xff, 0, dac_dig_vol_tlv),
+ SOC_SINGLE_TLV("DAC2 CHB Digital Playback Volume", TAC5X1X_DACCH2B0,
+ 0, 0xff, 0, dac_dig_vol_tlv),
+ SOC_SINGLE_TLV("DAC2 CHA Gain Calibration Playback Volume",
+ TAC5X1X_DACCH2A1, 4, 0xf, 0,
+ playback_gain_cali_tlv),
+ SOC_SINGLE_TLV("DAC2 CHB Gain Calibration Playback Volume",
+ TAC5X1X_DACCH2B1, 4, 0xf, 0,
+ playback_gain_cali_tlv),
+ SOC_ENUM("DAC2 Audio BW", tac5x1x_dac2_wideband_enum),
+};
+
+static const struct snd_kcontrol_new taa5x12_controls[] = {
+ SOC_ENUM("ADC2 Audio BW", tac5x1x_adc2_wideband_enum),
+
+ SOC_SINGLE_TLV("ADC2 Digital Capture Volume", TAC5X1X_ADCCH2C2,
+ 0, 0xff, 0, record_dig_vol_tlv),
+
+ SOC_SINGLE_TLV("ADC2 Fine Capture Volume", TAC5X1X_ADCCH2C3,
+ 0, 0xff, 0, record_gain_cali_tlv),
+
+ SOC_SINGLE_RANGE("ADC2 Phase Capture Volume", TAC5X1X_ADCCH2C4,
+ 2, 0, 63, 0),
+};
+
+static const struct snd_kcontrol_new tolerance_ctrls[] = {
+ SOC_ENUM("ADC1 Common-mode Tolerance", tac5x1x_adc1_tolerance_enum),
+ SOC_ENUM("ADC2 Common-mode Tolerance", tac5x1x_adc2_tolerance_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_pdm_controls[] = {
+ SOC_ENUM("PDM Clk Divider", pdmclk_select_enum),
+
+ SOC_SINGLE_TLV("PDM1 Digital Capture Volume", TAC5X1X_ADCCH1C2,
+ 0, 0xff, 0, record_dig_vol_tlv),
+ SOC_SINGLE_TLV("PDM2 Digital Capture Volume", TAC5X1X_ADCCH2C2,
+ 0, 0xff, 0, record_dig_vol_tlv),
+ SOC_SINGLE_TLV("PDM1 Fine Capture Volume", TAC5X1X_ADCCH1C3,
+ 0, 0xff, 0, record_gain_cali_tlv),
+ SOC_SINGLE_TLV("PDM2 Fine Capture Volume", TAC5X1X_ADCCH2C3,
+ 0, 0xff, 0, record_gain_cali_tlv),
+ SOC_SINGLE_RANGE("PDM1 Phase Capture Volume", TAC5X1X_ADCCH1C4,
+ 2, 0, 63, 0),
+ SOC_SINGLE_RANGE("PDM2 Phase Capture Volume", TAC5X1X_ADCCH2C4,
+ 2, 0, 63, 0),
+ SOC_SINGLE_TLV("PDM3 Digital Capture Volume", TAC5X1X_ADCCH3C2,
+ 0, 0xff, 0, record_dig_vol_tlv),
+ SOC_SINGLE_TLV("PDM4 Digital Capture Volume", TAC5X1X_ADCCH4C2,
+ 0, 0xff, 0, record_dig_vol_tlv),
+ SOC_SINGLE_TLV("PDM3 Fine Capture Volume", TAC5X1X_ADCCH3C3,
+ 0, 0xff, 0, record_gain_cali_tlv),
+ SOC_SINGLE_TLV("PDM4 Fine Capture Volume", TAC5X1X_ADCCH4C3,
+ 0, 0xff, 0, record_gain_cali_tlv),
+ SOC_SINGLE_RANGE("PDM3 Phase Capture Volume", TAC5X1X_ADCCH3C4,
+ 2, 0, 63, 0),
+ SOC_SINGLE_RANGE("PDM4 Phase Capture Volume", TAC5X1X_ADCCH4C4,
+ 2, 0, 63, 0),
+};
+
+static const struct snd_kcontrol_new taa_ip_controls[] = {
+ SOC_ENUM("DIAG_SHT_TERM", diag_cfg1_sht_term_enum),
+ SOC_ENUM("DIAG_SHT_VBAT_IN", diag_cfg1_vbat_in_enum),
+ SOC_ENUM("DIAG_SHT_GND", diag_cfg2_sht_gnd_enum),
+ SOC_ENUM("DIAG_SHT_MICBIAS", diag_cfg2_micbias),
+};
+
+static const struct snd_soc_dapm_widget taa5x1x_dapm_widgets[] = {
+ /* ADC1 */
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_MUX("ADC1 Full-Scale", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dapm_adc1_fscale_control),
+ SND_SOC_DAPM_MUX("ADC1 Config", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dapm_adc1_config_control),
+ SND_SOC_DAPM_ADC_E("CH1_ADC_EN", "CH1 Capture", SND_SOC_NOPM, 0, 0,
+ tac5x1x_adc_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SWITCH("ASI_TX_CH1_EN", SND_SOC_NOPM, 0, 0,
+ &tx_ch1_asi_switch),
+ SND_SOC_DAPM_MICBIAS("Mic Bias", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SWITCH("ASI_TX_CH2_EN", SND_SOC_NOPM, 0, 0,
+ &tx_ch2_asi_switch),
+};
+
+static const struct snd_soc_dapm_widget tad5xx_dapm_widgets[] = {
+ /* pdm capture */
+ SND_SOC_DAPM_SWITCH("ASI_TX_CH1_EN", SND_SOC_NOPM, 0, 0,
+ &tx_ch1_asi_switch),
+ SND_SOC_DAPM_SWITCH("ASI_TX_CH2_EN", SND_SOC_NOPM, 0, 0,
+ &tx_ch2_asi_switch),
+};
+
+static const struct snd_soc_dapm_widget tad5x1x_dapm_widgets[] = {
+ /* DAC1 */
+ SND_SOC_DAPM_AIF_IN("ASI IN1", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_MUX("OUT1x Source", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dapm_out1_source_control),
+ SND_SOC_DAPM_MUX("OUT1x Config", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dapm_out1_config_control),
+ SND_SOC_DAPM_MUX("OUT1x Driver", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dapm_out1_driver_control),
+ SND_SOC_DAPM_DAC_E("Left DAC Enable", "ASI Playback", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SWITCH("ASI_RX_CH1_EN", SND_SOC_NOPM, 0, 0,
+ &rx_ch1_asi_switch),
+};
+
+static const struct snd_soc_dapm_widget taa5x12_dapm_widgets[] = {
+ /* ADC2 */
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_MUX("ADC2 Full-Scale", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dapm_adc2_fscale_control),
+ SND_SOC_DAPM_MUX("ADC2 Config", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dapm_adc2_config_control),
+ SND_SOC_DAPM_ADC_E("CH2_ADC_EN", "CH2 Capture", SND_SOC_NOPM, 1, 0,
+ tac5x1x_adc_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_widget tad5x12_dapm_widgets[] = {
+ /* DAC2 */
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+
+ SND_SOC_DAPM_AIF_IN("ASI IN2", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("OUT2x Source", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dapm_out2_source_control),
+ SND_SOC_DAPM_MUX("OUT2x Config", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dapm_out2_config_control),
+ SND_SOC_DAPM_MUX("OUT2x Driver", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dapm_out2_driver_control),
+ SND_SOC_DAPM_DAC_E("Right DAC Enable", "ASI Playback", SND_SOC_NOPM, 1, 0,
+ tac5x1x_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SWITCH("ASI_RX_CH2_EN", SND_SOC_NOPM, 0, 0,
+ &rx_ch2_asi_switch),
+};
+
+static const struct snd_soc_dapm_widget tac5x1x_pdm_widgets[] = {
+ /* PDM - no event handler, power is managed via adc_event + post_powerup_work */
+ SND_SOC_DAPM_INPUT("DIN1"),
+ SND_SOC_DAPM_INPUT("DIN2"),
+ SND_SOC_DAPM_INPUT("DIN3"),
+ SND_SOC_DAPM_INPUT("DIN4"),
+
+ SND_SOC_DAPM_ADC_E("CH1_PDM_EN", "PDM CH1 Capture", SND_SOC_NOPM, 0, 0,
+ tac5x1x_adc_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("CH2_PDM_EN", "PDM CH2 Capture", SND_SOC_NOPM, 1, 0,
+ tac5x1x_adc_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("CH3_PDM_EN", "PDM CH3 Capture", SND_SOC_NOPM, 2, 0,
+ tac5x1x_adc_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("CH4_PDM_EN", "PDM CH4 Capture", SND_SOC_NOPM, 3, 0,
+ tac5x1x_adc_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SWITCH("ASI_TX_CH3_EN", SND_SOC_NOPM, 0, 0,
+ &tx_ch3_asi_switch),
+ SND_SOC_DAPM_SWITCH("ASI_TX_CH4_EN", SND_SOC_NOPM, 0, 0,
+ &tx_ch4_asi_switch),
+};
+
+static const struct snd_soc_dapm_widget tac5x1x_common_widgets[] = {
+ SND_SOC_DAPM_MUX("IN1 Source Mux", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dapm_in1_source_control),
+ SND_SOC_DAPM_MUX("IN2 Source Mux", SND_SOC_NOPM, 0, 0,
+ tac5x1x_dapm_in2_source_control),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "ASI Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_widget tad_common_widgets[] = {
+ SND_SOC_DAPM_MUX("IN1 Source Mux", SND_SOC_NOPM, 0, 0,
+ tad5x1x_dapm_in1_source_control),
+ SND_SOC_DAPM_MUX("IN2 Source Mux", SND_SOC_NOPM, 0, 0,
+ tad5x1x_dapm_in2_source_control),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "ASI Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route taa5x1x_dapm_routes[] = {
+ /*
+ * ADC channel1
+ * ADC path: AIN1 -> CH1_ADC_EN -> ADC1 Config ->
+ * ADC1 Full-Scale -> Mic Bias -> IN1 Source Mux (Analog) ->
+ * ASI_TX_CH1_EN -> AIF OUT
+ * PDM path: DIN1 -> IN1 Source Mux (PDM) ->
+ * ASI_TX_CH1_EN -> CH1_PDM_EN -> AIF OUT
+ *
+ * Note: The route {"IN1 Source Mux", "Analog", "Mic Bias"} is in
+ * tac_common_routes because IN1 Source Mux widget is created later.
+ */
+ {"CH1_ADC_EN", NULL, "AIN1"},
+ {"ADC1 Config", "Differential", "CH1_ADC_EN"},
+ {"ADC1 Config", "Single-ended", "CH1_ADC_EN"},
+ {"ADC1 Config", "Single-ended mux INxP", "CH1_ADC_EN"},
+ {"ADC1 Config", "Single-ended mux INxM", "CH1_ADC_EN"},
+
+ {"ADC1 Full-Scale", "2/10-VRMS", "ADC1 Config"},
+ {"ADC1 Full-Scale", "4/5-VRMS", "ADC1 Config"},
+ {"Mic Bias", NULL, "ADC1 Full-Scale"},
+
+};
+
+static const struct snd_soc_dapm_route tad5x1x_dapm_routes[] = {
+ /* Left Output */
+ {"ASI_RX_CH1_EN", "Switch", "ASI IN1"},
+
+ {"OUT1x Source", "DAC + Analog Bypass Mix", "ASI_RX_CH1_EN"},
+ {"OUT1x Source", "DAC -> OUTxP, INxP -> OUTxM", "ASI_RX_CH1_EN"},
+ {"OUT1x Source", "INxM -> OUTxP, DAC -> OUTxM", "ASI_RX_CH1_EN"},
+
+ {"OUT1x Config", "Differential", "OUT1x Source"},
+ // {"OUT1x Config", "Stereo Single-ended", "OUT1x Source"},
+ {"OUT1x Config", "Mono Single-ended at OUTxP only", "OUT1x Source"},
+ {"OUT1x Config", "Mono Single-ended at OUTxM only", "OUT1x Source"},
+ {"OUT1x Config", "Pseudo differential with OUTxM as VCOM",
+ "OUT1x Source"},
+ {"OUT1x Config", "Pseudo differential with OUTxM as external sensing",
+ "OUT1x Source"},
+ {"OUT1x Config", "Pseudo differential with OUTxP as VCOM",
+ "OUT1x Source"},
+
+ {"OUT1x Driver", "Line-out", "OUT1x Config"},
+ {"OUT1x Driver", "Headphone", "OUT1x Config"},
+
+ {"Left DAC Enable", NULL, "OUT1x Driver"},
+ {"OUT1", NULL, "Left DAC Enable"},
+};
+
+static const struct snd_soc_dapm_route taa5x12_dapm_routes[] = {
+ /*
+ * ADC channel2
+ * Same design as ADC channel1.
+ * Note: The route {"IN2 Source Mux", "Analog", "Mic Bias"} is in
+ * tac_common_routes because IN2 Source Mux widget is created later.
+ */
+ {"CH2_ADC_EN", NULL, "AIN2"},
+ {"ADC2 Config", "Differential", "CH2_ADC_EN"},
+ {"ADC2 Config", "Single-ended", "CH2_ADC_EN"},
+ {"ADC2 Full-Scale", "2/10-VRMS", "ADC2 Config"},
+ {"ADC2 Full-Scale", "4/5-VRMS", "ADC2 Config"},
+
+ {"Mic Bias", NULL, "ADC2 Full-Scale"},
+};
+
+static const struct snd_soc_dapm_route tad5x12_dapm_routes[] = {
+ /* Right Output */
+ {"ASI_RX_CH2_EN", "Switch", "ASI IN2"},
+
+ {"OUT2x Source", "DAC + Analog Bypass Mix", "ASI_RX_CH1_EN"},
+ {"OUT2x Source", "DAC -> OUTxP, INxP -> OUTxM", "ASI_RX_CH1_EN"},
+ {"OUT2x Source", "INxM -> OUTxP, DAC -> OUTxM", "ASI_RX_CH1_EN"},
+
+ {"OUT2x Config", "Differential", "OUT2x Source"},
+ // {"OUT2x Config", "Stereo Single-ended", "OUT2x Source"},
+ {"OUT2x Config", "Mono Single-ended at OUTxP only", "OUT2x Source"},
+ {"OUT2x Config", "Mono Single-ended at OUTxM only", "OUT2x Source"},
+ {"OUT2x Config", "Pseudo differential with OUTxM as VCOM",
+ "OUT2x Source"},
+ {"OUT2x Config", "Pseudo differential with OUTxP as VCOM",
+ "OUT2x Source"},
+ {"OUT2x Driver", "Line-out", "OUT2x Config"},
+ {"OUT2x Driver", "Headphone", "OUT2x Config"},
+ {"Right DAC Enable", NULL, "OUT2x Driver"},
+ {"OUT2", NULL, "Right DAC Enable"},
+};
+
+static const struct snd_soc_dapm_route tac5x1x_pdm_routes[] = {
+ /* PDM channel1 & Channel2
+ * PDM path directly from inputs through source mux
+ */
+ {"IN1 Source Mux", "PDM", "DIN1"},
+ {"IN2 Source Mux", "PDM", "DIN2"},
+
+ {"ASI_TX_CH1_EN", "Capture Switch", "IN1 Source Mux"},
+ {"ASI_TX_CH2_EN", "Capture Switch", "IN2 Source Mux"},
+
+ {"CH1_PDM_EN", NULL, "ASI_TX_CH1_EN"},
+ {"CH2_PDM_EN", NULL, "ASI_TX_CH2_EN"},
+
+ {"AIF OUT", NULL, "CH1_PDM_EN"},
+ {"AIF OUT", NULL, "CH2_PDM_EN"},
+
+ /* PDM channel3 & Channel4 */
+ {"ASI_TX_CH3_EN", "Capture Switch", "DIN3"},
+ {"ASI_TX_CH4_EN", "Capture Switch", "DIN4"},
+
+ {"CH3_PDM_EN", NULL, "ASI_TX_CH3_EN"},
+ {"CH4_PDM_EN", NULL, "ASI_TX_CH4_EN"},
+
+ {"AIF OUT", NULL, "CH3_PDM_EN"},
+ {"AIF OUT", NULL, "CH4_PDM_EN"},
+};
+
+static const struct snd_soc_dapm_route tac_common_routes[] = {
+ /*
+ * Analog ADC path - routed through Source Mux
+ * These routes must be here because IN1/IN2 Source Mux widgets
+ * are created in tac5x1x_common_widgets which is added after
+ * device-specific routes. The Analog path is already gated by
+ * CH1_Enable/CH2_Enable through the ADC routes in device-specific files.
+ */
+ {"IN1 Source Mux", "Analog", "Mic Bias"},
+ {"IN2 Source Mux", "Analog", "Mic Bias"},
+
+ /* Connect Source Mux to ASI_TX_EN for ADC capture */
+ {"ASI_TX_CH1_EN", "Capture Switch", "IN1 Source Mux"},
+ {"ASI_TX_CH2_EN", "Capture Switch", "IN2 Source Mux"},
+
+ /*
+ * Direct routes from ASI_TX_CHx_EN to AIF OUT for ADC path.
+ * PDM path goes through CHx_PDM_EN widgets which have their own
+ * routes to AIF OUT. This allows ADC and PDM to share ASI_TX_CHx_EN
+ * while maintaining separate event handlers.
+ */
+ {"AIF OUT", NULL, "ASI_TX_CH1_EN"},
+ {"AIF OUT", NULL, "ASI_TX_CH2_EN"},
+};
+
+/* TAD routes - PDM only, no ADC/Mic Bias */
+static const struct snd_soc_dapm_route tad_common_routes[] = {
+ /* Connect Source Mux to ASI_TX_EN for PDM capture */
+ {"ASI_TX_CH1_EN", "Capture Switch", "IN1 Source Mux"},
+ {"ASI_TX_CH2_EN", "Capture Switch", "IN2 Source Mux"},
+
+ /* Routes from ASI_TX_CHx_EN to AIF OUT for PDM path */
+ {"AIF OUT", NULL, "ASI_TX_CH1_EN"},
+ {"AIF OUT", NULL, "ASI_TX_CH2_EN"},
+};
+
+static bool is_stereo_device(struct tac5x1x *tac5x1x)
+{
+ switch (tac5x1x->codec_type) {
+ case TAA5212:
+ case TAA5412:
+ case TAC5112:
+ case TAC5212:
+ case TAC5312:
+ case TAC5412:
+ case TAD5112:
+ case TAD5212:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static s32 tac5x1x_pwr_ctrl(struct snd_soc_component *component,
+ bool power_state)
+{
+ struct tac5x1x_priv *tac5x1x_priv =
+ snd_soc_component_get_drvdata(component);
+ struct tac5x1x *tac5x1x = tac5x1x_priv->tac5x1x;
+ struct device *parent = component->dev->parent;
+ s32 active_ctrl, ret;
+ s32 pwr_ctrl = 0;
+
+ /* Ensure parent device is active before accessing registers */
+ ret = pm_runtime_resume_and_get(parent);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to resume parent: %d\n", ret);
+ return ret;
+ }
+
+ if (power_state) {
+ active_ctrl = TAC5X1X_VREF_SLEEP_ACTIVE_MASK;
+ snd_soc_component_update_bits(component, TAC5X1X_VREFCFG,
+ TAC5X1X_VREFCFG_MICBIAS_VAL_MASK,
+ tac5x1x->micbias_vg << 2);
+ snd_soc_component_update_bits(component, TAC5X1X_VREFCFG,
+ TAC5X1X_VREFCFG_VREF_FSCALE_MASK,
+ tac5x1x->vref_vg);
+
+ if (tac5x1x->uad_en)
+ pwr_ctrl |= TAC5X1X_PWR_CFG_UAD_EN;
+ if (tac5x1x->vad_en)
+ pwr_ctrl |= TAC5X1X_PWR_CFG_VAD_EN;
+ if (tac5x1x->uag_en)
+ pwr_ctrl |= TAC5X1X_PWR_CFG_UAG_EN;
+ } else {
+ active_ctrl = 0x0;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAC5X1X_VREF,
+ TAC5X1X_VREF_SLEEP_EXIT_VREF_EN |
+ TAC5X1X_VREF_SLEEP_ACTIVE_MASK,
+ active_ctrl);
+ if (ret < 0) {
+ dev_err(tac5x1x->dev,
+ "%s, device active or sleep failed!, ret %d/n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAC5X1X_PWR_CFG,
+ TAC5X1X_PWR_CFG_UAD_EN |
+ TAC5X1X_PWR_CFG_UAG_EN |
+ TAC5X1X_PWR_CFG_VAD_EN, pwr_ctrl);
+ if (ret < 0)
+ dev_err(tac5x1x->dev,
+ "%s, Power control set failed!, ret %d/n",
+ __func__, ret);
+
+out:
+ /* Release parent PM reference, autosuspend will handle delay */
+ pm_runtime_mark_last_busy(parent);
+ pm_runtime_put_autosuspend(parent);
+
+ return ret;
+}
+
+static s32 tac5x1x_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ s32 iface_reg_1 = 0;
+ s32 iface_reg_2 = 0;
+ s32 iface_reg_3 = 0;
+ s32 clk_pol = 0;
+ bool set_slot_positions = false;
+ int right_slot = 1;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ iface_reg_1 |= TAC5X1X_PASI_MODE_MASK;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: invalid DAI master/slave interface\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface_reg_2 |= TAC5X1X_PASI_FMT_I2S;
+ right_slot = 16;
+ set_slot_positions = true;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface_reg_2 |= TAC5X1X_PASI_FMT_TDM;
+ iface_reg_3 |= BIT(0); /* add offset 1 */
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface_reg_2 |= TAC5X1X_PASI_FMT_TDM;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface_reg_2 |= TAC5X1X_PASI_FMT_LJ;
+ right_slot = 16;
+ set_slot_positions = true;
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: invalid DAI interface format\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ clk_pol |= TAC5X1X_PASI_FSYNC_POL;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ clk_pol |= TAC5X1X_PASI_BCLK_POL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ clk_pol |= TAC5X1X_PASI_BCLK_POL | TAC5X1X_PASI_FSYNC_POL;
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: invalid DAI clock polarity\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Clock provider mode */
+ snd_soc_component_update_bits(component, TAC5X1X_CNTCLK2,
+ TAC5X1X_PASI_MODE_MASK, iface_reg_1);
+
+ /* ASI format */
+ snd_soc_component_update_bits(component, TAC5X1X_PASI0,
+ TAC5X1X_PASI_FMT_MASK, iface_reg_2);
+
+ /* BCLK/FSYNC polarity */
+ snd_soc_component_update_bits(component, TAC5X1X_PASI0,
+ TAC5X1X_PASI_BCLK_POL |
+ TAC5X1X_PASI_FSYNC_POL, clk_pol);
+
+ /* TX slot offset */
+ snd_soc_component_update_bits(component, TAC5X1X_PASITX1,
+ TAC5X1X_PASITX_OFFSET_MASK, iface_reg_3);
+
+ /* RX slot offset */
+ snd_soc_component_update_bits(component, TAC5X1X_PASIRX0,
+ TAC5X1X_PASIRX_OFFSET_MASK, iface_reg_3);
+
+ /* Set slot positions only for I2S mode (left=0, right=16) */
+ if (set_slot_positions) {
+ snd_soc_component_update_bits(component, TAC5X1X_PASIRXCH1,
+ TAC5X1X_PASIRX_OFFSET_MASK, 0);
+ snd_soc_component_update_bits(component, TAC5X1X_PASITXCH1,
+ TAC5X1X_PASITX_OFFSET_MASK, 0);
+ snd_soc_component_update_bits(component, TAC5X1X_PASIRXCH2,
+ TAC5X1X_PASIRX_OFFSET_MASK, right_slot);
+ snd_soc_component_update_bits(component, TAC5X1X_PASITXCH2,
+ TAC5X1X_PASITX_OFFSET_MASK, right_slot);
+ }
+
+ return 0;
+}
+
+static s32 tac5x1x_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;
+ s32 sample_rate, word_length = 0;
+
+ switch (params_rate(params)) {
+ case 24000:
+ sample_rate = 25;
+ break;
+ case 32000:
+ sample_rate = 23;
+ break;
+ case 44100:
+ case 48000:
+ sample_rate = 20;
+ break;
+ case 64000:
+ sample_rate = 18;
+ break;
+ case 96000:
+ sample_rate = 15;
+ break;
+ case 192000:
+ sample_rate = 10;
+ break;
+ default:
+ /* Auto detect sample rate */
+ sample_rate = 0;
+ break;
+ }
+
+ switch (params_physical_width(params)) {
+ case 16:
+ word_length |= TAC5X1X_WORD_LEN_16BITS;
+ break;
+ case 20:
+ word_length |= TAC5X1X_WORD_LEN_20BITS;
+ break;
+ case 24:
+ word_length |= TAC5X1X_WORD_LEN_24BITS;
+ break;
+ case 32:
+ word_length |= TAC5X1X_WORD_LEN_32BITS;
+ break;
+ default:
+ dev_err(component->dev, "%s, set word length failed\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "sample rate: %d, word length: %d\n",
+ sample_rate, word_length);
+
+ snd_soc_component_update_bits(component, TAC5X1X_PASI0,
+ TAC5X1X_PASI_DATALEN_MASK, word_length);
+
+ tac5x1x_pwr_ctrl(component, true);
+ return 0;
+}
+
+static s32 tac5x1x_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ s32 ret = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = tac5x1x_pwr_ctrl(component, true);
+ if (ret < 0)
+ dev_err(component->dev,
+ "%s, power up failed!\n", __func__);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ case SND_SOC_BIAS_OFF:
+ ret = tac5x1x_pwr_ctrl(component, false);
+ if (ret < 0)
+ dev_err(component->dev,
+ "%s, power down failed!\n", __func__);
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops tac5x1x_ops = {
+ .hw_params = tac5x1x_hw_params,
+ .set_fmt = tac5x1x_set_dai_fmt,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver tac5x1x_dai = {
+ .name = "tac5x1x-hifi",
+ .playback = {
+ .stream_name = "ASI Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAC5X1X_RATES,
+ .formats = TAC5X1X_FORMATS,},
+ .capture = {
+ .stream_name = "ASI Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAC5X1X_RATES,
+ .formats = TAC5X1X_FORMATS,
+ },
+ .ops = &tac5x1x_ops,
+ .symmetric_rate = 1,
+};
+
+static struct snd_soc_dai_driver taa5x1x_dai = {
+ .name = "taa5x1x-hifi",
+ .capture = {
+ .stream_name = "ASI Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAC5X1X_RATES,
+ .formats = TAC5X1X_FORMATS,
+ },
+ .ops = &tac5x1x_ops,
+};
+
+static struct snd_soc_dai_driver tad5x1x_dai = {
+ .name = "tad5x1x-hifi",
+ .playback = {
+ .stream_name = "ASI Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAC5X1X_RATES,
+ .formats = TAC5X1X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASI Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAC5X1X_RATES,
+ .formats = TAC5X1X_FORMATS,
+ },
+ .ops = &tac5x1x_ops,
+ .symmetric_rate = 1,
+};
+
+static s32 tac5x1x_add_ip_diag_controls(struct snd_soc_component *component)
+{
+ struct tac5x1x_priv *tac5x1x_priv =
+ snd_soc_component_get_drvdata(component);
+ struct tac5x1x *tac5x1x = tac5x1x_priv->tac5x1x;
+ struct tac5x1x_input_diag_config *input_diag_config =
+ &tac5x1x_priv->tac5x1x->input_diag_config;
+ s32 ret = 0;
+
+ switch (tac5x1x->codec_type) {
+ case TAA5212:
+ break;
+ case TAA5412:
+ case TAC5301:
+ case TAC5311:
+ case TAC5312:
+ case TAC5411:
+ case TAC5412:
+ if (input_diag_config->in_ch_en) {
+ ret = snd_soc_add_component_controls(component,
+ taa_ip_controls,
+ ARRAY_SIZE(taa_ip_controls));
+ if (ret)
+ return ret;
+ snd_soc_component_update_bits(component,
+ TAC5X1X_DIAG_CFG0,
+ TAC5X1X_IN_CH_DIAG_EN_MASK,
+ TAC5X1X_IN_CH_DIAG_EN_MASK);
+ }
+ if (input_diag_config->out_ch_en)
+ snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG0,
+ TAC5X1X_OUT1P_DIAG_EN_MASK,
+ TAC5X1X_OUT1P_DIAG_EN_MASK);
+
+ if (input_diag_config->incl_se_inm)
+ snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG0,
+ TAC5X1X_INCL_SE_INM_MASK,
+ TAC5X1X_INCL_SE_INM_MASK);
+ if (input_diag_config->incl_ac_coup)
+ snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG0,
+ TAC5X1X_INCL_AC_COUP_MASK,
+ TAC5X1X_INCL_AC_COUP_MASK);
+
+ if (tac5x1x->micbias_thr[0] > 0 && tac5x1x->micbias_thr[1] > 0) {
+ snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG7,
+ 0xff,
+ tac5x1x->micbias_thr[0]);
+ snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG6,
+ 0xff,
+ tac5x1x->micbias_thr[1]);
+ }
+
+ ret = tac5x1x_register_interrupt(tac5x1x_priv);
+ if (ret < 0)
+ dev_warn(tac5x1x->dev,
+ "ignore IRQ registration failure\n");
+
+ fallthrough;
+ case TAC5111:
+ case TAC5112:
+ case TAC5211:
+ case TAC5212:
+ case TAD5112:
+ case TAD5212:
+ snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG9,
+ 0xff, tac5x1x->gpa_threshold[0]);
+ snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG8,
+ 0xff, tac5x1x->gpa_threshold[1]);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+};
+
+static s32 tac5x1x_add_controls(struct snd_soc_component *component)
+{
+ struct tac5x1x_priv *tac5x1x =
+ snd_soc_component_get_drvdata(component);
+ s32 ret;
+
+ switch (tac5x1x->tac5x1x->codec_type) {
+ case TAA5212:
+ fallthrough;
+ case TAA5412:
+ ret =
+ snd_soc_add_component_controls(component, tolerance_ctrls,
+ ARRAY_SIZE(tolerance_ctrls));
+ if (ret)
+ return ret;
+ ret =
+ snd_soc_add_component_controls(component,
+ taa_ip_controls,
+ ARRAY_SIZE(taa_ip_controls));
+ if (ret)
+ return ret;
+ break;
+ case TAC5111:
+ case TAC5211:
+ ret =
+ snd_soc_add_component_controls(component,
+ tac5x11_controls,
+ ARRAY_SIZE(tac5x11_controls));
+ if (ret)
+ return ret;
+ fallthrough;
+ case TAC5301:
+ case TAC5311:
+ case TAC5411:
+ ret =
+ snd_soc_add_component_controls(component, tad5x1x_controls,
+ ARRAY_SIZE(tad5x1x_controls));
+ if (ret)
+ return ret;
+ break;
+ case TAC5112:
+ case TAC5212:
+ fallthrough;
+ case TAC5312:
+ case TAC5412:
+ ret =
+ snd_soc_add_component_controls(component, tolerance_ctrls,
+ ARRAY_SIZE(tolerance_ctrls));
+ if (ret)
+ return ret;
+ ret =
+ snd_soc_add_component_controls(component, tad5x1x_controls,
+ ARRAY_SIZE(tad5x1x_controls));
+ if (ret)
+ return ret;
+
+ /* Only add stereo ADC controls for stereo devices
+ * Mono devices should not have CH2 ADC controls
+ */
+ if (is_stereo_device(tac5x1x->tac5x1x)) {
+ ret =
+ snd_soc_add_component_controls(component, taa5x12_controls,
+ ARRAY_SIZE(taa5x12_controls));
+ if (ret)
+ return ret;
+ }
+
+ ret =
+ snd_soc_add_component_controls(component, tad5x12_controls,
+ ARRAY_SIZE(tad5x12_controls));
+ if (ret)
+ return ret;
+ break;
+ case TAD5212:
+ case TAD5112:
+ ret = snd_soc_add_component_controls(component, tad5x12_controls,
+ ARRAY_SIZE(tad5x12_controls));
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ /* Always add PDM controls - Source Mux controls handle routing */
+ ret = snd_soc_add_component_controls(component,
+ tac5x1x_pdm_controls,
+ ARRAY_SIZE(tac5x1x_pdm_controls));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_add_component_controls(component, tac5x1x_tdm_slot_controls,
+ ARRAY_SIZE(tac5x1x_tdm_slot_controls));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static s32 tac5x1x_add_widgets(struct snd_soc_component *component)
+{
+ struct tac5x1x_priv *tac5x1x_priv =
+ snd_soc_component_get_drvdata(component);
+ struct tac5x1x *tac5x1x = tac5x1x_priv->tac5x1x;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_to_dapm(component);
+ s32 ret;
+
+ switch (tac5x1x->codec_type) {
+ case TAC5111:
+ case TAC5211:
+ case TAC5301:
+ case TAC5311:
+ case TAC5411:
+ ret =
+ snd_soc_dapm_new_controls(dapm, tad5x1x_dapm_widgets,
+ ARRAY_SIZE(tad5x1x_dapm_widgets));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_add_routes(dapm, tad5x1x_dapm_routes,
+ ARRAY_SIZE(tad5x1x_dapm_routes));
+ if (ret)
+ return ret;
+ break;
+ case TAC5112:
+ case TAC5212:
+ case TAC5312:
+ case TAC5412:
+ ret =
+ snd_soc_dapm_new_controls(dapm, tad5x1x_dapm_widgets,
+ ARRAY_SIZE(tad5x1x_dapm_widgets));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_add_routes(dapm, tad5x1x_dapm_routes,
+ ARRAY_SIZE(tad5x1x_dapm_routes));
+ if (ret)
+ return ret;
+ ret =
+ snd_soc_dapm_new_controls(dapm, tad5x12_dapm_widgets,
+ ARRAY_SIZE(tad5x12_dapm_widgets));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_add_routes(dapm, tad5x12_dapm_routes,
+ ARRAY_SIZE(tad5x12_dapm_routes));
+ if (ret)
+ return ret;
+ fallthrough;
+ case TAA5212:
+ case TAA5412:
+ /* Add stereo ADC widgets/routes for stereo devices */
+ if (is_stereo_device(tac5x1x)) {
+ ret =
+ snd_soc_dapm_new_controls(dapm, taa5x12_dapm_widgets,
+ ARRAY_SIZE(taa5x12_dapm_widgets));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_add_routes(dapm, taa5x12_dapm_routes,
+ ARRAY_SIZE(taa5x12_dapm_routes));
+ if (ret)
+ return ret;
+ }
+ break;
+ case TAD5212:
+ case TAD5112:
+ ret =
+ snd_soc_dapm_new_controls(dapm, tad5xx_dapm_widgets,
+ ARRAY_SIZE(tad5xx_dapm_widgets));
+ if (ret)
+ return ret;
+
+ ret =
+ snd_soc_dapm_new_controls(dapm, tad5x12_dapm_widgets,
+ ARRAY_SIZE(tad5x12_dapm_widgets));
+ if (ret)
+ return ret;
+ ret = snd_soc_dapm_add_routes(dapm, tad5x12_dapm_routes,
+ ARRAY_SIZE(tad5x12_dapm_routes));
+ if (ret)
+ return ret;
+
+ break;
+ default:
+ break;
+ }
+
+ switch (tac5x1x->codec_type) {
+ case TAD5212:
+ case TAD5112:
+ ret =
+ snd_soc_dapm_new_controls(dapm, tad_common_widgets,
+ ARRAY_SIZE(tad_common_widgets));
+ if (ret)
+ return ret;
+ /* TAD devices only have PDM capture, use PDM-only routes */
+ ret = snd_soc_dapm_add_routes(dapm, tad_common_routes,
+ ARRAY_SIZE(tad_common_routes));
+ if (ret)
+ return ret;
+ break;
+ default:
+ ret =
+ snd_soc_dapm_new_controls(dapm, tac5x1x_common_widgets,
+ ARRAY_SIZE(tac5x1x_common_widgets));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, tac_common_routes,
+ ARRAY_SIZE(tac_common_routes));
+ if (ret)
+ return ret;
+ break;
+ }
+
+ /* Always add PDM widgets - Source Mux controls handle routing */
+ ret = snd_soc_dapm_new_controls(dapm, tac5x1x_pdm_widgets,
+ ARRAY_SIZE(tac5x1x_pdm_widgets));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(dapm, tac5x1x_pdm_routes,
+ ARRAY_SIZE(tac5x1x_pdm_routes));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int tac5x1x_setup_adc_impedance(struct device *dev,
+ struct tac5x1x_priv *tac5x1x_priv)
+{
+ struct tac5x1x *tac5x1x = tac5x1x_priv->tac5x1x;
+
+ if (tac5x1x->adc_impedance[0] != -1)
+ snd_soc_component_update_bits(tac5x1x_priv->component,
+ TAC5X1X_ADCCH1C0,
+ TAC5X1X_ADCCH1C0_IMPEDANCE_MASK,
+ tac5x1x->adc_impedance[0] << 4);
+
+ if (tac5x1x->adc_impedance[1] != -1)
+ snd_soc_component_update_bits(tac5x1x_priv->component,
+ TAC5X1X_ADCCH2C0,
+ TAC5X1X_ADCCH2C0_IMPEDANCE_MASK,
+ tac5x1x->adc_impedance[1] << 4);
+
+ return 0;
+}
+
+static s32 tac5x1x_component_probe(struct snd_soc_component *component)
+{
+ s32 ret;
+ struct tac5x1x_priv *tac5x1x_priv =
+ snd_soc_component_get_drvdata(component);
+ struct tac5x1x *tac5x1x = dev_get_drvdata(component->dev->parent);
+ struct device *dev = tac5x1x->dev;
+
+ snd_soc_component_init_regmap(component, tac5x1x->regmap);
+ tac5x1x_priv->component = component;
+
+ /* Initialize power sequence handling */
+ mutex_init(&tac5x1x_priv->ch_lock);
+ tac5x1x_priv->pwr_up_done = false;
+ INIT_DELAYED_WORK(&tac5x1x_priv->powerup_work, post_powerup_work);
+
+ ret = tac5x1x_add_controls(component);
+ if (ret < 0) {
+ dev_err(dev, "create control failed\n");
+ return ret;
+ }
+
+ ret = tac5x1x_add_widgets(component);
+ if (ret < 0) {
+ dev_err(dev, "device widget addition failed\n");
+ return ret;
+ }
+
+ tac5x1x_setup_adc_impedance(dev, tac5x1x_priv);
+
+ ret = tac5x1x_add_ip_diag_controls(component);
+ if (ret < 0)
+ dev_warn(dev, "add diag control failed\n");
+
+ return 0;
+}
+
+static int tac5x1x_soc_suspend(struct snd_soc_component *component)
+{
+ struct tac5x1x_priv *tac5x1x = snd_soc_component_get_drvdata(component);
+
+ /* Cancel any pending power management work before suspend */
+ cancel_delayed_work_sync(&tac5x1x->powerup_work);
+
+ return 0;
+}
+
+static int tac5x1x_soc_resume(struct snd_soc_component *component)
+{
+ return 0;
+}
+
+static const struct snd_soc_component_driver component_tac5x1x = {
+ .probe = tac5x1x_component_probe,
+ .set_bias_level = tac5x1x_set_bias_level,
+ .suspend = tac5x1x_soc_suspend,
+ .resume = tac5x1x_soc_resume,
+ .controls = taa5x1x_controls,
+ .num_controls = ARRAY_SIZE(taa5x1x_controls),
+ .dapm_widgets = taa5x1x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(taa5x1x_dapm_widgets),
+ .dapm_routes = taa5x1x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(taa5x1x_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 0,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver component_taa5x1x = {
+ .probe = tac5x1x_component_probe,
+ .set_bias_level = tac5x1x_set_bias_level,
+ .suspend = tac5x1x_soc_suspend,
+ .resume = tac5x1x_soc_resume,
+ .controls = taa5x1x_controls,
+ .num_controls = ARRAY_SIZE(taa5x1x_controls),
+ .dapm_widgets = taa5x1x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(taa5x1x_dapm_widgets),
+ .dapm_routes = taa5x1x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(taa5x1x_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 0,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_component_driver component_tad5x1x = {
+ .probe = tac5x1x_component_probe,
+ .set_bias_level = tac5x1x_set_bias_level,
+ .suspend = tac5x1x_soc_suspend,
+ .resume = tac5x1x_soc_resume,
+ .controls = tad5x1x_controls,
+ .num_controls = ARRAY_SIZE(tad5x1x_controls),
+ .dapm_widgets = tad5x1x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tad5x1x_dapm_widgets),
+ .dapm_routes = tad5x1x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tad5x1x_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 0,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int tac5x1x_probe(struct platform_device *pdev)
+{
+ s32 ret;
+ struct tac5x1x_priv *tac5x1x_priv;
+ struct device *dev = &pdev->dev;
+ struct i2c_client *i2c = to_i2c_client(dev->parent);
+ struct snd_soc_dai_driver *dai;
+
+ tac5x1x_priv = devm_kzalloc(&pdev->dev, sizeof(struct tac5x1x_priv),
+ GFP_KERNEL);
+ if (!tac5x1x_priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, tac5x1x_priv);
+ tac5x1x_priv->tac5x1x = dev_get_drvdata(pdev->dev.parent);
+
+ /* Allocate unique DAI for each codec instance to avoid naming conflicts */
+ dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ switch (tac5x1x_priv->tac5x1x->codec_type) {
+ case TAA5212:
+ case TAA5412:
+ /* Copy from TAA template */
+ memcpy(dai, &taa5x1x_dai, sizeof(*dai));
+ dai->name = devm_kasprintf(dev, GFP_KERNEL, "taa5x1x-hifi-%d-%02x",
+ i2c->adapter->nr, i2c->addr);
+ if (!dai->name)
+ return -ENOMEM;
+ ret = devm_snd_soc_register_component(dev, &component_taa5x1x,
+ dai, 1);
+ break;
+ case TAC5111:
+ case TAC5112:
+ case TAC5211:
+ case TAC5212:
+ case TAC5301:
+ case TAC5311:
+ case TAC5312:
+ case TAC5411:
+ case TAC5412:
+ /* Copy from TAC template */
+ memcpy(dai, &tac5x1x_dai, sizeof(*dai));
+ dai->name = devm_kasprintf(dev, GFP_KERNEL, "tac5x1x-hifi-%d-%02x",
+ i2c->adapter->nr,
+ i2c->addr);
+ if (!dai->name)
+ return -ENOMEM;
+ ret = devm_snd_soc_register_component(dev, &component_tac5x1x,
+ dai, 1);
+ break;
+ case TAD5112:
+ case TAD5212:
+ /* Copy from TAD template */
+ memcpy(dai, &tad5x1x_dai, sizeof(*dai));
+ dai->name = devm_kasprintf(dev, GFP_KERNEL, "tad5x1x-hifi-%d-%02x",
+ i2c->adapter->nr, i2c->addr);
+ if (!dai->name)
+ return -ENOMEM;
+ ret = devm_snd_soc_register_component(dev, &component_tad5x1x,
+ dai, 1);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret)
+ dev_err(dev, "Failed to register component\n");
+
+ return ret;
+}
+
+static void tac5x1x_remove(struct platform_device *pdev)
+{
+ struct tac5x1x_priv *tac5x1x_priv = platform_get_drvdata(pdev);
+
+ /* Cancel any pending power management work before removal */
+ cancel_delayed_work_sync(&tac5x1x_priv->powerup_work);
+}
+
+static struct platform_driver tac5x1x_codec_driver = {
+ .driver = {
+ .name = "tac5x1x-codec",
+ },
+ .probe = tac5x1x_probe,
+ .remove = tac5x1x_remove,
+};
+module_platform_driver(tac5x1x_codec_driver);
+
+MODULE_DESCRIPTION("ASoC tac5x1x driver");
+MODULE_AUTHOR("Niranjan H Y <niranjan.hy@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:tac5x1x-codec");
+MODULE_SOFTDEP("pre: tac5x1x-pinctrl");
diff --git a/sound/soc/codecs/tac5x1x.h b/sound/soc/codecs/tac5x1x.h
new file mode 100644
index 000000000000..c89a2fb54283
--- /dev/null
+++ b/sound/soc/codecs/tac5x1x.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Header file for tac5x1x codec driver
+ *
+ * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Author: Niranjan H Y <niranjan.hy@ti.com>
+ */
+#ifndef __TAC5X1X_H__
+
+#include <sound/pcm.h>
+#include <linux/mfd/tac5x1x/core.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+
+#define TAC5X1X_RATES SNDRV_PCM_RATE_8000_192000
+#define TAC5X1X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct tac5x1x_priv {
+ struct tac5x1x *tac5x1x;
+ struct snd_soc_component *component;
+ struct tac5x1x_irqinfo irqinfo;
+ u32 ch_enabled;
+ /* Flag to prevent duplicate power-up writes.
+ * Protected by ch_lock mutex. Used to ensure only one event
+ * (ADC or DAC) writes PWR_CFG during power-up sequence.
+ */
+ struct mutex ch_lock;
+ bool pwr_up_done;
+ struct delayed_work powerup_work;
+};
+
+#endif //__TAC5X1X_H__
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v1 8/8] ASoC: pcm6240: remove support for taac5x1x family
2026-03-12 18:48 [PATCH v1 0/8] ASoC: support TI's TAC5x1x audio codec family Niranjan H Y
` (6 preceding siblings ...)
2026-03-12 18:48 ` [PATCH v1 7/8] ASoC: tac5x1x: Add TI TAC5x1x codec driver Niranjan H Y
@ 2026-03-12 18:48 ` Niranjan H Y
7 siblings, 0 replies; 18+ messages in thread
From: Niranjan H Y @ 2026-03-12 18:48 UTC (permalink / raw)
To: linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, niranjan.hy, nb, navada, v-hampiholi, sandeepk,
baojun.xu, shenghao-ding
Remove TAC5X1X support from the legacy pcm6240 codec driver. The TAC5X1X
family now uses a dedicated MFD-based driver for better code organization
and maintainability.
Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
---
sound/soc/codecs/pcm6240.c | 126 +------------------------------------
sound/soc/codecs/pcm6240.h | 4 --
2 files changed, 3 insertions(+), 127 deletions(-)
diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c
index 78b21fbfad50..b1902b9e9f32 100644
--- a/sound/soc/codecs/pcm6240.c
+++ b/sound/soc/codecs/pcm6240.c
@@ -44,10 +44,6 @@ static const struct i2c_device_id pcmdevice_i2c_id[] = {
{ "pcmd3140", PCMD3140 },
{ "pcmd3180", PCMD3180 },
{ "pcmd512x", PCMD512X },
- { "taa5212", TAA5212 },
- { "taa5412", TAA5412 },
- { "tad5212", TAD5212 },
- { "tad5412", TAD5412 },
{}
};
MODULE_DEVICE_TABLE(i2c, pcmdevice_i2c_id);
@@ -442,60 +438,6 @@ static const struct pcmdevice_mixer_control pcmd3180_fine_gain_ctl[] = {
}
};
-static const struct pcmdevice_mixer_control taa5412_digi_vol_ctl[] = {
- {
- .shift = 0,
- .reg = TAA5412_REG_CH1_DIGITAL_VOLUME,
- .max = 0xff,
- .invert = 0,
- },
- {
- .shift = 0,
- .reg = TAA5412_REG_CH2_DIGITAL_VOLUME,
- .max = 0xff,
- .invert = 0,
- },
- {
- .shift = 0,
- .reg = TAA5412_REG_CH3_DIGITAL_VOLUME,
- .max = 0xff,
- .invert = 0,
- },
- {
- .shift = 0,
- .reg = TAA5412_REG_CH4_DIGITAL_VOLUME,
- .max = 0xff,
- .invert = 0,
- }
-};
-
-static const struct pcmdevice_mixer_control taa5412_fine_gain_ctl[] = {
- {
- .shift = 4,
- .reg = TAA5412_REG_CH1_FINE_GAIN,
- .max = 0xf,
- .invert = 0,
- },
- {
- .shift = 4,
- .reg = TAA5412_REG_CH2_FINE_GAIN,
- .max = 0xf,
- .invert = 0,
- },
- {
- .shift = 4,
- .reg = TAA5412_REG_CH3_FINE_GAIN,
- .max = 0xf,
- .invert = 4,
- },
- {
- .shift = 0,
- .reg = TAA5412_REG_CH4_FINE_GAIN,
- .max = 0xf,
- .invert = 4,
- }
-};
-
static const DECLARE_TLV_DB_MINMAX_MUTE(pcmd3140_dig_gain_tlv,
-10000, 2700);
static const DECLARE_TLV_DB_MINMAX_MUTE(pcm1690_fine_dig_gain_tlv,
@@ -510,9 +452,7 @@ static const DECLARE_TLV_DB_LINEAR(adc5120_chgain_tlv, 0, 4200);
static const DECLARE_TLV_DB_MINMAX_MUTE(pcm6260_fgain_tlv,
-10000, 2700);
static const DECLARE_TLV_DB_LINEAR(pcm6260_chgain_tlv, 0, 4200);
-static const DECLARE_TLV_DB_MINMAX_MUTE(taa5412_dig_vol_tlv,
- -8050, 4700);
-static const DECLARE_TLV_DB_LINEAR(taa5412_fine_gain_tlv,
+static const DECLARE_TLV_DB_LINEAR(pcmd31x0_fine_gain_tlv,
-80, 70);
static int pcmdev_change_dev(struct pcmdevice_priv *pcm_priv,
@@ -981,7 +921,7 @@ static const struct pcmdev_ctrl_info pcmdev_gain_ctl_info[][2] = {
// PCMD3140
{
{
- .gain = taa5412_fine_gain_tlv,
+ .gain = pcmd31x0_fine_gain_tlv,
.pcmdev_ctrl = pcmd3140_fine_gain_ctl,
.ctrl_array_size = ARRAY_SIZE(pcmd3140_fine_gain_ctl),
.get = pcmdevice_get_volsw,
@@ -1000,7 +940,7 @@ static const struct pcmdev_ctrl_info pcmdev_gain_ctl_info[][2] = {
// PCMD3180
{
{
- .gain = taa5412_fine_gain_tlv,
+ .gain = pcmd31x0_fine_gain_tlv,
.pcmdev_ctrl = pcmd3180_fine_gain_ctl,
.ctrl_array_size = ARRAY_SIZE(pcmd3180_fine_gain_ctl),
.get = pcmdevice_get_volsw,
@@ -1025,62 +965,6 @@ static const struct pcmdev_ctrl_info pcmdev_gain_ctl_info[][2] = {
.ctrl_array_size = 0,
},
},
- // TAA5212
- {
- {
- .gain = taa5412_fine_gain_tlv,
- .pcmdev_ctrl = taa5412_fine_gain_ctl,
- .ctrl_array_size = ARRAY_SIZE(taa5412_fine_gain_ctl),
- .get = pcmdevice_get_volsw,
- .put = pcmdevice_put_volsw,
- .pcmdev_ctrl_name_id = 2,
- },
- {
- .gain = taa5412_dig_vol_tlv,
- .pcmdev_ctrl = taa5412_digi_vol_ctl,
- .ctrl_array_size = ARRAY_SIZE(taa5412_digi_vol_ctl),
- .get = pcmdevice_get_volsw,
- .put = pcmdevice_put_volsw,
- .pcmdev_ctrl_name_id = 1,
- },
- },
- // TAA5412
- {
- {
- .gain = taa5412_fine_gain_tlv,
- .pcmdev_ctrl = taa5412_fine_gain_ctl,
- .ctrl_array_size = ARRAY_SIZE(taa5412_fine_gain_ctl),
- .get = pcmdevice_get_volsw,
- .put = pcmdevice_put_volsw,
- .pcmdev_ctrl_name_id = 2,
- },
- {
- .gain = taa5412_dig_vol_tlv,
- .pcmdev_ctrl = taa5412_digi_vol_ctl,
- .ctrl_array_size = ARRAY_SIZE(taa5412_digi_vol_ctl),
- .get = pcmdevice_get_volsw,
- .put = pcmdevice_put_volsw,
- .pcmdev_ctrl_name_id = 1,
- },
- },
- // TAD5212
- {
- {
- .ctrl_array_size = 0,
- },
- {
- .ctrl_array_size = 0,
- },
- },
- // TAD5412
- {
- {
- .ctrl_array_size = 0,
- },
- {
- .ctrl_array_size = 0,
- },
- },
};
static int pcmdev_dev_bulk_write(struct pcmdevice_priv *pcm_dev,
@@ -1998,10 +1882,6 @@ static const struct of_device_id pcmdevice_of_match[] = {
{ .compatible = "ti,pcmd3140" },
{ .compatible = "ti,pcmd3180" },
{ .compatible = "ti,pcmd512x" },
- { .compatible = "ti,taa5212" },
- { .compatible = "ti,taa5412" },
- { .compatible = "ti,tad5212" },
- { .compatible = "ti,tad5412" },
{},
};
MODULE_DEVICE_TABLE(of, pcmdevice_of_match);
diff --git a/sound/soc/codecs/pcm6240.h b/sound/soc/codecs/pcm6240.h
index 2d8f9e798139..86b1ef734a3d 100644
--- a/sound/soc/codecs/pcm6240.h
+++ b/sound/soc/codecs/pcm6240.h
@@ -33,10 +33,6 @@ enum pcm_device {
PCMD3140,
PCMD3180,
PCMD512X,
- TAA5212,
- TAA5412,
- TAD5212,
- TAD5412,
MAX_DEVICE,
};
--
2.34.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v1 7/8] ASoC: tac5x1x: Add TI TAC5x1x codec driver
2026-03-12 18:48 ` [PATCH v1 7/8] ASoC: tac5x1x: Add TI TAC5x1x codec driver Niranjan H Y
@ 2026-03-12 19:56 ` Mark Brown
0 siblings, 0 replies; 18+ messages in thread
From: Mark Brown @ 2026-03-12 19:56 UTC (permalink / raw)
To: Niranjan H Y
Cc: linux-kernel, linux-gpio, linux-sound, devicetree, lee, linusw,
lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt, nb, navada,
v-hampiholi, sandeepk, baojun.xu, shenghao-ding
[-- Attachment #1: Type: text/plain, Size: 1869 bytes --]
On Fri, Mar 13, 2026 at 12:18:32AM +0530, Niranjan H Y wrote:
> +/* ADC Analog source Selection */
> +static const char *const tac5x1x_input_analog_sel_text[] = {
> + "Differential",
> + "Single-ended",
> + "Single-ended mux INxP",
> + "Single-ended mux INxM",
> +};
> +
> +static const char *const tac5x1x_input_analog2_sel_text[] = {
> + "Differential",
> + "Single-ended",
> +};
Should these be configurd from DT given that they'll likely depend on
wiring (I know a lot of drivers, especially old ones) make them runtime
configurable)?
> +static s32 tac5x1x_add_ip_diag_controls(struct snd_soc_component *component)
> +{
> + struct tac5x1x_priv *tac5x1x_priv =
> + snd_soc_component_get_drvdata(component);
> + struct tac5x1x *tac5x1x = tac5x1x_priv->tac5x1x;
> + struct tac5x1x_input_diag_config *input_diag_config =
> + &tac5x1x_priv->tac5x1x->input_diag_config;
> + s32 ret = 0;
> +
> + switch (tac5x1x->codec_type) {
> + case TAA5212:
> + break;
> + case TAA5412:
> + case TAC5301:
> + case TAC5311:
> + case TAC5312:
> + case TAC5411:
> + case TAC5412:
> + if (input_diag_config->in_ch_en) {
> + ret = snd_soc_add_component_controls(component,
> + taa_ip_controls,
> + ARRAY_SIZE(taa_ip_controls));
These...
> +static s32 tac5x1x_add_controls(struct snd_soc_component *component)
> +{
> + case TAA5412:
> + ret =
> + snd_soc_add_component_controls(component, tolerance_ctrls,
> + ARRAY_SIZE(tolerance_ctrls));
> + if (ret)
> + return ret;
> + ret =
> + snd_soc_add_component_controls(component,
> + taa_ip_controls,
> + ARRAY_SIZE(taa_ip_controls));
..are also added unconditionally here which will fail due to duplciate
registration?
> +static int tac5x1x_soc_resume(struct snd_soc_component *component)
> +{
> + return 0;
> +}
Just delete the function if there's nothig to do.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v1 1/8] dt-bindings: mfd: Add bindings for TI TAC5x1x MFD core
2026-03-12 18:48 ` [PATCH v1 1/8] dt-bindings: mfd: Add bindings for TI TAC5x1x MFD core Niranjan H Y
@ 2026-03-13 20:43 ` Rob Herring
2026-03-14 10:13 ` Krzysztof Kozlowski
1 sibling, 0 replies; 18+ messages in thread
From: Rob Herring @ 2026-03-13 20:43 UTC (permalink / raw)
To: Niranjan H Y
Cc: linux-kernel, linux-gpio, linux-sound, devicetree, lee, linusw,
lgirdwood, broonie, perex, tiwai, krzk+dt, conor+dt, nb, navada,
v-hampiholi, sandeepk, baojun.xu, shenghao-ding
On Fri, Mar 13, 2026 at 12:18:26AM +0530, Niranjan H Y wrote:
> Add device tree bindings for the Texas Instruments TAC5x1x family of
> audio codecs with integrated GPIO controller, describing the MFD core
> device interface, power supplies, and clock configuration.
>
> Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
> ---
> .../devicetree/bindings/mfd/ti,tac5x1x.yaml | 247 ++++++++++++++++++
> 1 file changed, 247 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
This fails testing (not sure what happened to the email about it):
yamllint warnings/errors:
dtschema/dtc warnings/errors:
Warning: Duplicate compatible "ti,tad5212" found in schemas matching "$id":
http://devicetree.org/schemas/mfd/ti,tac5x1x.yaml
http://devicetree.org/schemas/sound/ti,pcm6240.yaml#
Warning: Duplicate compatible "ti,taa5412" found in schemas matching "$id":
http://devicetree.org/schemas/mfd/ti,tac5x1x.yaml
http://devicetree.org/schemas/sound/ti,pcm6240.yaml#
Warning: Duplicate compatible "ti,taa5212" found in schemas matching "$id":
http://devicetree.org/schemas/mfd/ti,tac5x1x.yaml
http://devicetree.org/schemas/sound/ti,pcm6240.yaml#
doc reference errors (make refcheckdocs):
Warning: Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml references a file that doesn't exist: Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
Warning: Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml references a file that doesn't exist: Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml
Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml: Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml: Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml
>
> diff --git a/Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml b/Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
> new file mode 100644
> index 000000000000..3d7943c0411f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
> @@ -0,0 +1,247 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mfd/ti,tac5x1x.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Texas Instruments TAC5x1x Multi-Function Audio Device
> +
> +maintainers:
> + - Niranjan H Y <niranjan.hy@ti.com>
> +
> +description: |
> + TAC5x1x family of low-power, high-performance audio codecs with integrated
> + GPIO controller and diagnostic capabilities.
> +
> + This is the parent binding. Child nodes are bound by these bindings:
> + - Pin controller: Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
> + - Audio codec: Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml
> +
> + Hardware features:
> + - Audio ADC/DAC with configurable impedance and voltage references
> + - 4-pin GPIO controller with alternate functions (PDM, IRQ)
> + - Analog voltage and current monitoring circuits
> + - Diagnostic fault detection with interrupt generation
> +
> + Device datasheets can be found at:
> + TAA5212: https://www.ti.com/lit/ds/symlink/taa5212.pdf
> + TAA5412-Q1: https://www.ti.com/lit/ds/symlink/taa5412-q1.pdf
> + TAC5111: https://www.ti.com/lit/ds/symlink/tac5111.pdf
> + TAC5112: https://www.ti.com/lit/ds/symlink/tac5112.pdf
> + TAC5211: https://www.ti.com/lit/ds/symlink/tac5211.pdf
> + TAC5212: https://www.ti.com/lit/ds/symlink/tac5212.pdf
> + TAC5301: https://www.ti.com/lit/ds/symlink/tac5301-q1.pdf
> + TAC5311-Q1: https://www.ti.com/lit/ds/symlink/tac5311-q1.pdf
> + TAC5312-Q1: https://www.ti.com/lit/ds/symlink/tac5312-q1.pdf
> + TAC5411-Q1: https://www.ti.com/lit/ds/symlink/tac5411-q1.pdf
> + TAC5412-Q1: https://www.ti.com/lit/ds/symlink/tac5412-q1.pdf
> + TAD5112: https://www.ti.com/lit/ds/symlink/tad5112.pdf
> + TAD5212: https://www.ti.com/lit/ds/symlink/tad5212.pdf
> +
> +properties:
> + compatible:
> + enum:
> + - ti,taa5212
> + - ti,taa5412
> + - ti,tac5111
> + - ti,tac5112
> + - ti,tac5211
> + - ti,tac5212
> + - ti,tac5301
> + - ti,tac5311
> + - ti,tac5312
> + - ti,tac5411
> + - ti,tac5412
> + - ti,tad5112
> + - ti,tad5212
> +
> + reg:
> + maxItems: 1
> + description: I2C slave address
> +
> + reset-gpios:
> + maxItems: 1
> + description: Hardware reset control pin (active low)
> +
> + interrupts:
> + maxItems: 1
> + description: |
> + Interrupt from device diagnostic circuits to host processor.
> + Generated on voltage/current fault conditions and other diagnostic events.
> +
> + clocks:
> + maxItems: 1
> + description: Master clock input (MCLK)
> +
> + clock-names:
> + items:
> + - const: mclk
> +
> + avdd-supply:
> + description: |
> + Analog supply voltage input (AVDD pin).
> + Typical voltages: 1.8V, 3.0V, 3.3V, 5.0V
> +
> + iovdd-supply:
> + description: |
> + Digital I/O supply voltage input (IOVDD pin).
> + Typical voltages: 1.8V, 3.3V
> +
> + ti,vref-voltage:
Use unit suffixes for things with units.
> + description: |
> + Internal voltage reference setting in microvolts.
> +
> + Supported values:
> + - 1375000: VREF = 1.375V
> + - 2500000: VREF = 2.5V
> + - 2750000: VREF = 2.75V
> +
> + Constraint: Selected VREF must be lower than AVDD supply voltage.
> + $ref: /schemas/types.yaml#/definitions/uint32
> + enum: [1375000, 2500000, 2750000]
> + default: 2500000
> +
> + ti,micbias-voltage:
> + description: |
> + Microphone bias output voltage in microvolts.
> +
> + Configuration options:
> + - VREF voltage: Set to same value as ti,vref-voltage
> + - 0.5 × VREF: Set to half of ti,vref-voltage value
> + - AVDD voltage: Set to AVDD supply voltage value
> +
> + Note: When set to AVDD voltage, hardware automatically overrides
> + VREF setting to 2.75V regardless of ti,vref-voltage property.
> + $ref: /schemas/types.yaml#/definitions/uint32
> + minimum: 687500
> + maximum: 3300000
> +
> + ti,adc1-impedance:
Same here.
Though maybe existing bindings are using these same properties. That's
fine then, but please state that at least. Really we should have a
single, common definition for them.
> + description: |
> + ADC Channel 1 input impedance in Ohms.
> + Supported impedance values:
> + - 5000: 5k input impedance
> + - 10000: 10k input impedance
> + - 40000: 40k input impedance
> + Available only for TAC5111, TAC5211, TAC5212, and TAA5212 variants.
> + $ref: /schemas/types.yaml#/definitions/uint32
> + enum: [5000, 10000, 40000]
> + default: 10000
> +
> + ti,adc2-impedance:
> + description: |
> + ADC Channel 2 input impedance in Ohms.
> + Supported impedance values:
> + - 5000: 5k input impedance
> + - 10000: 10k input impedance
> + - 40000: 40k input impedance
> + Available on stereo variants only (TAA5212, TAC5212).
> + $ref: /schemas/types.yaml#/definitions/uint32
> + enum: [5000, 10000, 40000]
> + default: 10000
> +
> + ti,out2x-vcom-cfg:
> + description: |
> + Channel OUT2x VCOM reference voltage selection.
> +
> + Configuration options:
> + - 0: VCOM = 0.6 × VREF
> + - 1: VCOM = AVDD / 2
> +
> + Available on devices with stereo DAC output.
> + $ref: /schemas/types.yaml#/definitions/uint32
> + enum: [0, 1]
> + default: 0
> +
> + ti,gpa-threshold:
> + description: |
> + General Purpose Analog voltage monitoring thresholds in millivolts.
> + Format: [low_threshold_mv, high_threshold_mv]
> +
> + Monitoring range: 0mV to 6000mV, Resolution: ~24mV per step
> + Default thresholds: 200mV (low), 2600mV (high)
> +
> + Generates interrupt on voltage fault conditions.
> + $ref: /schemas/types.yaml#/definitions/uint32-array
> + minItems: 2
> + maxItems: 2
> + items:
> + minimum: 200
> + maximum: 6000
> + default: [200, 2600]
> +
> + ti,in-ch-en:
> + description: |
> + Enable input channel diagnostic monitoring circuits.
> +
> + When enabled (1), activates hardware monitoring for:
> + - Input channel fault detection
> + - Micbias current monitoring (if ti,micbias-threshold configured)
> + - Input overvoltage detection
> +
> + Available on: TAC5301, TAC5311, TAC5312, TAC5411, TAC5412, TAA5412
> + $ref: /schemas/types.yaml#/definitions/uint32
> + enum: [0, 1]
> + default: 0
> +
> + ti,out-ch-en:
> + description: |
> + Enable output channel diagnostic monitoring circuits.
> +
> + When enabled (1), activates hardware monitoring for:
> + - Output channel fault detection
> + - Driver fault monitoring
> + - Short circuit detection
> +
> + Available on: TAC5301, TAC5311, TAC5312, TAC5411, TAC5412, TAA5412
> + $ref: /schemas/types.yaml#/definitions/uint32
> + enum: [0, 1]
> + default: 0
> +
> + ti,incl-se-inm:
> + description: |
> + Include INxM pins in single-ended input diagnostic scan.
> +
> + When enabled (1), includes negative input pins (INxM) in diagnostic
> + monitoring for single-ended input configurations.
> + $ref: /schemas/types.yaml#/definitions/uint32
> + enum: [0, 1]
> + default: 0
> +
> + ti,incl-ac-coup:
> + description: |
> + Include AC-coupled input channels in diagnostic scan.
> +
> + When enabled (1), includes AC-coupled input channels in the
> + diagnostic monitoring system.
> + $ref: /schemas/types.yaml#/definitions/uint32
> + enum: [0, 1]
> + default: 0
> +
> + ti,micbias-threshold:
> + description: |
> + Microphone bias current monitoring thresholds in microamps.
> + Format: [low_threshold_ua, high_threshold_ua]
> +
> + Current monitoring range: 100uA to 33000uA, Resolution: ~132uA per step
> + Default thresholds: 2600uA (low), 18000uA (high)
> +
> + Hardware monitors actual current flow through MICBIAS pin.
> + Generates interrupts on fault conditions when ti,in-ch-en = <1>.
> +
> + Available on: TAC5301, TAC5311, TAC5312, TAC5411, TAC5412, TAA5412
> + $ref: /schemas/types.yaml#/definitions/uint32-array
> + minItems: 2
> + maxItems: 2
> + items:
> + minimum: 100
> + maximum: 33000
> + default: [2600, 18000]
> +
> +required:
> + - compatible
> + - reg
> + - avdd-supply
> + - iovdd-supply
> +
> +unevaluatedProperties: false
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v1 2/8] dt-bindings: pinctrl: Add bindings for TI TAC5x1x pinctrl
2026-03-12 18:48 ` [PATCH v1 2/8] dt-bindings: pinctrl: Add bindings for TI TAC5x1x pinctrl Niranjan H Y
@ 2026-03-14 10:10 ` Krzysztof Kozlowski
2026-03-14 10:12 ` Krzysztof Kozlowski
1 sibling, 0 replies; 18+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-14 10:10 UTC (permalink / raw)
To: Niranjan H Y
Cc: linux-kernel, linux-gpio, linux-sound, devicetree, lee, linusw,
lgirdwood, broonie, perex, tiwai, robh, krzk+dt, conor+dt, nb,
navada, v-hampiholi, sandeepk, baojun.xu, shenghao-ding
On Fri, Mar 13, 2026 at 12:18:27AM +0530, Niranjan H Y wrote:
> Add device tree bindings for the Texas Instruments TAC5x1x family
> pin controller. These bindings define the GPIO and pin control
> configuration interface for the device.
A nit, subject: drop second/last, redundant "bindings for". The
"dt-bindings" prefix is already stating that these are bindings.
See also:
https://elixir.bootlin.com/linux/v6.17-rc3/source/Documentation/devicetree/bindings/submitting-patches.rst#L18
>
> Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
> ---
> .../bindings/pinctrl/ti,tac5x1x-pinctrl.yaml | 163 ++++++++++++++++++
> include/dt-bindings/pinctrl/tac5x1x.h | 44 +++++
> 2 files changed, 207 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
> create mode 100644 include/dt-bindings/pinctrl/tac5x1x.h
>
> diff --git a/Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
> new file mode 100644
> index 000000000000..3ccb262d6247
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
> @@ -0,0 +1,163 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/pinctrl/ti,tac5x1x-pinctrl.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: TI TAC5x1x Pin Controller
> +
> +maintainers:
> + - Niranjan H Y <niranjan.hy@ti.com>
> +
> +description: |
> + The TAC5x1x devices have 5 configurable pins that can be used for GPIO
> + or alternate functions like PDM (Pulse Density Modulation) and interrupt
> + generation. A subset of pins can be present in any variant of the HW.
> +
> + This binding is used as a child node of the main TAC5x1x MFD device
> + described in Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
> +
> + Pin capabilities:
> + - GPIO1, GPIO2: Bidirectional, configurable as GPIO, PDM clock, or IRQ output
> + - GPO1: Output only, configurable as GPIO, PDM clock, or IRQ output.
> + Some variants use different name GPO1A.
> + - GPI1: Input only, configurable as GPIO or PDM data input
> + Some variants use different name GPI1A.
> + - GPI2A: Input only, configurable as GPIO or PDM data input
> +
> +properties:
> + compatible:
> + const: ti,tac5x1x-pinctrl
No wildcards in compatibles and filenames.
> +
> + gpio-controller: true
> +
> + '#gpio-cells':
> + const: 2
> + description: |
> + First cell is the pin number (0-4 corresponding to GPIO1, GPIO2, GPO1, GPI1, GPI2A)
> + Second cell is flags (GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW)
> +
> + gpio-ranges:
> + maxItems: 1
> + description: GPIO range mapping to pin controller
> +
> + gpio-line-names:
> + minItems: 1
> + maxItems: 5
> + description: Names for the GPIO lines
> +
> +patternProperties:
> + '-state$':
> + type: object
> + description: Pin configuration state
> + $ref: /schemas/pinctrl/pinmux-node.yaml#
> +
> + patternProperties:
> + '^.*$':
-group$ or -grp$
> + type: object
> + $ref: /schemas/pinctrl/pincfg-node.yaml#
> +
> + properties:
> + groups:
> + description: Pin groups to configure
> + items:
> + enum:
> + - gpio1
> + - gpio2
> + - gpo1
> + - gpi1
> + - gpi2a
> + - pdm_gpio1_gpio2
> + - pdm_gpio1_gpi1
> + - pdm_gpio1_gpi2a
> + - pdm_gpio2_gpio1
> + - pdm_gpio2_gpi1
> + - pdm_gpio2_gpi2a
> + - pdm_gpo1_gpio1
> + - pdm_gpo1_gpio2
> + - pdm_gpo1_gpi1
> + - pdm_gpo1_gpi2a
> +
> + function:
> + description: Function to assign
> + enum:
> + - gpio
> + - pdm
> + - irq
> +
> + drive-push-pull:
> + type: boolean
> + description: Enable push-pull drive mode
> +
> + drive-open-drain:
> + type: boolean
> + description: Enable open-drain drive mode
> +
> + drive-open-source:
> + type: boolean
> + description: Enable open-source drive mode
> +
> + bias-pull-up:
> + type: boolean
> + description: Enable pull-up bias
> +
> + bias-pull-down:
> + type: boolean
> + description: Enable pull-down bias
> +
> + bias-high-impedance:
> + type: boolean
> + description: Set pin to high impedance
> +
> + input-enable:
> + type: boolean
> + description: Enable input buffer
> +
> + output-enable:
> + type: boolean
> + description: Enable output buffer
Drop all descriptions and type. common schema defines them. You only
need :true if none of other properties are applicable here.
> +
> + required:
> + - groups
> + - function
> +
> + additionalProperties: false
> +
> + additionalProperties: false
> +
> +required:
> + - compatible
> + - gpio-controller
> + - '#gpio-cells'
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + pinctrl {
> + compatible = "ti,tac5x1x-pinctrl";
> + gpio-controller;
> + #gpio-cells = <2>;
> + gpio-ranges = <&pinctrl 0 0 5>;
> + gpio-line-names = "GPIO1", "GPIO2", "GPO1", "GPI1", "GPI2A";
> +
> + default_state: default-state {
> + pdm_config {
> + groups = "pdm_gpo1_gpi1";
> + function = "pdm";
> + drive-push-pull;
> + };
> +
> + gpio_config {
> + groups = "gpio1", "gpio2";
> + function = "gpio";
> + bias-pull-up;
> + };
> +
> + irq_config {
> + groups = "gpo1";
> + function = "irq";
> + drive-open-drain;
> + };
> + };
> + };
> diff --git a/include/dt-bindings/pinctrl/tac5x1x.h b/include/dt-bindings/pinctrl/tac5x1x.h
> new file mode 100644
> index 000000000000..8cc3fa0b7946
> --- /dev/null
> +++ b/include/dt-bindings/pinctrl/tac5x1x.h
Header always match bindings filename.
> @@ -0,0 +1,44 @@
> +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
> +/*
> + * Device Tree binding constants for TAC5x1x pinctrl
> + *
> + * Copyright (C) 2025 Texas Instruments Incorporated
> + * Author: Niranjan H Y <niranjan.hy@ti.com>
> + */
> +
> +#ifndef _DT_BINDINGS_PINCTRL_TAC5X1X_H
> +#define _DT_BINDINGS_PINCTRL_TAC5X1X_H
> +
> +/* Pin IDs */
> +#define TAC5X1X_PIN_GPIO1 0
> +#define TAC5X1X_PIN_GPIO2 1
> +#define TAC5X1X_PIN_GPO1 2
> +#define TAC5X1X_PIN_GPI1 3
> +#define TAC5X1X_PIN_GPI2A 4
Since when GPIOs are bindings?
> +
> +/* Pin functions */
> +#define TAC5X1X_FUNC_GPIO 0
> +#define TAC5X1X_FUNC_PDM 1
> +#define TAC5X1X_FUNC_IRQ 2
No, pin function is a string.
> +
> +/* Pin drive modes */
> +#define TAC5X1X_DRIVE_HIZ 0
> +#define TAC5X1X_DRIVE_PUSH_PULL 1
> +#define TAC5X1X_DRIVE_PULL_DOWN 2
> +#define TAC5X1X_DRIVE_OPEN_DRAIN 3
> +#define TAC5X1X_DRIVE_PULL_UP 4
> +#define TAC5X1X_DRIVE_OPEN_SOURCE 5
Are you sure this patch was extensively reviewed internally?
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v1 6/8] pinctrl: pinctrl-tac5x1x: Add TI TAC5x1x pinctrl driver
2026-03-12 18:48 ` [PATCH v1 6/8] pinctrl: pinctrl-tac5x1x: Add TI TAC5x1x pinctrl driver Niranjan H Y
@ 2026-03-14 10:11 ` Krzysztof Kozlowski
0 siblings, 0 replies; 18+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-14 10:11 UTC (permalink / raw)
To: Niranjan H Y
Cc: linux-kernel, linux-gpio, linux-sound, devicetree, lee, linusw,
lgirdwood, broonie, perex, tiwai, robh, krzk+dt, conor+dt, nb,
navada, v-hampiholi, sandeepk, baojun.xu, shenghao-ding
On Fri, Mar 13, 2026 at 12:18:31AM +0530, Niranjan H Y wrote:
> Add the Texas Instruments TAC5x1x pin controller driver. This driver
> provides GPIO control for 5 configurable pins (GPIO1, GPIO2, GPO1, GPI1,
> GPI2A) with support for GPIO, PDM, and IRQ functions.
>
> The pinctrl driver handles:
> - GPIO input/output operations
> - Pin multiplexing between GPIO, PDM, and IRQ functions
>
> Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
> ---
> drivers/pinctrl/Kconfig | 11 +
> drivers/pinctrl/Makefile | 1 +
> drivers/pinctrl/pinctrl-tac5x1x.c | 889 ++++++++++++++++++++++++++++++
> 3 files changed, 901 insertions(+)
> create mode 100644 drivers/pinctrl/pinctrl-tac5x1x.c
>
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index afecd9407f53..2054e9998880 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -585,6 +585,17 @@ config PINCTRL_SX150X
> - 8 bits: sx1508q, sx1502q
> - 16 bits: sx1509q, sx1506q
>
> +config PINCTRL_TI_TAC5X1X
> + tristate "TAC5X1X pin control driver"
> + depends on OF && (MFD_TAC5X1X || COMPILE_TEST)
> + select GENERIC_PINCTRL_GROUPS
> + select GENERIC_PINMUX_FUNCTIONS
> + select GENERIC_PINCONF
> + help
> + TAC5X1X family may have 1 or more configurable GPIO pins
> + which can be grouped and configured to function as PDM, GPIOs
> + or interrupt outputs.
> +
> config PINCTRL_TB10X
> bool "Pinctrl for TB10X" if COMPILE_TEST
> depends on OF && ARC_PLAT_TB10X || COMPILE_TEST
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index f7d5d5f76d0c..1fe6b0e8a666 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -58,6 +58,7 @@ obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
> obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
> obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o
> obj-$(CONFIG_PINCTRL_SX150X) += pinctrl-sx150x.o
> +obj-$(CONFIG_PINCTRL_TI_TAC5X1X)+= pinctrl-tac5x1x.o
> obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o
> obj-$(CONFIG_PINCTRL_TPS6594) += pinctrl-tps6594.o
> obj-$(CONFIG_PINCTRL_TH1520) += pinctrl-th1520.o
> diff --git a/drivers/pinctrl/pinctrl-tac5x1x.c b/drivers/pinctrl/pinctrl-tac5x1x.c
> new file mode 100644
> index 000000000000..4e59501f3f78
> --- /dev/null
> +++ b/drivers/pinctrl/pinctrl-tac5x1x.c
> @@ -0,0 +1,889 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * TAC5X1X Pinctrl and GPIO driver
> + *
> + * Copyright (C) 2023-2025 Texas Instruments Incorporated - https://www.ti.com
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bits.h>
> +#include <linux/bitfield.h>
> +#include <linux/build_bug.h>
> +#include <linux/err.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/mfd/tac5x1x/registers.h>
> +#include <linux/mfd/tac5x1x/core.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/string_choices.h>
> +
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinconf-generic.h>
> +#include <linux/pinctrl/pinmux.h>
So where is the include for bindings? How do you use them if you claim
these are bindings?
> +
> +#include "pinctrl-utils.h"
> +
> +/* 2 pins can be gpio */
> +#define TAC5X1X_NUM_GPIO_PINS 5
> +#define TAC5X1X_MAX_PINS 5
...
> +
> +static const struct platform_device_id tac5x1x_pin_id_table[] = {
> + { "tac5x1x-pinctrl", },
> + {}
> +};
> +MODULE_DEVICE_TABLE(platform, tac5x1x_pin_id_table);
> +
> +static struct platform_driver tac5x1x_pin_driver = {
> + .driver = {
> + .name = "tac5x1x-pinctrl",
> + },
> + .probe = tac5x1x_pin_probe,
> + .id_table = tac5x1x_pin_id_table,
What is the point of compatible which is not used. That's another
completely unused thing from your bindings.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v1 2/8] dt-bindings: pinctrl: Add bindings for TI TAC5x1x pinctrl
2026-03-12 18:48 ` [PATCH v1 2/8] dt-bindings: pinctrl: Add bindings for TI TAC5x1x pinctrl Niranjan H Y
2026-03-14 10:10 ` Krzysztof Kozlowski
@ 2026-03-14 10:12 ` Krzysztof Kozlowski
1 sibling, 0 replies; 18+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-14 10:12 UTC (permalink / raw)
To: Niranjan H Y, linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, nb, navada, v-hampiholi, sandeepk, baojun.xu,
shenghao-ding
On 12/03/2026 19:48, Niranjan H Y wrote:
> +---
> +$id: http://devicetree.org/schemas/pinctrl/ti,tac5x1x-pinctrl.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: TI TAC5x1x Pin Controller
> +
> +maintainers:
> + - Niranjan H Y <niranjan.hy@ti.com>
> +
> +description: |
> + The TAC5x1x devices have 5 configurable pins that can be used for GPIO
> + or alternate functions like PDM (Pulse Density Modulation) and interrupt
> + generation. A subset of pins can be present in any variant of the HW.
> +
> + This binding is used as a child node of the main TAC5x1x MFD device
> + described in Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
> +
> + Pin capabilities:
> + - GPIO1, GPIO2: Bidirectional, configurable as GPIO, PDM clock, or IRQ output
> + - GPO1: Output only, configurable as GPIO, PDM clock, or IRQ output.
> + Some variants use different name GPO1A.
> + - GPI1: Input only, configurable as GPIO or PDM data input
> + Some variants use different name GPI1A.
> + - GPI2A: Input only, configurable as GPIO or PDM data input
> +
> +properties:
> + compatible:
> + const: ti,tac5x1x-pinctrl
As your code points out - drop, completely unused.
...
> + };
> diff --git a/include/dt-bindings/pinctrl/tac5x1x.h b/include/dt-bindings/pinctrl/tac5x1x.h
Also drop - not used.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v1 1/8] dt-bindings: mfd: Add bindings for TI TAC5x1x MFD core
2026-03-12 18:48 ` [PATCH v1 1/8] dt-bindings: mfd: Add bindings for TI TAC5x1x MFD core Niranjan H Y
2026-03-13 20:43 ` Rob Herring
@ 2026-03-14 10:13 ` Krzysztof Kozlowski
1 sibling, 0 replies; 18+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-14 10:13 UTC (permalink / raw)
To: Niranjan H Y, linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, nb, navada, v-hampiholi, sandeepk, baojun.xu,
shenghao-ding
On 12/03/2026 19:48, Niranjan H Y wrote:
> Add device tree bindings for the Texas Instruments TAC5x1x family of
> audio codecs with integrated GPIO controller, describing the MFD core
> device interface, power supplies, and clock configuration.
>
> Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
> ---
> .../devicetree/bindings/mfd/ti,tac5x1x.yaml | 247 ++++++++++++++++++
> 1 file changed, 247 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
>
> diff --git a/Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml b/Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
> new file mode 100644
> index 000000000000..3d7943c0411f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
> @@ -0,0 +1,247 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mfd/ti,tac5x1x.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Texas Instruments TAC5x1x Multi-Function Audio Device
> +
> +maintainers:
> + - Niranjan H Y <niranjan.hy@ti.com>
> +
> +description: |
> + TAC5x1x family of low-power, high-performance audio codecs with integrated
> + GPIO controller and diagnostic capabilities.
> +
> + This is the parent binding. Child nodes are bound by these bindings:
> + - Pin controller: Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
> + - Audio codec: Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml
> +
> + Hardware features:
> + - Audio ADC/DAC with configurable impedance and voltage references
> + - 4-pin GPIO controller with alternate functions (PDM, IRQ)
> + - Analog voltage and current monitoring circuits
> + - Diagnostic fault detection with interrupt generation
> +
> + Device datasheets can be found at:
> + TAA5212: https://www.ti.com/lit/ds/symlink/taa5212.pdf
> + TAA5412-Q1: https://www.ti.com/lit/ds/symlink/taa5412-q1.pdf
> + TAC5111: https://www.ti.com/lit/ds/symlink/tac5111.pdf
> + TAC5112: https://www.ti.com/lit/ds/symlink/tac5112.pdf
> + TAC5211: https://www.ti.com/lit/ds/symlink/tac5211.pdf
> + TAC5212: https://www.ti.com/lit/ds/symlink/tac5212.pdf
> + TAC5301: https://www.ti.com/lit/ds/symlink/tac5301-q1.pdf
> + TAC5311-Q1: https://www.ti.com/lit/ds/symlink/tac5311-q1.pdf
> + TAC5312-Q1: https://www.ti.com/lit/ds/symlink/tac5312-q1.pdf
> + TAC5411-Q1: https://www.ti.com/lit/ds/symlink/tac5411-q1.pdf
> + TAC5412-Q1: https://www.ti.com/lit/ds/symlink/tac5412-q1.pdf
> + TAD5112: https://www.ti.com/lit/ds/symlink/tad5112.pdf
> + TAD5212: https://www.ti.com/lit/ds/symlink/tad5212.pdf
> +
> +properties:
> + compatible:
> + enum:
> + - ti,taa5212
> + - ti,taa5412
> + - ti,tac5111
> + - ti,tac5112
> + - ti,tac5211
> + - ti,tac5212
> + - ti,tac5301
> + - ti,tac5311
> + - ti,tac5312
> + - ti,tac5411
> + - ti,tac5412
> + - ti,tad5112
> + - ti,tad5212
> +
> + reg:
> + maxItems: 1
> + description: I2C slave address
> +
> + reset-gpios:
> + maxItems: 1
> + description: Hardware reset control pin (active low)
> +
> + interrupts:
> + maxItems: 1
> + description: |
> + Interrupt from device diagnostic circuits to host processor.
> + Generated on voltage/current fault conditions and other diagnostic events.
> +
> + clocks:
> + maxItems: 1
> + description: Master clock input (MCLK)
> +
> + clock-names:
> + items:
> + - const: mclk
> +
> + avdd-supply:
> + description: |
> + Analog supply voltage input (AVDD pin).
> + Typical voltages: 1.8V, 3.0V, 3.3V, 5.0V
> +
> + iovdd-supply:
> + description: |
> + Digital I/O supply voltage input (IOVDD pin).
> + Typical voltages: 1.8V, 3.3V
> +
> + ti,vref-voltage:
NAK
This was repeated multiple times, was mentioned in my
talks/presentations, is explicitly documented in kernel docs.
This posting has poor quality. First go via internal review because I do
not believe such code was reviewed earlier.
...
> + default: [2600, 18000]
> +
> +required:
> + - compatible
> + - reg
> + - avdd-supply
> + - iovdd-supply
> +
> +unevaluatedProperties: false
You never tested this binding. You cannot have one without example.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v1 3/8] dt-bindings: sound: Add bindings for TI TAC5x1x codec
2026-03-12 18:48 ` [PATCH v1 3/8] dt-bindings: sound: Add bindings for TI TAC5x1x codec Niranjan H Y
@ 2026-03-14 10:14 ` Krzysztof Kozlowski
0 siblings, 0 replies; 18+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-14 10:14 UTC (permalink / raw)
To: Niranjan H Y, linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, nb, navada, v-hampiholi, sandeepk, baojun.xu,
shenghao-ding
On 12/03/2026 19:48, Niranjan H Y wrote:
> Add device tree bindings for the Texas Instruments TAC5x1x family
> audio codec. These bindings define the ALSA audio interface and
> regulator configuration.
>
> Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
> ---
> .../devicetree/bindings/sound/ti,tac5x1x.yaml | 49 +++++++++++++++++++
> 1 file changed, 49 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml
>
Same comments, same problems. Read submitting patches for DT and other
docs mentioned there and carefully follow each rule.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v1 4/8] dt-bindings: sound: Update ti,pcm6240.yaml to remove TAC5x1x family
2026-03-12 18:48 ` [PATCH v1 4/8] dt-bindings: sound: Update ti,pcm6240.yaml to remove TAC5x1x family Niranjan H Y
@ 2026-03-14 10:15 ` Krzysztof Kozlowski
0 siblings, 0 replies; 18+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-14 10:15 UTC (permalink / raw)
To: Niranjan H Y, linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, nb, navada, v-hampiholi, sandeepk, baojun.xu,
shenghao-ding
On 12/03/2026 19:48, Niranjan H Y wrote:
> Remove TAC5x1x family references from the pcm6240 device tree bindings.
> The TAC5x1x family (taa5212, taa5412, tad5212, tad5412) now uses a
> dedicated MFD-based driver with its own device tree bindings defined in:
> - Documentation/devicetree/bindings/mfd/ti,tac5x1x.yaml
> - Documentation/devicetree/bindings/pinctrl/ti,tac5x1x-pinctrl.yaml
> - Documentation/devicetree/bindings/sound/ti,tac5x1x.yaml
NAK, because taa5212 is not TAC5x1x.
Plus, what your drivers are supporting does not matter.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v1 5/8] mfd: tac5x1x: Add TI TAC5x1x MFD core driver
2026-03-12 18:48 ` [PATCH v1 5/8] mfd: tac5x1x: Add TI TAC5x1x MFD core driver Niranjan H Y
@ 2026-03-14 10:20 ` Krzysztof Kozlowski
0 siblings, 0 replies; 18+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-14 10:20 UTC (permalink / raw)
To: Niranjan H Y, linux-kernel, linux-gpio, linux-sound, devicetree
Cc: lee, linusw, lgirdwood, broonie, perex, tiwai, robh, krzk+dt,
conor+dt, nb, navada, v-hampiholi, sandeepk, baojun.xu,
shenghao-ding
On 12/03/2026 19:48, Niranjan H Y wrote:
> This patch adds the core Multi-Function Device (MFD) driver for the Texas
This patchset has multiple basic mistakes, including but not limited to:
1. Not following current kernel style. What does the submitting patches
say about such commit msg?
2. Repeating known old issues which we already fixed. Start from scratch
from recent drivers.
> Instruments TAC5x1x family of audio codecs. The MFD core handles device
> initialization, power management, and common resources for child devices.
>
> +
> +static int tac5x1x_setup_regulators(struct device *dev,
> + struct tac5x1x *tac5x1x)
> +{
> + int i, ret;
> +
> + for (i = 0; i < TAC5X1X_NUM_SUPPLIES; i++)
> + tac5x1x->supplies[i].supply = tac5x1x_supply_names[i];
> +
> + ret = devm_regulator_bulk_get(dev, TAC5X1X_NUM_SUPPLIES,
> + tac5x1x->supplies);
> + if (ret) {
> + dev_err(dev, "Failed to get regulators: %d\n", ret);
Look how all drivers are doing this - dev_err_probe
> + return ret;
> + }
> +
> + /* Regulators managed by PM runtime during probe */
> + tac5x1x->regulators_enabled = false;
> +
> + return 0;
> +}
> +
> +static int tac5x1x_probe(struct device *dev, struct tac5x1x *tac5x1x)
> +{
> + struct device_node *np = dev->of_node;
> + int ret;
> +
> + ret = tac5x1x_setup_regulators(dev, tac5x1x);
> + if (ret)
> + return ret;
> +
> + /* Initialize PM runtime before adding child devices */
> + pm_runtime_set_autosuspend_delay(dev, 3000);
> + pm_runtime_use_autosuspend(dev);
> + pm_runtime_set_active(dev);
> + pm_runtime_mark_last_busy(dev);
> + ret = devm_pm_runtime_enable(dev);
> + if (ret)
> + return ret;
> +
> + /* Enable regulators for device initialization */
> + ret = regulator_bulk_enable(TAC5X1X_NUM_SUPPLIES, tac5x1x->supplies);
> + if (ret) {
> + dev_err(dev, "Failed to enable regulators: %d\n", ret);
> + return ret;
> + }
> + tac5x1x->regulators_enabled = true;
> +
> + ret = tac5x1x_reset(tac5x1x);
> + if (ret) {
> + dev_err(dev, "Failed to reset device\n");
> + goto err_disable_regulators;
> + }
> + tac5x1x_init(tac5x1x);
> +
> + ret = tac5x1x_parse_dt(tac5x1x, np);
> + if (ret) {
> + dev_err(dev, "Failed to parse DT node: %d\n", ret);
> + goto err_disable_regulators;
> + }
> +
> + /* update if vcom property is found */
> + if (tac5x1x->out2x_vcom_cfg != -1) {
> + regmap_update_bits(tac5x1x->regmap, TAC5X1X_OUT2CFG0,
> + TAC5X1X_OUT2CFG0_VCOM_MASK,
> + tac5x1x->out2x_vcom_cfg);
> + }
> +
> + dev_dbg(dev, "%s adding mfds\n", __func__);
Drop, useless debug.
> +
> + /* Add child devices now PM runtime is initialized */
> + ret = mfd_add_devices(dev, PLATFORM_DEVID_NONE, tac5x1x_mfd_devs,
> + ARRAY_SIZE(tac5x1x_mfd_devs), NULL, 0, NULL);
> + if (ret) {
> + dev_err(dev, "Failed to add mfd devices\n");
> + goto err_remove_mfd;
> + }
> +
> + return 0;
> +
> +err_remove_mfd:
> + mfd_remove_devices(dev);
> +err_disable_regulators:
> + if (tac5x1x->regulators_enabled) {
> + regulator_bulk_disable(TAC5X1X_NUM_SUPPLIES, tac5x1x->supplies);
> + tac5x1x->regulators_enabled = false;
> + }
> + return ret;
> +}
> +
> +static void tac5x1x_remove(struct tac5x1x *tac5x1x)
> +{
> + mfd_remove_devices(tac5x1x->dev);
> + /* Only disable regulators if they are still enabled */
> + if (tac5x1x->regulators_enabled) {
> + regulator_bulk_disable(TAC5X1X_NUM_SUPPLIES, tac5x1x->supplies);
> + tac5x1x->regulators_enabled = false;
Nope, you are not refcounting in parallel to regulator core. Drop.
> + }
Why aren't you using devm interfaces?
> +}
> +
> +const struct of_device_id tac5x1x_of_match[] = {
> + { .compatible = "ti,taa5212", .data = (void *)TAA5212 },
> + { .compatible = "ti,taa5412", .data = (void *)TAA5412 },
> + { .compatible = "ti,tac5111", .data = (void *)TAC5111 },
> + { .compatible = "ti,tac5112", .data = (void *)TAC5112 },
> + { .compatible = "ti,tac5211", .data = (void *)TAC5211 },
> + { .compatible = "ti,tac5212", .data = (void *)TAC5212 },
> + { .compatible = "ti,tac5301", .data = (void *)TAC5301 },
> + { .compatible = "ti,tac5311", .data = (void *)TAC5311 },
> + { .compatible = "ti,tac5312", .data = (void *)TAC5312 },
> + { .compatible = "ti,tac5411", .data = (void *)TAC5411 },
> + { .compatible = "ti,tac5412", .data = (void *)TAC5412 },
> + { .compatible = "ti,tad5112", .data = (void *)TAD5112 },
> + { .compatible = "ti,tad5212", .data = (void *)TAD5212 },
What is all this? Your bindings said 5x1x?
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, tac5x1x_of_match);
> +
> +static int tac5x1x_i2c_probe(struct i2c_client *i2c)
> +{
> + enum tac5x1x_type type;
> + struct tac5x1x *tac5x1x;
> + struct regmap *regmap;
> + const struct regmap_config *config = &tac5x1x_regmap;
> +
> + tac5x1x = devm_kzalloc(&i2c->dev, sizeof(struct tac5x1x),
> + GFP_KERNEL);
> + if (!tac5x1x)
> + return -ENOMEM;
> +
> + i2c_set_clientdata(i2c, tac5x1x);
> + regmap = devm_regmap_init_i2c(i2c, config);
> + if (IS_ERR(regmap))
> + return PTR_ERR(regmap);
> + type = (uintptr_t)i2c_get_match_data(i2c);
No, don't use uintptr_t
> +
> + tac5x1x->dev = &i2c->dev;
> + tac5x1x->codec_type = type;
> + tac5x1x->regmap = regmap;
> +
> + return tac5x1x_probe(&i2c->dev, tac5x1x);
> +}
> +
> +static void tac5x1x_i2c_remove(struct i2c_client *client)
> +{
> + tac5x1x_remove(i2c_get_clientdata(client));
> +}
> +
> +static const struct i2c_device_id tac5x1x_id[] = {
> + {"taa5212", TAA5212},
> + {"taa5412", TAA5412},
> + {"tac5111", TAC5111},
> + {"tac5112", TAC5112},
> + {"tac5211", TAC5211},
> + {"tac5212", TAC5212},
> + {"tac5301", TAC5301},
> + {"tac5311", TAC5311},
> + {"tac5312", TAC5312},
> + {"tac5411", TAC5411},
> + {"tac5412", TAC5412},
> + {"tad5112", TAD5112},
> + {"tad5212", TAD5212},
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, tac5x1x_id);
> +
> +static DEFINE_RUNTIME_DEV_PM_OPS(tac5x1x_pm_ops, tac5x1x_suspend,
> + tac5x1x_resume, NULL);
> +
> +static struct i2c_driver tac5x1x_i2c_driver = {
> + .driver = {
> + .name = "tac5x1x-core",
> + .pm = pm_ptr(&tac5x1x_pm_ops),
> + .of_match_table = of_match_ptr(tac5x1x_of_match),
You have warnings here, drop of_match_ptr and compile test your code
> + },
> + .probe = tac5x1x_i2c_probe,
> + .remove = tac5x1x_i2c_remove,
> + .id_table = tac5x1x_id,
> +};
> +module_i2c_driver(tac5x1x_i2c_driver);
> +
> +MODULE_DESCRIPTION("Core support for the ASoC tac5x1x codec driver");
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Niranjan H Y <niranjan.hy@ti.com>");
> +MODULE_SOFTDEP("pre: tac5x1x_pinctrl");
Drop
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-03-14 10:20 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-12 18:48 [PATCH v1 0/8] ASoC: support TI's TAC5x1x audio codec family Niranjan H Y
2026-03-12 18:48 ` [PATCH v1 1/8] dt-bindings: mfd: Add bindings for TI TAC5x1x MFD core Niranjan H Y
2026-03-13 20:43 ` Rob Herring
2026-03-14 10:13 ` Krzysztof Kozlowski
2026-03-12 18:48 ` [PATCH v1 2/8] dt-bindings: pinctrl: Add bindings for TI TAC5x1x pinctrl Niranjan H Y
2026-03-14 10:10 ` Krzysztof Kozlowski
2026-03-14 10:12 ` Krzysztof Kozlowski
2026-03-12 18:48 ` [PATCH v1 3/8] dt-bindings: sound: Add bindings for TI TAC5x1x codec Niranjan H Y
2026-03-14 10:14 ` Krzysztof Kozlowski
2026-03-12 18:48 ` [PATCH v1 4/8] dt-bindings: sound: Update ti,pcm6240.yaml to remove TAC5x1x family Niranjan H Y
2026-03-14 10:15 ` Krzysztof Kozlowski
2026-03-12 18:48 ` [PATCH v1 5/8] mfd: tac5x1x: Add TI TAC5x1x MFD core driver Niranjan H Y
2026-03-14 10:20 ` Krzysztof Kozlowski
2026-03-12 18:48 ` [PATCH v1 6/8] pinctrl: pinctrl-tac5x1x: Add TI TAC5x1x pinctrl driver Niranjan H Y
2026-03-14 10:11 ` Krzysztof Kozlowski
2026-03-12 18:48 ` [PATCH v1 7/8] ASoC: tac5x1x: Add TI TAC5x1x codec driver Niranjan H Y
2026-03-12 19:56 ` Mark Brown
2026-03-12 18:48 ` [PATCH v1 8/8] ASoC: pcm6240: remove support for taac5x1x family Niranjan H Y
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox