* [PATCH v2 1/2] dt-bindings: trivial-devices: Add sony,aps-379
From: Chris Packham @ 2026-04-01 2:42 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, linux
Cc: devicetree, linux-hwmon, linux-kernel, Chris Packham
In-Reply-To: <20260401024214.804237-1-chris.packham@alliedtelesis.co.nz>
Add the compatible string for the sony,aps-379. This is a simple PMBus
(I2C) device that requires no additional attributes.
Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
Documentation/devicetree/bindings/trivial-devices.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index a482aeadcd44..0a559beff878 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -430,6 +430,8 @@ properties:
- smsc,emc6d103s
# Socionext Uniphier SMP control registers
- socionext,uniphier-smpctrl
+ # Sony APS-379 Power Supply
+ - sony,aps-379
# SparkFun Qwiic Joystick (COM-15168) with i2c interface
- sparkfun,qwiic-joystick
# STMicroelectronics Hot-swap controller stef48h28
--
2.53.0
^ permalink raw reply related
* [PATCH v2 0/2] hwmon: pmbus: Sony APS-379
From: Chris Packham @ 2026-04-01 2:42 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, linux
Cc: devicetree, linux-hwmon, linux-kernel, Chris Packham
This series add support for the PMBus hwmon on the Sony
APS-379 power supply module. There's some deviations from
the PMBus specification that need to be dealt with.
Chris Packham (2):
dt-bindings: trivial-devices: Add sony,aps-379
hwmon: pmbus: Add support for Sony APS-379
.../devicetree/bindings/trivial-devices.yaml | 2 +
Documentation/hwmon/aps-379.rst | 58 ++++++
Documentation/hwmon/index.rst | 1 +
drivers/hwmon/pmbus/Kconfig | 6 +
drivers/hwmon/pmbus/Makefile | 1 +
drivers/hwmon/pmbus/aps-379.c | 168 ++++++++++++++++++
6 files changed, 236 insertions(+)
create mode 100644 Documentation/hwmon/aps-379.rst
create mode 100644 drivers/hwmon/pmbus/aps-379.c
--
2.53.0
^ permalink raw reply
* [PATCH 3/4] Documentation: sound: Add TAS675x codec mixer controls documentation
From: Sen Wang @ 2026-04-01 2:42 UTC (permalink / raw)
To: linux-sound
Cc: broonie, lgirdwood, robh, krzk+dt, conor+dt, devicetree, perex,
tiwai, shenghao-ding, kevin-lu, baojun.xu, niranjan.hy,
l-badrinarayanan, devarsht, v-singh1, linux-kernel, Sen Wang
In-Reply-To: <20260401024210.28542-1-sen@ti.com>
Add ALSA mixer controls documentation for the TAS675x driver, covering
DSP signal path modes, volume controls, load diagnostics, and fault
monitoring.
Signed-off-by: Sen Wang <sen@ti.com>
---
Documentation/sound/codecs/index.rst | 1 +
Documentation/sound/codecs/tas675x.rst | 618 +++++++++++++++++++++++++
2 files changed, 619 insertions(+)
create mode 100644 Documentation/sound/codecs/tas675x.rst
diff --git a/Documentation/sound/codecs/index.rst b/Documentation/sound/codecs/index.rst
index 2cb95d87bbef..7594d0a38d6b 100644
--- a/Documentation/sound/codecs/index.rst
+++ b/Documentation/sound/codecs/index.rst
@@ -7,3 +7,4 @@ Codec-Specific Information
:maxdepth: 2
cs35l56
+ tas675x
diff --git a/Documentation/sound/codecs/tas675x.rst b/Documentation/sound/codecs/tas675x.rst
new file mode 100644
index 000000000000..334b1d8be3ea
--- /dev/null
+++ b/Documentation/sound/codecs/tas675x.rst
@@ -0,0 +1,618 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================
+TAS675x Codec Mixer Controls
+======================================
+
+This document describes the ALSA mixer controls for the TAS675x
+4-channel automotive Class-D amplifier driver.
+
+For device tree bindings, see:
+Documentation/devicetree/bindings/sound/ti,tas675x.yaml
+
+DSP Signal Path Mode
+====================
+
+DSP Signal Path Mode
+--------------------
+
+:Description: Signal processing mode selection.
+:Type: Enumerated
+:Default: Normal
+:Options: Normal, LLP, FFLP
+
+Normal
+ Full DSP with all features available.
+
+LLP (Low Latency Path)
+ Bypasses DSP processing. DSP protection features (Thermal Foldback,
+ PVDD Foldback, Clip Detect) and Real-Time Load Diagnostics unavailable.
+
+FFLP (Full Feature Low Latency Path)
+ Reduced latency. Real-Time Load Diagnostics unavailable.
+
+The following controls are unavailable in LLP mode:
+``Thermal Foldback Switch``, ``PVDD Foldback Switch``,
+``DC Blocker Bypass Switch``, ``Clip Detect Switch``, ``Audio SDOUT Switch``.
+
+The following controls require Normal mode (unavailable in FFLP and LLP):
+``CHx RTLDG Switch``, ``RTLDG Clip Mask Switch``, ``ISENSE Calibration Switch``,
+``RTLDG Open Load Threshold``, ``RTLDG Short Load Threshold``,
+``CHx RTLDG Impedance``, ``RTLDG Fault Latched``.
+
+Volume Controls
+===============
+
+Analog Playback Volume
+----------------------
+
+:Description: Analog output gain for all channels (CH1/CH2 and CH3/CH4 pairs).
+:Type: Volume (TLV)
+:Default: 0 dB
+:Range: -15.5 dB to 0 dB (0.5 dB steps)
+
+Analog Gain Ramp Step
+---------------------
+
+:Description: Anti-pop ramp step duration for analog gain transitions.
+:Type: Enumerated
+:Default: 15us
+:Options: 15us, 60us, 200us, 400us
+
+CHx Digital Playback Volume
+---------------------------
+
+:Description: Per-channel digital volume control (x = 1, 2, 3, 4).
+:Type: Volume (TLV)
+:Default: 0 dB
+:Range: -103 dB to 0 dB (0.5 dB steps)
+:Bounds: 0x30 (min/mute) to 0xFF (max)
+
+Volume Ramp Down Rate
+---------------------
+
+:Description: Update frequency during mute transition.
+:Type: Enumerated
+:Default: 16 FS
+:Options: 4 FS, 16 FS, 32 FS, Instant
+
+Volume Ramp Down Step
+---------------------
+
+:Description: dB change per update during mute.
+:Type: Enumerated
+:Default: 0.5dB
+:Options: 4dB, 2dB, 1dB, 0.5dB
+
+Volume Ramp Up Rate
+-------------------
+
+:Description: Update frequency during unmute transition.
+:Type: Enumerated
+:Default: 16 FS
+:Options: 4 FS, 16 FS, 32 FS, Instant
+
+Volume Ramp Up Step
+-------------------
+
+:Description: dB change per update during unmute.
+:Type: Enumerated
+:Default: 0.5dB
+:Options: 4dB, 2dB, 1dB, 0.5dB
+
+CH1/2 Volume Combine
+--------------------
+
+:Description: Links digital volume controls for CH1 and CH2.
+:Type: Enumerated
+:Default: Independent
+:Options: Independent, CH2 follows CH1, CH1 follows CH2
+
+CH3/4 Volume Combine
+--------------------
+
+:Description: Links digital volume controls for CH3 and CH4.
+:Type: Enumerated
+:Default: Independent
+:Options: Independent, CH4 follows CH3, CH3 follows CH4
+
+Auto Mute & Silence Detection
+==============================
+
+CHx Auto Mute Switch
+--------------------
+
+:Description: Enables automatic muting on zero-signal detection (x = 1, 2, 3, 4).
+:Type: Boolean Switch
+:Default: Disabled (0)
+
+Auto Mute Combine Switch
+------------------------
+
+:Description: Coordinated muting behaviour across all channels.
+:Type: Boolean Switch
+:Default: Disabled (0)
+:Behavior: Disabled: channels mute independently when their signal is zero.
+ Enabled: all channels mute together only when all detect zero
+ signal; unmute when any channel has non-zero signal.
+
+CHx Auto Mute Time
+------------------
+
+:Description: Duration of zero signal before muting triggers (x = 1, 2, 3, 4).
+:Type: Enumerated
+:Default: 11.5ms
+:Options: 11.5ms, 53ms, 106.5ms, 266.5ms, 535ms, 1065ms, 2665ms, 5330ms
+:Note: Values are at 96 kHz. At 48 kHz, times are doubled.
+
+Clock & EMI Management
+======================
+
+Spread Spectrum Mode
+--------------------
+
+:Description: Frequency dithering mode to reduce peak EMI.
+:Type: Enumerated
+:Default: Disabled
+:Options: Disabled, Triangle, Random, Triangle and Random
+
+SS Triangle Range
+-----------------
+
+:Description: Frequency deviation range for Triangle spread spectrum.
+:Type: Enumerated
+:Default: 6.5%
+:Options: 6.5%, 13.5%, 5%, 10%
+:Note: Applies only when Spread Spectrum Mode includes Triangle.
+
+SS Random Range
+---------------
+
+:Description: Frequency deviation range for Random spread spectrum.
+:Type: Enumerated
+:Default: 0.83%
+:Options: 0.83%, 2.50%, 5.83%, 12.50%, 25.83%
+:Note: Applies only when Spread Spectrum Mode includes Random.
+
+SS Random Dwell Range
+---------------------
+
+:Description: Dwell time range for Random spread spectrum (FSS = spread
+ spectrum modulation frequency).
+:Type: Enumerated
+:Default: 1/FSS to 2/FSS
+:Options: 1/FSS to 2/FSS, 1/FSS to 4/FSS, 1/FSS to 8/FSS, 1/FSS to 15/FSS
+:Note: Applies only when Spread Spectrum Mode includes Random.
+
+SS Triangle Dwell Min
+---------------------
+
+:Description: Minimum dwell time for Triangle spread spectrum.
+:Type: Integer
+:Default: 0
+:Range: 0 to 15 (raw register value)
+
+SS Triangle Dwell Max
+---------------------
+
+:Description: Maximum dwell time for Triangle spread spectrum.
+:Type: Integer
+:Default: 0
+:Range: 0 to 15 (raw register value)
+
+Hardware Protection
+===================
+
+OTSD Auto Recovery Switch
+--------------------------
+
+:Description: Enables automatic recovery from over-temperature shutdown.
+:Type: Boolean Switch
+:Default: Disabled (0)
+:Note: When disabled, manual fault clearing is required after OTSD events.
+
+Overcurrent Limit Level
+-----------------------
+
+:Description: Current-limit trip point sensitivity.
+:Type: Enumerated
+:Default: Level 4 (least sensitive)
+:Options: Level 4 (least sensitive), Level 3, Level 2, Level 1 (most sensitive)
+
+CHx OTW Threshold
+-----------------
+
+:Description: Over-temperature warning threshold per channel (x = 1, 2, 3, 4).
+:Type: Enumerated
+:Default: >95C
+:Options: Disabled, >95C, >110C, >125C, >135C, >145C, >155C, >165C
+
+Temperature and Voltage Monitoring
+===================================
+
+PVDD Sense
+----------
+
+:Description: Supply voltage sense register.
+:Type: Integer (read-only)
+:Range: 0 to 255
+:Conversion: value × 0.19 V
+
+Global Temperature
+------------------
+
+:Description: Global die temperature sense register.
+:Type: Integer (read-only)
+:Range: 0 to 255
+:Conversion: (value × 0.5 °C) − 50 °C
+
+CHx Temperature Range
+---------------------
+
+:Description: Per-channel coarse temperature range indicator (x = 1, 2, 3, 4).
+:Type: Integer (read-only)
+:Range: 0 to 3
+:Mapping: 0 = <80 °C, 1 = 80–100 °C, 2 = 100–120 °C, 3 = >120 °C
+
+Load Diagnostics
+================
+
+The TAS675x provides three load diagnostic modes:
+
+DC Load Diagnostics (DC LDG)
+ Measures DC resistance to detect S2G (short-to-ground), S2P
+ (short-to-power), OL (open load), and SL (shorted load) faults.
+
+AC Load Diagnostics (AC LDG)
+ Measures complex AC impedance at a configurable frequency. Detects
+ capacitive loads and tweeter configurations.
+
+Real-Time Load Diagnostics (RTLDG)
+ Monitors impedance continuously during playback using a pilot tone.
+ Normal DSP mode only, at 48 kHz or 96 kHz.
+
+Fast Boot Mode
+--------------
+
+By default the device runs DC load diagnostics at initialisation before
+accepting audio. Setting ``ti,fast-boot`` in the device tree bypasses this
+initial diagnostic run for faster startup. Automatic diagnostics after
+fault recovery remain enabled::
+
+ codec: tas675x@70 {
+ compatible = "ti,tas67524";
+ reg = <0x70>;
+ ti,fast-boot;
+ };
+
+DC Load Diagnostics
+-------------------
+
+The ``CHx DC LDG Report`` 4-bit fault field uses the following encoding:
+
+ ====== =========== ===================================================
+ Bit Fault Description
+ ====== =========== ===================================================
+ [3] S2G Short-to-Ground
+ [2] S2P Short-to-Power
+ [1] OL Open Load
+ [0] SL Shorted Load
+ ====== =========== ===================================================
+
+DC LDG Trigger
+~~~~~~~~~~~~~~
+
+:Description: Triggers manual DC load diagnostics on all channels.
+:Type: Boolean (write-only)
+:Note: Returns -EBUSY if any DAI stream (playback or capture) is active.
+ The driver manages all channel state transitions. Blocks until
+ diagnostics complete or time out (300 ms).
+
+DC LDG Auto Diagnostics Switch
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Enables automatic DC diagnostics after fault recovery.
+:Type: Boolean Switch
+:Default: Enabled (1)
+:Note: When enabled, affected channels re-run diagnostics after fault
+ recovery and retry approximately every 750 ms until resolved.
+
+CHx LO LDG Switch
+~~~~~~~~~~~~~~~~~
+
+:Description: Enables line output load detection per channel (x = 1, 2, 3, 4).
+:Type: Boolean Switch
+:Default: Disabled (0)
+:Note: When enabled and DC diagnostics report OL, the device tests for
+ a high-impedance line output load.
+
+DC LDG SLOL Ramp Time
+~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Voltage ramp time for shorted-load and open-load detection.
+:Type: Enumerated
+:Default: 15 ms
+:Options: 15 ms, 30 ms, 10 ms, 20 ms
+
+DC LDG SLOL Settling Time
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Settling time for shorted-load and open-load detection.
+:Type: Enumerated
+:Default: 10 ms
+:Options: 10 ms, 5 ms, 20 ms, 15 ms
+
+DC LDG S2PG Ramp Time
+~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Voltage ramp time for short-to-power and short-to-ground detection.
+:Type: Enumerated
+:Default: 5 ms
+:Options: 5 ms, 2.5 ms, 10 ms, 15 ms
+
+DC LDG S2PG Settling Time
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Settling time for short-to-power and short-to-ground detection.
+:Type: Enumerated
+:Default: 10 ms
+:Options: 10 ms, 5 ms, 20 ms, 30 ms
+
+CHx DC LDG SL Threshold
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Shorted-load detection threshold per channel (x = 1, 2, 3, 4).
+:Type: Enumerated
+:Default: 1 Ohm
+:Options: 0.5 Ohm, 1 Ohm, 1.5 Ohm, 2 Ohm, 2.5 Ohm,
+ 3 Ohm, 3.5 Ohm, 4 Ohm, 4.5 Ohm, 5 Ohm
+
+DC LDG Result
+~~~~~~~~~~~~~
+
+:Description: Overall DC diagnostic result register.
+:Type: Integer (read-only)
+:Range: 0x00 to 0xFF
+:Bit Encoding:
+
+ ======== =====================================================
+ Bits Description
+ ======== =====================================================
+ [7:4] Line output detection result, one bit per channel
+ [3:0] DC diagnostic pass/fail per channel (1=pass, 0=fail)
+ ======== =====================================================
+
+CHx DC LDG Report
+~~~~~~~~~~~~~~~~~
+
+:Description: DC diagnostic fault status per channel (x = 1, 2, 3, 4).
+:Type: Integer (read-only)
+:Range: 0x0 to 0xF
+:Note: See fault bit encoding table at the start of this section.
+
+CHx LO LDG Report
+~~~~~~~~~~~~~~~~~
+
+:Description: Line output load detection result per channel (x = 1, 2, 3, 4).
+:Type: Boolean (read-only)
+:Values: 0 = not detected, 1 = line output load detected
+
+CHx DC Resistance
+~~~~~~~~~~~~~~~~~
+
+:Description: Measured DC load resistance per channel (x = 1, 2, 3, 4).
+:Type: Float (read-only, displayed in ohms)
+:Resolution: 0.1 ohm per code (10-bit value)
+:Range: 0.0 to 102.3 ohms
+
+AC Load Diagnostics
+-------------------
+
+AC LDG Trigger
+~~~~~~~~~~~~~~
+
+:Description: Triggers AC impedance measurement on all channels.
+:Type: Boolean (write-only)
+:Note: Returns -EBUSY if any DAI stream (playback or capture) is active.
+ The driver transitions all channels to SLEEP state before starting
+ the measurement. Blocks until diagnostics complete or time out.
+
+AC DIAG GAIN
+~~~~~~~~~~~~
+
+:Description: Measurement resolution for AC diagnostics.
+:Type: Boolean Switch
+:Default: 1 (Gain 8)
+:Values: 0 = 0.8 ohm/code (Gain 1), 1 = 0.1 ohm/code (Gain 8)
+:Note: Gain 8 recommended for load impedances below 8 ohms.
+
+AC LDG Test Frequency
+~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Test signal frequency for AC impedance measurement.
+:Type: Integer
+:Default: 200 (0xC8 = 18.75 kHz)
+:Range: 0x01 to 0xFF (0x00 reserved)
+:Formula: Frequency = 93.75 Hz × register value
+
+CHx AC LDG Real / CHx AC LDG Imag
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Real and imaginary AC impedance components per channel
+ (x = 1, 2, 3, 4).
+:Type: Integer (read-only)
+:Range: 0x00 to 0xFF (8-bit signed)
+:Note: Scale set by AC DIAG GAIN.
+
+Speaker Protection & Detection
+-------------------------------
+
+Tweeter Detection Switch
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Enables tweeter detection using the AC impedance magnitude comparator.
+:Type: Boolean Switch
+:Default: Enabled (1)
+:Note: Inverted logic — control value 0 = enabled, 1 = disabled.
+
+Tweeter Detect Threshold
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Magnitude threshold for tweeter detection.
+:Type: Integer
+:Default: 0
+:Range: 0x00 to 0xFF
+:Resolution: 0.8 ohm/code (AC DIAG GAIN=0) or 0.1 ohm/code (AC DIAG GAIN=1)
+
+CHx Tweeter Detect Report
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Tweeter detection result per channel (x = 1, 2, 3, 4).
+:Type: Boolean (read-only)
+:Values: 0 = no tweeter detected, 1 = tweeter detected
+
+DSP Protection Features
+=======================
+
+These controls are unavailable in LLP mode.
+
+Thermal Foldback Switch
+-----------------------
+
+:Description: Enables dynamic gain reduction based on die temperature.
+:Type: Boolean Switch
+:Default: Disabled (0)
+
+PVDD Foldback Switch
+--------------------
+
+:Description: Enables automatic gain limiting when supply voltage drops
+ (Automatic Gain Limiter).
+:Type: Boolean Switch
+:Default: Disabled (0)
+
+DC Blocker Bypass Switch
+------------------------
+
+:Description: Bypasses the DC-blocking high-pass filter.
+:Type: Boolean Switch
+:Default: Not bypassed (0)
+
+Clip Detect Switch
+------------------
+
+:Description: Enables DSP-based clip detection (Pseudo-Analog Clip Detect).
+:Type: Boolean Switch
+:Default: Disabled (0)
+
+Audio SDOUT Switch
+------------------
+
+:Description: Routes post-processed audio to the SDOUT pin instead of
+ Vpredict data.
+:Type: Boolean Switch
+:Default: Disabled (0)
+:Note: Requires I2S or TDM format. Not supported in Left-justified
+ or DSP mode formats.
+
+Real-Time Load Diagnostics
+===========================
+
+These controls require Normal DSP mode at 48 kHz or 96 kHz. They are
+unavailable at 192 kHz and in FFLP and LLP modes.
+
+The ``RTLDG Fault Latched`` register uses the following encoding:
+
+ ======== ==========================================
+ Bits Description
+ ======== ==========================================
+ [7:4] Shorted Load faults, CH1–CH4 respectively
+ [3:0] Open Load faults, CH1–CH4 respectively
+ ======== ==========================================
+
+CHx RTLDG Switch
+----------------
+
+:Description: Enables real-time impedance monitoring during playback
+ (x = 1, 2, 3, 4).
+:Type: Boolean Switch
+:Default: Disabled (0)
+
+RTLDG Clip Mask Switch
+----------------------
+
+:Description: Suppresses impedance updates during clipping events.
+:Type: Boolean Switch
+:Default: Enabled (1)
+
+ISENSE Calibration Switch
+--------------------------
+
+:Description: Enables current sense calibration for accurate impedance
+ measurements.
+:Type: Boolean Switch
+:Default: Disabled (0)
+
+RTLDG Open Load Threshold
+--------------------------
+
+:Description: DSP coefficient for open load fault detection threshold.
+:Type: DSP coefficient (extended control)
+:Location: DSP book 0x8C, page 0x22
+
+RTLDG Short Load Threshold
+---------------------------
+
+:Description: DSP coefficient for shorted load fault detection threshold.
+:Type: DSP coefficient (extended control)
+:Location: DSP book 0x8C, page 0x22
+
+CHx RTLDG Impedance
+-------------------
+
+:Description: Real-time load impedance per channel (x = 1, 2, 3, 4).
+:Type: Float (read-only, displayed in ohms)
+:Note: Valid only during PLAY state with RTLDG enabled at 48 or
+ 96 kHz. Holds stale data in SLEEP, MUTE, or Hi-Z states.
+
+RTLDG Fault Latched
+-------------------
+
+:Description: Latched fault register for OL and SL conditions detected
+ during playback.
+:Type: Integer (read-only, read-to-clear)
+:Range: 0x00 to 0xFF
+:Note: See bit encoding table at the start of this section.
+ Reading the register clears all latched bits.
+
+Known Limitations
+=================
+
+Clock Fault Behaviour
+---------------------
+
+On Stream Stop
+~~~~~~~~~~~~~~
+
+Every time a playback stream stops the FAULT pin briefly asserts.
+The CPU DAI (McASP) stops SCLK during ``trigger(STOP)`` — an atomic
+context where codec I2C writes are not permitted — before the codec can
+transition to sleep. The device detects the clock halt and latches
+``CLK_FAULT_LATCHED``, which asserts the FAULT pin. The driver clears
+the latch in the ``mute_stream`` callback that follows, so the FAULT pin
+flicker lasts only a few milliseconds. Audio output is not affected and
+no kernel log message is produced.
+
+On Rapid Rate Switching
+~~~~~~~~~~~~~~~~~~~~~~~
+
+When streams are started in rapid succession, an intermittent
+``Clock Fault Latched: 0x01`` message may appear in the kernel log.
+A 0.5 second settling gap between sessions eliminates this.
+
+References
+==========
+
+- TAS675x Technical Reference Manual: SLOU589A
+- Device Tree Bindings: Documentation/devicetree/bindings/sound/ti,tas675x.yaml
+- ALSA Control Name Conventions: Documentation/sound/designs/control-names.rst
--
2.43.0
^ permalink raw reply related
* [PATCH 2/4] ASoC: codecs: Add TAS675x quad-channel audio amplifier driver
From: Sen Wang @ 2026-04-01 2:42 UTC (permalink / raw)
To: linux-sound
Cc: broonie, lgirdwood, robh, krzk+dt, conor+dt, devicetree, perex,
tiwai, shenghao-ding, kevin-lu, baojun.xu, niranjan.hy,
l-badrinarayanan, devarsht, v-singh1, linux-kernel, Sen Wang
In-Reply-To: <20260401024210.28542-1-sen@ti.com>
The TAS675x (TAS6754, TAS67524) are quad-channel, digital-input
Class-D amplifiers with an integrated DSP, controlled over I2C.
They support I2S and TDM serial audio interfaces.
The driver exposes three DAI endpoints: standard playback, a
low-latency DSP bypass path, and a sense capture DAI for real-time
voltage and current feedback. DC, AC and real-time load diagnostics
and hardware fault monitoring are supported.
Link: https://www.ti.com/product/TAS6754-Q1
Signed-off-by: Sen Wang <sen@ti.com>
---
sound/soc/codecs/Kconfig | 12 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tas675x.c | 2172 ++++++++++++++++++++++++++++++++++++
sound/soc/codecs/tas675x.h | 367 ++++++
4 files changed, 2553 insertions(+)
create mode 100644 sound/soc/codecs/tas675x.c
create mode 100644 sound/soc/codecs/tas675x.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index f9e6a83e55c6..0ca7c7e2283b 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -275,6 +275,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_TAS571X
imply SND_SOC_TAS5720
imply SND_SOC_TAS6424
+ imply SND_SOC_TAS675X
imply SND_SOC_TDA7419
imply SND_SOC_TFA9879
imply SND_SOC_TFA989X
@@ -2240,6 +2241,17 @@ config SND_SOC_TAS6424
Enable support for Texas Instruments TAS6424 high-efficiency
digital input quad-channel Class-D audio power amplifiers.
+config SND_SOC_TAS675X
+ tristate "Texas Instruments TAS675x Quad-Channel Audio Amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for Texas Instruments TAS675x quad-channel Class-D
+ audio power amplifiers (TAS6754, TAS67524). The devices support I2S
+ and TDM interfaces with real-time voltage and current sense feedback
+ via SDOUT, and provide DC/AC/real-time load diagnostics for speaker
+ monitoring.
+
config SND_SOC_TDA7419
tristate "ST TDA7419 audio processor"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 172861d17cfd..106fdc140d42 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -318,6 +318,7 @@ snd-soc-tas571x-y := tas571x.o
snd-soc-tas5720-y := tas5720.o
snd-soc-tas5805m-y := tas5805m.o
snd-soc-tas6424-y := tas6424.o
+snd-soc-tas675x-y := tas675x.o
snd-soc-tda7419-y := tda7419.o
snd-soc-tas2770-y := tas2770.o
snd-soc-tas2781-comlib-y := tas2781-comlib.o
@@ -760,6 +761,7 @@ obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
obj-$(CONFIG_SND_SOC_TAS5805M) += snd-soc-tas5805m.o
obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o
+obj-$(CONFIG_SND_SOC_TAS675X) += snd-soc-tas675x.o
obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o
obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o
obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
diff --git a/sound/soc/codecs/tas675x.c b/sound/soc/codecs/tas675x.c
new file mode 100644
index 000000000000..cf150a115bb3
--- /dev/null
+++ b/sound/soc/codecs/tas675x.c
@@ -0,0 +1,2172 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Texas Instruments TAS675x Quad-Channel Audio Amplifier
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Sen Wang <sen@ti.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/property.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
+#include <linux/iopoll.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+
+#include "tas675x.h"
+
+#define TAS675X_FAULT_CHECK_INTERVAL_MS 200
+
+enum tas675x_type {
+ TAS6754,
+};
+
+struct tas675x_reg_param {
+ u8 page;
+ u8 reg;
+ u32 val;
+};
+
+struct tas675x_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ enum tas675x_type dev_type;
+ struct mutex io_lock;
+
+ struct gpio_desc *pd_gpio;
+ struct gpio_desc *stby_gpio;
+ struct regulator_bulk_data supplies[2];
+ struct regulator *vbat;
+ bool fast_boot;
+
+ int audio_slot;
+ int llp_slot;
+ int vpredict_slot;
+ int isense_slot;
+ int bclk_offset;
+ int slot_width;
+ unsigned int tx_mask;
+
+ int gpio1_func;
+ int gpio2_func;
+
+ unsigned long active_playback_dais;
+ unsigned long active_capture_dais;
+ unsigned int rate;
+ unsigned int saved_rtldg_en;
+#define TAS675X_DSP_PARAM_NUM 2
+ struct tas675x_reg_param dsp_params[TAS675X_DSP_PARAM_NUM];
+
+ /* Fault monitor, disabled when Fault IRQ is used */
+ struct delayed_work fault_check_work;
+#define TAS675X_FAULT_REGS_NUM 8
+ unsigned int last_status[TAS675X_FAULT_REGS_NUM];
+};
+
+static const char * const tas675x_supply_names[] = {
+ "dvdd", /* Digital power supply */
+ "pvdd", /* Output powerstage supply */
+};
+
+/* Page 1 setup initialization defaults */
+static const struct reg_sequence tas675x_page1_init[] = {
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC8), 0x20), /* Charge pump clock */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0x2F), 0x90), /* VBAT idle */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0x29), 0x40), /* OC/CBC threshold */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0x2E), 0x0C), /* OC/CBC config */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC5), 0x02), /* OC/CBC config */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC6), 0x10), /* OC/CBC config */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0x1F), 0x20), /* OC/CBC config */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0x16), 0x01), /* OC/CBC config */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0x1E), 0x04), /* OC/CBC config */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC1), 0x00), /* CH1 DC fault */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC2), 0x04), /* CH2 DC fault */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC3), 0x00), /* CH3 DC fault */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC4), 0x00), /* CH4 DC fault */
+};
+
+static inline const char *tas675x_state_name(unsigned int state)
+{
+ switch (state & 0x0F) {
+ case TAS675X_STATE_DEEPSLEEP: return "DEEPSLEEP";
+ case TAS675X_STATE_LOAD_DIAG: return "LOAD_DIAG";
+ case TAS675X_STATE_SLEEP: return "SLEEP";
+ case TAS675X_STATE_HIZ: return "HIZ";
+ case TAS675X_STATE_PLAY: return "PLAY";
+ case TAS675X_STATE_FAULT: return "FAULT";
+ case TAS675X_STATE_AUTOREC: return "AUTOREC";
+ default: return "UNKNOWN";
+ }
+}
+
+static inline int tas675x_set_state_all(struct tas675x_priv *tas, u8 state)
+{
+ const struct reg_sequence seq[] = {
+ REG_SEQ0(TAS675X_STATE_CTRL_CH1_CH2_REG, state),
+ REG_SEQ0(TAS675X_STATE_CTRL_CH3_CH4_REG, state),
+ };
+
+ return regmap_multi_reg_write(tas->regmap, seq, ARRAY_SIZE(seq));
+}
+
+static inline int tas675x_select_book(struct regmap *regmap, u8 book)
+{
+ int ret;
+
+ /* Reset page to 0 before switching books */
+ ret = regmap_write(regmap, TAS675X_PAGE_CTRL_REG, 0x00);
+ if (!ret)
+ ret = regmap_write(regmap, TAS675X_BOOK_CTRL_REG, book);
+
+ return ret;
+}
+
+/* Raw I2C version of tas675x_select_book, must be called with io_lock held */
+static inline int __tas675x_select_book(struct tas675x_priv *tas, u8 book)
+{
+ struct i2c_client *client = to_i2c_client(tas->dev);
+ int ret;
+
+ /* Reset page to 0 before switching books */
+ ret = i2c_smbus_write_byte_data(client, TAS675X_PAGE_CTRL_REG, 0x00);
+ if (ret)
+ return ret;
+
+ return i2c_smbus_write_byte_data(client, TAS675X_BOOK_CTRL_REG, book);
+}
+
+static int tas675x_dsp_mem_write(struct tas675x_priv *tas, u8 page, u8 reg, u32 val)
+{
+ struct i2c_client *client = to_i2c_client(tas->dev);
+ u8 buf[4];
+ int ret;
+
+ /* DSP registers are 24-bit values, zero-padded MSB to 32-bit */
+ buf[0] = 0x00;
+ buf[1] = (val >> 16) & 0xFF;
+ buf[2] = (val >> 8) & 0xFF;
+ buf[3] = val & 0xFF;
+
+ /*
+ * DSP regs in a different book, therefore block
+ * regmap access before completion.
+ */
+ mutex_lock(&tas->io_lock);
+
+ ret = __tas675x_select_book(tas, TAS675X_BOOK_DSP);
+ if (ret)
+ goto out;
+
+ ret = i2c_smbus_write_byte_data(client, TAS675X_PAGE_CTRL_REG, page);
+ if (ret)
+ goto out;
+
+ ret = i2c_smbus_write_i2c_block_data(client, reg, sizeof(buf), buf);
+
+out:
+ __tas675x_select_book(tas, TAS675X_BOOK_DEFAULT);
+ mutex_unlock(&tas->io_lock);
+
+ return ret;
+}
+
+static int tas675x_dsp_mem_read(struct tas675x_priv *tas, u8 page, u8 reg, u32 *val)
+{
+ struct i2c_client *client = to_i2c_client(tas->dev);
+ u8 buf[4];
+ int ret;
+
+ /*
+ * DSP regs in a different book, therefore block
+ * regmap access before completion.
+ */
+ mutex_lock(&tas->io_lock);
+
+ ret = __tas675x_select_book(tas, TAS675X_BOOK_DSP);
+ if (ret)
+ goto out;
+
+ ret = i2c_smbus_write_byte_data(client, TAS675X_PAGE_CTRL_REG, page);
+ if (ret)
+ goto out;
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf);
+ if (ret == sizeof(buf)) {
+ *val = (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ ret = 0;
+ } else if (ret >= 0) {
+ ret = -EIO;
+ }
+
+out:
+ __tas675x_select_book(tas, TAS675X_BOOK_DEFAULT);
+ mutex_unlock(&tas->io_lock);
+
+ return ret;
+}
+
+static const struct {
+ const char *name;
+ int val;
+} tas675x_gpio_func_map[] = {
+ /* Output functions */
+ { "low", TAS675X_GPIO_SEL_LOW },
+ { "auto-mute", TAS675X_GPIO_SEL_AUTO_MUTE_ALL },
+ { "auto-mute-ch4", TAS675X_GPIO_SEL_AUTO_MUTE_CH4 },
+ { "auto-mute-ch3", TAS675X_GPIO_SEL_AUTO_MUTE_CH3 },
+ { "auto-mute-ch2", TAS675X_GPIO_SEL_AUTO_MUTE_CH2 },
+ { "auto-mute-ch1", TAS675X_GPIO_SEL_AUTO_MUTE_CH1 },
+ { "sdout2", TAS675X_GPIO_SEL_SDOUT2 },
+ { "sdout1", TAS675X_GPIO_SEL_SDOUT1 },
+ { "warn", TAS675X_GPIO_SEL_WARN },
+ { "fault", TAS675X_GPIO_SEL_FAULT },
+ { "clock-sync", TAS675X_GPIO_SEL_CLOCK_SYNC },
+ { "invalid-clock", TAS675X_GPIO_SEL_INVALID_CLK },
+ { "high", TAS675X_GPIO_SEL_HIGH },
+ /* Input functions */
+ { "mute", TAS675X_GPIO_IN_MUTE },
+ { "phase-sync", TAS675X_GPIO_IN_PHASE_SYNC },
+ { "sdin2", TAS675X_GPIO_IN_SDIN2 },
+ { "deep-sleep", TAS675X_GPIO_IN_DEEP_SLEEP },
+ { "hiz", TAS675X_GPIO_IN_HIZ },
+ { "play", TAS675X_GPIO_IN_PLAY },
+ { "sleep", TAS675X_GPIO_IN_SLEEP },
+};
+
+static int tas675x_gpio_func_parse(struct device *dev, const char *propname)
+{
+ const char *str;
+ int i, ret;
+
+ ret = device_property_read_string(dev, propname, &str);
+ if (ret)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE(tas675x_gpio_func_map); i++) {
+ if (!strcmp(str, tas675x_gpio_func_map[i].name))
+ return tas675x_gpio_func_map[i].val;
+ }
+
+ dev_warn(dev, "Invalid %s value '%s'\n", propname, str);
+ return -1;
+}
+
+static const struct {
+ unsigned int reg;
+ unsigned int mask;
+} tas675x_gpio_input_table[TAS675X_GPIO_IN_NUM] = {
+ [TAS675X_GPIO_IN_ID_MUTE] = {
+ TAS675X_GPIO_INPUT_MUTE_REG, TAS675X_GPIO_IN_MUTE_MASK },
+ [TAS675X_GPIO_IN_ID_PHASE_SYNC] = {
+ TAS675X_GPIO_INPUT_SYNC_REG, TAS675X_GPIO_IN_SYNC_MASK },
+ [TAS675X_GPIO_IN_ID_SDIN2] = {
+ TAS675X_GPIO_INPUT_SDIN2_REG, TAS675X_GPIO_IN_SDIN2_MASK },
+ [TAS675X_GPIO_IN_ID_DEEP_SLEEP] = {
+ TAS675X_GPIO_INPUT_SLEEP_HIZ_REG, TAS675X_GPIO_IN_DEEP_SLEEP_MASK },
+ [TAS675X_GPIO_IN_ID_HIZ] = {
+ TAS675X_GPIO_INPUT_SLEEP_HIZ_REG, TAS675X_GPIO_IN_HIZ_MASK },
+ [TAS675X_GPIO_IN_ID_PLAY] = {
+ TAS675X_GPIO_INPUT_PLAY_SLEEP_REG, TAS675X_GPIO_IN_PLAY_MASK },
+ [TAS675X_GPIO_IN_ID_SLEEP] = {
+ TAS675X_GPIO_INPUT_PLAY_SLEEP_REG, TAS675X_GPIO_IN_SLEEP_MASK },
+};
+
+static void tas675x_config_gpio_pin(struct regmap *regmap, int func_id,
+ unsigned int out_sel_reg,
+ unsigned int pin_idx,
+ unsigned int *gpio_ctrl)
+{
+ int id;
+
+ if (func_id < 0)
+ return;
+
+ if (func_id & TAS675X_GPIO_FUNC_INPUT) {
+ /* 3-bit mux: 0 = disabled, 0b1 = GPIO1, 0b10 = GPIO2 */
+ id = func_id & ~TAS675X_GPIO_FUNC_INPUT;
+ regmap_update_bits(regmap,
+ tas675x_gpio_input_table[id].reg,
+ tas675x_gpio_input_table[id].mask,
+ (pin_idx + 1) << __ffs(tas675x_gpio_input_table[id].mask));
+ } else {
+ /* Output GPIO, update selection register and enable bit */
+ regmap_write(regmap, out_sel_reg, func_id);
+ *gpio_ctrl |= pin_idx ? TAS675X_GPIO2_OUTPUT_EN : TAS675X_GPIO1_OUTPUT_EN;
+ }
+}
+
+static int tas675x_rtldg_thresh_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ /* Accepts 32-bit values, even though 8bit MSB is ignored */
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
+static int tas675x_set_rtldg_thresh(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
+ const struct tas675x_reg_param *t =
+ (const struct tas675x_reg_param *)kcontrol->private_value;
+ u32 val = ucontrol->value.integer.value[0];
+ int ret;
+
+ ret = tas675x_dsp_mem_write(tas, t->page, t->reg, val);
+
+ /* Cache the value */
+ if (!ret) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tas->dsp_params); i++) {
+ if (tas->dsp_params[i].page == t->page &&
+ tas->dsp_params[i].reg == t->reg) {
+ tas->dsp_params[i].val = val;
+ break;
+ }
+ }
+ }
+
+ /* Return 1 to notify change, or propagate error */
+ return ret ? ret : 1;
+}
+
+static int tas675x_get_rtldg_thresh(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
+ const struct tas675x_reg_param *t =
+ (const struct tas675x_reg_param *)kcontrol->private_value;
+ u32 val = 0;
+ int ret;
+
+ ret = tas675x_dsp_mem_read(tas, t->page, t->reg, &val);
+ if (!ret)
+ ucontrol->value.integer.value[0] = val;
+
+ return ret;
+}
+
+static const struct tas675x_reg_param tas675x_dsp_defaults[] = {
+ [TAS675X_DSP_PARAM_ID_OL_THRESH] = {
+ TAS675X_DSP_PAGE_RTLDG, TAS675X_DSP_RTLDG_OL_THRESH_REG },
+ [TAS675X_DSP_PARAM_ID_SL_THRESH] = {
+ TAS675X_DSP_PAGE_RTLDG, TAS675X_DSP_RTLDG_SL_THRESH_REG },
+};
+
+static int tas675x_set_dcldg_trigger(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
+ unsigned int state, state34;
+ int ret;
+
+ if (!ucontrol->value.integer.value[0])
+ return 0;
+
+ if (snd_soc_component_active(comp))
+ return -EBUSY;
+
+ ret = pm_runtime_resume_and_get(tas->dev);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Abort automatic DC LDG retry loops (startup or init-after-fault)
+ * and clear faults before manual diagnostics.
+ */
+ regmap_update_bits(tas->regmap, TAS675X_DC_LDG_CTRL_REG,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT);
+ regmap_write(tas->regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR);
+
+ /* Wait for LOAD_DIAG to exit */
+ regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG,
+ state, (state & 0x0F) != TAS675X_STATE_LOAD_DIAG &&
+ (state >> 4) != TAS675X_STATE_LOAD_DIAG,
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_STATE_TRANSITION_TIMEOUT_US);
+ regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG,
+ state34, (state34 & 0x0F) != TAS675X_STATE_LOAD_DIAG &&
+ (state34 >> 4) != TAS675X_STATE_LOAD_DIAG,
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_STATE_TRANSITION_TIMEOUT_US);
+
+ /* Transition to HIZ state */
+ ret = tas675x_set_state_all(tas, TAS675X_STATE_HIZ_BOTH);
+ if (ret)
+ goto out_restore_ldg_ctrl;
+
+ /* Set LOAD_DIAG state for manual DC LDG */
+ ret = tas675x_set_state_all(tas, TAS675X_STATE_LOAD_DIAG_BOTH);
+ if (ret)
+ goto out_restore_ldg_ctrl;
+
+ /* Wait for device to transition to LOAD_DIAG state */
+ regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG,
+ state, state == TAS675X_STATE_LOAD_DIAG_BOTH,
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_STATE_TRANSITION_TIMEOUT_US);
+ regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG,
+ state34, state34 == TAS675X_STATE_LOAD_DIAG_BOTH,
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_STATE_TRANSITION_TIMEOUT_US);
+
+ /* Clear ABORT and BYPASS bits to enable manual DC LDG */
+ ret = regmap_update_bits(tas->regmap, TAS675X_DC_LDG_CTRL_REG,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
+ 0);
+ if (ret)
+ goto out_restore_hiz;
+
+ dev_dbg(tas->dev, "DC LDG: Started\n");
+
+ /* Poll all channels for SLEEP state */
+ ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG,
+ state, state == TAS675X_STATE_SLEEP_BOTH,
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_DC_LDG_TIMEOUT_US);
+ ret |= regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG,
+ state34, state34 == TAS675X_STATE_SLEEP_BOTH,
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_DC_LDG_TIMEOUT_US);
+ if (ret) {
+ dev_err(tas->dev,
+ "DC LDG: timeout (CH1/2=0x%02x [%s/%s], CH3/4=0x%02x [%s/%s])\n",
+ state, tas675x_state_name(state), tas675x_state_name(state >> 4),
+ state34, tas675x_state_name(state34), tas675x_state_name(state34 >> 4));
+ goto out_restore_hiz;
+ }
+
+ dev_dbg(tas->dev, "DC LDG: Completed successfully (CH1/2=0x%02x, CH3/4=0x%02x)\n",
+ state, state34);
+
+out_restore_hiz:
+ tas675x_set_state_all(tas, TAS675X_STATE_HIZ_BOTH);
+
+out_restore_ldg_ctrl:
+ regmap_update_bits(tas->regmap, TAS675X_DC_LDG_CTRL_REG,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
+ 0);
+
+ pm_runtime_mark_last_busy(tas->dev);
+ pm_runtime_put_autosuspend(tas->dev);
+
+ return ret;
+}
+
+static int tas675x_set_acldg_trigger(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
+ unsigned int state, state34;
+ int ret;
+
+ if (!ucontrol->value.integer.value[0])
+ return 0;
+
+ if (snd_soc_component_active(comp))
+ return -EBUSY;
+
+ ret = pm_runtime_resume_and_get(tas->dev);
+ if (ret < 0)
+ return ret;
+
+ /* AC Load Diagnostics requires SLEEP state */
+ ret = tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
+ if (ret) {
+ dev_err(tas->dev, "AC LDG: Failed to set SLEEP state: %d\n", ret);
+ goto out;
+ }
+
+ /* Start AC LDG on all 4 channels (0x0F) */
+ ret = regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x0F);
+ if (ret) {
+ dev_err(tas->dev, "AC LDG: Failed to start: %d\n", ret);
+ goto out;
+ }
+
+ dev_dbg(tas->dev, "AC LDG: Started\n");
+
+ /* Poll all channels for SLEEP state */
+ ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG,
+ state, (state == TAS675X_STATE_SLEEP_BOTH),
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_AC_LDG_TIMEOUT_US);
+ if (ret) {
+ dev_err(tas->dev,
+ "AC LDG: CH1/CH2 timeout: %d (state=0x%02x [%s/%s])\n",
+ ret, state, tas675x_state_name(state),
+ tas675x_state_name(state >> 4));
+ regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x00);
+ goto out;
+ }
+
+ ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG,
+ state34, (state34 == TAS675X_STATE_SLEEP_BOTH),
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_AC_LDG_TIMEOUT_US);
+ if (ret) {
+ dev_err(tas->dev,
+ "AC LDG: CH3/CH4 timeout: %d (state=0x%02x [%s/%s])\n",
+ ret, state34, tas675x_state_name(state34),
+ tas675x_state_name(state34 >> 4));
+ regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x00);
+ goto out;
+ }
+
+ dev_dbg(tas->dev, "AC LDG: Completed successfully (CH1/2=0x%02x, CH3/4=0x%02x)\n",
+ state, state34);
+ regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x00);
+ ret = 0;
+
+out:
+ pm_runtime_mark_last_busy(tas->dev);
+ pm_runtime_put_autosuspend(tas->dev);
+
+ return ret;
+}
+
+static int tas675x_rtldg_impedance_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFF;
+ return 0;
+}
+
+static int tas675x_get_rtldg_impedance(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
+ unsigned int msb_reg = (unsigned int)kcontrol->private_value;
+ unsigned int msb, lsb;
+ int ret;
+
+ ret = regmap_read(tas->regmap, msb_reg, &msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(tas->regmap, msb_reg + 1, &lsb);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = (msb << 8) | lsb;
+ return 0;
+}
+
+static int tas675x_dc_resistance_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ /* 10-bit: 2-bit MSB + 8-bit LSB, 0.1 ohm/code, 0-102.3 ohm */
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1023;
+ return 0;
+}
+
+static int tas675x_get_dc_resistance(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
+ unsigned int lsb_reg = (unsigned int)kcontrol->private_value;
+ unsigned int msb, lsb, shift;
+ int ret;
+
+ ret = regmap_read(tas->regmap, TAS675X_DC_LDG_DCR_MSB_REG, &msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(tas->regmap, lsb_reg, &lsb);
+ if (ret)
+ return ret;
+
+ /* 2-bit MSB: CH1=[7:6], CH2=[5:4], CH3=[3:2], CH4=[1:0] */
+ shift = 6 - (lsb_reg - TAS675X_CH1_DC_LDG_DCR_LSB_REG) * 2;
+ msb = (msb >> shift) & 0x3;
+
+ ucontrol->value.integer.value[0] = (msb << 8) | lsb;
+ return 0;
+}
+
+/* Counterparts with read-only access */
+#define SOC_SINGLE_RO(xname, xreg, xshift, xmax) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_soc_info_volsw, \
+ .get = snd_soc_get_volsw, \
+ .private_value = SOC_SINGLE_VALUE(xreg, xshift, 0, xmax, 0, 0) }
+#define SOC_DC_RESIST_RO(xname, xlsb_reg) \
+{ .name = xname, \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = tas675x_dc_resistance_info, \
+ .get = tas675x_get_dc_resistance, \
+ .private_value = (xlsb_reg) }
+#define SOC_RTLDG_IMP_RO(xname, xreg) \
+{ .name = xname, \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = tas675x_rtldg_impedance_info, \
+ .get = tas675x_get_rtldg_impedance, \
+ .private_value = (xreg) }
+
+#define SOC_DSP_THRESH_EXT(xname, xthresh) \
+{ .name = xname, \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .info = tas675x_rtldg_thresh_info, \
+ .get = tas675x_get_rtldg_thresh, \
+ .put = tas675x_set_rtldg_thresh, \
+ .private_value = (unsigned long)&(xthresh) }
+
+/*
+ * DAC digital volumes. From -103 to 0 dB in 0.5 dB steps, -103.5 dB means mute.
+ * DAC analog gain. From -15.5 to 0 dB in 0.5 dB steps, no mute.
+ */
+static const DECLARE_TLV_DB_SCALE(tas675x_dig_vol_tlv, -10350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(tas675x_ana_gain_tlv, -1550, 50, 0);
+
+static const char * const tas675x_ss_texts[] = {
+ "Disabled", "Triangle", "Random", "Triangle and Random"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ss_enum, TAS675X_SS_CTRL_REG, 0, tas675x_ss_texts);
+
+static const char * const tas675x_ss_tri_range_texts[] = {
+ "6.5%", "13.5%", "5%", "10%"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ss_tri_range_enum,
+ TAS675X_SS_RANGE_CTRL_REG, 0,
+ tas675x_ss_tri_range_texts);
+
+static const char * const tas675x_ss_rdm_range_texts[] = {
+ "0.83%", "2.50%", "5.83%", "12.50%", "25.83%"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ss_rdm_range_enum,
+ TAS675X_SS_RANGE_CTRL_REG, 4,
+ tas675x_ss_rdm_range_texts);
+
+static const char * const tas675x_ss_rdm_dwell_texts[] = {
+ "1/FSS to 2/FSS", "1/FSS to 4/FSS", "1/FSS to 8/FSS", "1/FSS to 15/FSS"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ss_rdm_dwell_enum,
+ TAS675X_SS_RANGE_CTRL_REG, 2,
+ tas675x_ss_rdm_dwell_texts);
+
+static const char * const tas675x_oc_limit_texts[] = {
+ "Level 4", "Level 3", "Level 2", "Level 1"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_oc_limit_enum, TAS675X_CURRENT_LIMIT_CTRL_REG,
+ 0, tas675x_oc_limit_texts);
+
+static const char * const tas675x_otw_texts[] = {
+ "Disabled", ">95C", ">110C", ">125C", ">135C", ">145C", ">155C", ">165C"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ch1_otw_enum,
+ TAS675X_OTW_CTRL_CH1_CH2_REG, 4,
+ tas675x_otw_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch2_otw_enum,
+ TAS675X_OTW_CTRL_CH1_CH2_REG, 0,
+ tas675x_otw_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch3_otw_enum,
+ TAS675X_OTW_CTRL_CH3_CH4_REG, 4,
+ tas675x_otw_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch4_otw_enum,
+ TAS675X_OTW_CTRL_CH3_CH4_REG, 0,
+ tas675x_otw_texts);
+
+static const char * const tas675x_dc_ldg_sl_texts[] = {
+ "0.5 Ohm", "1 Ohm", "1.5 Ohm", "2 Ohm", "2.5 Ohm",
+ "3 Ohm", "3.5 Ohm", "4 Ohm", "4.5 Ohm", "5 Ohm"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ch1_dc_ldg_sl_enum,
+ TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 4,
+ tas675x_dc_ldg_sl_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch2_dc_ldg_sl_enum,
+ TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0,
+ tas675x_dc_ldg_sl_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch3_dc_ldg_sl_enum,
+ TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 4,
+ tas675x_dc_ldg_sl_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch4_dc_ldg_sl_enum,
+ TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0,
+ tas675x_dc_ldg_sl_texts);
+
+static const char * const tas675x_dc_slol_ramp_texts[] = {
+ "15 ms", "30 ms", "10 ms", "20 ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_dc_slol_ramp_enum,
+ TAS675X_DC_LDG_TIME_CTRL_REG, 6,
+ tas675x_dc_slol_ramp_texts);
+
+static const char * const tas675x_dc_slol_settling_texts[] = {
+ "10 ms", "5 ms", "20 ms", "15 ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_dc_slol_settling_enum,
+ TAS675X_DC_LDG_TIME_CTRL_REG, 4,
+ tas675x_dc_slol_settling_texts);
+
+static const char * const tas675x_dc_s2pg_ramp_texts[] = {
+ "5 ms", "2.5 ms", "10 ms", "15 ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_dc_s2pg_ramp_enum,
+ TAS675X_DC_LDG_TIME_CTRL_REG, 2,
+ tas675x_dc_s2pg_ramp_texts);
+
+static const char * const tas675x_dc_s2pg_settling_texts[] = {
+ "10 ms", "5 ms", "20 ms", "30 ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_dc_s2pg_settling_enum,
+ TAS675X_DC_LDG_TIME_CTRL_REG, 0,
+ tas675x_dc_s2pg_settling_texts);
+
+static const char * const tas675x_dsp_mode_texts[] = {
+ "Normal", "LLP", "FFLP"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_dsp_mode_enum,
+ TAS675X_LL_EN_REG, 0,
+ tas675x_dsp_mode_texts);
+
+static const char * const tas675x_ana_ramp_texts[] = {
+ "15us", "60us", "200us", "400us"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ana_ramp_enum,
+ TAS675X_ANALOG_GAIN_RAMP_CTRL_REG, 2,
+ tas675x_ana_ramp_texts);
+
+static const char * const tas675x_ramp_rate_texts[] = {
+ "4 FS", "16 FS", "32 FS", "Instant"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ramp_down_rate_enum,
+ TAS675X_DIG_VOL_RAMP_CTRL_REG, 6,
+ tas675x_ramp_rate_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ramp_up_rate_enum,
+ TAS675X_DIG_VOL_RAMP_CTRL_REG, 2,
+ tas675x_ramp_rate_texts);
+
+static const char * const tas675x_ramp_step_texts[] = {
+ "4dB", "2dB", "1dB", "0.5dB"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ramp_down_step_enum,
+ TAS675X_DIG_VOL_RAMP_CTRL_REG, 4,
+ tas675x_ramp_step_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ramp_up_step_enum,
+ TAS675X_DIG_VOL_RAMP_CTRL_REG, 0,
+ tas675x_ramp_step_texts);
+
+static const char * const tas675x_vol_combine_ch12_texts[] = {
+ "Independent", "CH2 follows CH1", "CH1 follows CH2"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_vol_combine_ch12_enum,
+ TAS675X_DIG_VOL_COMBINE_CTRL_REG, 0,
+ tas675x_vol_combine_ch12_texts);
+
+static const char * const tas675x_vol_combine_ch34_texts[] = {
+ "Independent", "CH4 follows CH3", "CH3 follows CH4"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_vol_combine_ch34_enum,
+ TAS675X_DIG_VOL_COMBINE_CTRL_REG, 2,
+ tas675x_vol_combine_ch34_texts);
+
+static const char * const tas675x_auto_mute_time_texts[] = {
+ "11.5ms", "53ms", "106.5ms", "266.5ms",
+ "535ms", "1065ms", "2665ms", "5330ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ch1_mute_time_enum,
+ TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG, 4,
+ tas675x_auto_mute_time_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch2_mute_time_enum,
+ TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG, 0,
+ tas675x_auto_mute_time_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch3_mute_time_enum,
+ TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG, 4,
+ tas675x_auto_mute_time_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch4_mute_time_enum,
+ TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG, 0,
+ tas675x_auto_mute_time_texts);
+
+/*
+ * ALSA Mixer Controls
+ *
+ * For detailed documentation of each control see:
+ * Documentation/sound/codecs/tas675x.rst
+ */
+static const struct snd_kcontrol_new tas675x_snd_controls[] = {
+ /* Volume & Gain Control */
+ SOC_DOUBLE_R_TLV("Analog Playback Volume", TAS675X_ANALOG_GAIN_CH1_CH2_REG,
+ TAS675X_ANALOG_GAIN_CH3_CH4_REG, 1, 0x1F, 1, tas675x_ana_gain_tlv),
+ SOC_ENUM("Analog Gain Ramp Step", tas675x_ana_ramp_enum),
+ SOC_SINGLE_RANGE_TLV("CH1 Digital Playback Volume",
+ TAS675X_DIG_VOL_CH1_REG, 0, 0x30, 0xFF, 1,
+ tas675x_dig_vol_tlv),
+ SOC_SINGLE_RANGE_TLV("CH2 Digital Playback Volume",
+ TAS675X_DIG_VOL_CH2_REG, 0, 0x30, 0xFF, 1,
+ tas675x_dig_vol_tlv),
+ SOC_SINGLE_RANGE_TLV("CH3 Digital Playback Volume",
+ TAS675X_DIG_VOL_CH3_REG, 0, 0x30, 0xFF, 1,
+ tas675x_dig_vol_tlv),
+ SOC_SINGLE_RANGE_TLV("CH4 Digital Playback Volume",
+ TAS675X_DIG_VOL_CH4_REG, 0, 0x30, 0xFF, 1,
+ tas675x_dig_vol_tlv),
+ SOC_ENUM("Volume Ramp Down Rate", tas675x_ramp_down_rate_enum),
+ SOC_ENUM("Volume Ramp Down Step", tas675x_ramp_down_step_enum),
+ SOC_ENUM("Volume Ramp Up Rate", tas675x_ramp_up_rate_enum),
+ SOC_ENUM("Volume Ramp Up Step", tas675x_ramp_up_step_enum),
+ SOC_ENUM("CH1/2 Volume Combine", tas675x_vol_combine_ch12_enum),
+ SOC_ENUM("CH3/4 Volume Combine", tas675x_vol_combine_ch34_enum),
+
+ /* Auto Mute & Silence Detection */
+ SOC_SINGLE("CH1 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 0, 1, 0),
+ SOC_SINGLE("CH2 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 1, 1, 0),
+ SOC_SINGLE("CH3 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 2, 1, 0),
+ SOC_SINGLE("CH4 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 3, 1, 0),
+ SOC_SINGLE("Auto Mute Combine Switch", TAS675X_AUTO_MUTE_EN_REG, 4, 1, 0),
+ SOC_ENUM("CH1 Auto Mute Time", tas675x_ch1_mute_time_enum),
+ SOC_ENUM("CH2 Auto Mute Time", tas675x_ch2_mute_time_enum),
+ SOC_ENUM("CH3 Auto Mute Time", tas675x_ch3_mute_time_enum),
+ SOC_ENUM("CH4 Auto Mute Time", tas675x_ch4_mute_time_enum),
+
+ /* Clock & EMI Management */
+ SOC_ENUM("Spread Spectrum Mode", tas675x_ss_enum),
+ SOC_ENUM("SS Triangle Range", tas675x_ss_tri_range_enum),
+ SOC_ENUM("SS Random Range", tas675x_ss_rdm_range_enum),
+ SOC_ENUM("SS Random Dwell Range", tas675x_ss_rdm_dwell_enum),
+ SOC_SINGLE("SS Triangle Dwell Min", TAS675X_SS_DWELL_CTRL_REG, 4, 15, 0),
+ SOC_SINGLE("SS Triangle Dwell Max", TAS675X_SS_DWELL_CTRL_REG, 0, 15, 0),
+
+ /* Hardware Protection */
+ SOC_SINGLE("OTSD Auto Recovery Switch", TAS675X_OTSD_RECOVERY_EN_REG, 1, 1, 0),
+ SOC_ENUM("Overcurrent Limit Level", tas675x_oc_limit_enum),
+ SOC_ENUM("CH1 OTW Threshold", tas675x_ch1_otw_enum),
+ SOC_ENUM("CH2 OTW Threshold", tas675x_ch2_otw_enum),
+ SOC_ENUM("CH3 OTW Threshold", tas675x_ch3_otw_enum),
+ SOC_ENUM("CH4 OTW Threshold", tas675x_ch4_otw_enum),
+
+ /* DSP Signal Path & Mode */
+ SOC_ENUM("DSP Signal Path Mode", tas675x_dsp_mode_enum),
+
+ /* DC Load Diagnostics */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DC LDG Trigger",
+ .access = SNDRV_CTL_ELEM_ACCESS_WRITE,
+ .info = snd_ctl_boolean_mono_info,
+ .put = tas675x_set_dcldg_trigger,
+ },
+ SOC_SINGLE("DC LDG Auto Diagnostics Switch", TAS675X_DC_LDG_CTRL_REG, 0, 1, 1),
+ SOC_SINGLE("CH1 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 3, 1, 0),
+ SOC_SINGLE("CH2 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 2, 1, 0),
+ SOC_SINGLE("CH3 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 1, 1, 0),
+ SOC_SINGLE("CH4 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 0, 1, 0),
+ SOC_ENUM("DC LDG SLOL Ramp Time", tas675x_dc_slol_ramp_enum),
+ SOC_ENUM("DC LDG SLOL Settling Time", tas675x_dc_slol_settling_enum),
+ SOC_ENUM("DC LDG S2PG Ramp Time", tas675x_dc_s2pg_ramp_enum),
+ SOC_ENUM("DC LDG S2PG Settling Time", tas675x_dc_s2pg_settling_enum),
+ SOC_ENUM("CH1 DC LDG SL Threshold", tas675x_ch1_dc_ldg_sl_enum),
+ SOC_ENUM("CH2 DC LDG SL Threshold", tas675x_ch2_dc_ldg_sl_enum),
+ SOC_ENUM("CH3 DC LDG SL Threshold", tas675x_ch3_dc_ldg_sl_enum),
+ SOC_ENUM("CH4 DC LDG SL Threshold", tas675x_ch4_dc_ldg_sl_enum),
+ SOC_SINGLE_RO("DC LDG Result", TAS675X_DC_LDG_RESULT_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH1 DC LDG Report", TAS675X_DC_LDG_REPORT_CH1_CH2_REG, 4, 0x0F),
+ SOC_SINGLE_RO("CH2 DC LDG Report", TAS675X_DC_LDG_REPORT_CH1_CH2_REG, 0, 0x0F),
+ SOC_SINGLE_RO("CH3 DC LDG Report", TAS675X_DC_LDG_REPORT_CH3_CH4_REG, 4, 0x0F),
+ SOC_SINGLE_RO("CH4 DC LDG Report", TAS675X_DC_LDG_REPORT_CH3_CH4_REG, 0, 0x0F),
+ SOC_SINGLE_RO("CH1 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 7, 1),
+ SOC_SINGLE_RO("CH2 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 6, 1),
+ SOC_SINGLE_RO("CH3 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 5, 1),
+ SOC_SINGLE_RO("CH4 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 4, 1),
+ SOC_DC_RESIST_RO("CH1 DC Resistance", TAS675X_CH1_DC_LDG_DCR_LSB_REG),
+ SOC_DC_RESIST_RO("CH2 DC Resistance", TAS675X_CH2_DC_LDG_DCR_LSB_REG),
+ SOC_DC_RESIST_RO("CH3 DC Resistance", TAS675X_CH3_DC_LDG_DCR_LSB_REG),
+ SOC_DC_RESIST_RO("CH4 DC Resistance", TAS675X_CH4_DC_LDG_DCR_LSB_REG),
+
+ /* AC Load Diagnostics */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "AC LDG Trigger",
+ .access = SNDRV_CTL_ELEM_ACCESS_WRITE,
+ .info = snd_ctl_boolean_mono_info,
+ .put = tas675x_set_acldg_trigger,
+ },
+ SOC_SINGLE("AC DIAG GAIN", TAS675X_AC_LDG_CTRL_REG, 4, 1, 0),
+ SOC_SINGLE("AC LDG Test Frequency", TAS675X_AC_LDG_FREQ_CTRL_REG, 0, 0xFF, 0),
+ SOC_SINGLE_RO("CH1 AC LDG Real", TAS675X_AC_LDG_REPORT_CH1_R_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH1 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH1_I_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH2 AC LDG Real", TAS675X_AC_LDG_REPORT_CH2_R_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH2 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH2_I_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH3 AC LDG Real", TAS675X_AC_LDG_REPORT_CH3_R_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH3 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH3_I_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH4 AC LDG Real", TAS675X_AC_LDG_REPORT_CH4_R_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH4 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH4_I_REG, 0, 0xFF),
+
+ /* Temperature and Voltage Monitoring */
+ SOC_SINGLE_RO("PVDD Sense", TAS675X_PVDD_SENSE_REG, 0, 0xFF),
+ SOC_SINGLE_RO("Global Temperature", TAS675X_TEMP_GLOBAL_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH1 Temperature Range", TAS675X_TEMP_CH1_CH2_REG, 6, 3),
+ SOC_SINGLE_RO("CH2 Temperature Range", TAS675X_TEMP_CH1_CH2_REG, 4, 3),
+ SOC_SINGLE_RO("CH3 Temperature Range", TAS675X_TEMP_CH3_CH4_REG, 2, 3),
+ SOC_SINGLE_RO("CH4 Temperature Range", TAS675X_TEMP_CH3_CH4_REG, 0, 3),
+
+ /* Speaker Protection & Detection */
+ SOC_SINGLE("Tweeter Detection Switch", TAS675X_TWEETER_DETECT_CTRL_REG, 0, 1, 1),
+ SOC_SINGLE("Tweeter Detect Threshold", TAS675X_TWEETER_DETECT_THRESH_REG, 0, 0xFF, 0),
+ SOC_SINGLE_RO("CH1 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 3, 1),
+ SOC_SINGLE_RO("CH2 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 2, 1),
+ SOC_SINGLE_RO("CH3 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 1, 1),
+ SOC_SINGLE_RO("CH4 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 0, 1),
+
+ /*
+ * Unavailable in LLP, available in Normal & FFLP
+ */
+ SOC_SINGLE("Thermal Foldback Switch", TAS675X_DSP_CTRL_REG, 0, 1, 0),
+ SOC_SINGLE("PVDD Foldback Switch", TAS675X_DSP_CTRL_REG, 4, 1, 0),
+ SOC_SINGLE("DC Blocker Bypass Switch", TAS675X_DC_BLOCK_BYP_REG, 0, 1, 0),
+ SOC_SINGLE("Clip Detect Switch", TAS675X_CLIP_DETECT_CTRL_REG, 6, 1, 0),
+ SOC_SINGLE("Audio SDOUT Switch", TAS675X_DSP_CTRL_REG, 5, 1, 0),
+
+ /*
+ * Unavailable in both FFLP and LLP, Normal mode only
+ */
+
+ /* Real-Time Load Diagnostics */
+ SOC_SINGLE("CH1 RTLDG Switch", TAS675X_RTLDG_EN_REG, 3, 1, 0),
+ SOC_SINGLE("CH2 RTLDG Switch", TAS675X_RTLDG_EN_REG, 2, 1, 0),
+ SOC_SINGLE("CH3 RTLDG Switch", TAS675X_RTLDG_EN_REG, 1, 1, 0),
+ SOC_SINGLE("CH4 RTLDG Switch", TAS675X_RTLDG_EN_REG, 0, 1, 0),
+ SOC_SINGLE("RTLDG Clip Mask Switch", TAS675X_RTLDG_EN_REG, 4, 1, 0),
+ SOC_SINGLE("ISENSE Calibration Switch", TAS675X_ISENSE_CAL_REG, 3, 1, 0),
+ SOC_DSP_THRESH_EXT("RTLDG Open Load Threshold",
+ tas675x_dsp_defaults[TAS675X_DSP_PARAM_ID_OL_THRESH]),
+ SOC_DSP_THRESH_EXT("RTLDG Short Load Threshold",
+ tas675x_dsp_defaults[TAS675X_DSP_PARAM_ID_SL_THRESH]),
+ SOC_RTLDG_IMP_RO("CH1 RTLDG Impedance", TAS675X_CH1_RTLDG_IMP_MSB_REG),
+ SOC_RTLDG_IMP_RO("CH2 RTLDG Impedance", TAS675X_CH2_RTLDG_IMP_MSB_REG),
+ SOC_RTLDG_IMP_RO("CH3 RTLDG Impedance", TAS675X_CH3_RTLDG_IMP_MSB_REG),
+ SOC_RTLDG_IMP_RO("CH4 RTLDG Impedance", TAS675X_CH4_RTLDG_IMP_MSB_REG),
+ SOC_SINGLE_RO("RTLDG Fault Latched", TAS675X_RTLDG_OL_SL_FAULT_LATCHED_REG, 0, 0xFF),
+};
+
+static const struct snd_kcontrol_new tas675x_audio_path_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 1);
+
+static const struct snd_kcontrol_new tas675x_anc_path_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 1);
+
+static int tas675x_dapm_sleep_event(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = pm_runtime_resume_and_get(component->dev);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ pm_runtime_mark_last_busy(component->dev);
+ pm_runtime_put_autosuspend(component->dev);
+ break;
+ }
+ return ret < 0 ? ret : 0;
+}
+
+static const struct snd_soc_dapm_widget tas675x_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Analog Core", SND_SOC_NOPM, 0, 0,
+ tas675x_dapm_sleep_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("SDOUT Vpredict", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SDOUT Isense", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("Audio DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("ANC DAC", "ANC Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("Feedback ADC", "Feedback Capture", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SWITCH("Audio Path", SND_SOC_NOPM, 0, 0,
+ &tas675x_audio_path_switch),
+ SND_SOC_DAPM_SWITCH("ANC Path", SND_SOC_NOPM, 0, 0,
+ &tas675x_anc_path_switch),
+
+ /*
+ * Even though all channels are coupled in terms of power control,
+ * use logical outputs for each channel to allow independent routing
+ * and DAPM controls if needed.
+ */
+ SND_SOC_DAPM_OUTPUT("OUT_CH1"),
+ SND_SOC_DAPM_OUTPUT("OUT_CH2"),
+ SND_SOC_DAPM_OUTPUT("OUT_CH3"),
+ SND_SOC_DAPM_OUTPUT("OUT_CH4"),
+ SND_SOC_DAPM_INPUT("SPEAKER_LOAD"),
+};
+
+static const struct snd_soc_dapm_route tas675x_audio_map[] = {
+ { "Audio DAC", NULL, "Analog Core" },
+ { "Audio Path", "Switch", "Audio DAC" },
+ { "OUT_CH1", NULL, "Audio Path" },
+ { "OUT_CH2", NULL, "Audio Path" },
+ { "OUT_CH3", NULL, "Audio Path" },
+ { "OUT_CH4", NULL, "Audio Path" },
+
+ { "ANC DAC", NULL, "Analog Core" },
+ { "ANC Path", "Switch", "ANC DAC" },
+ { "OUT_CH1", NULL, "ANC Path" },
+ { "OUT_CH2", NULL, "ANC Path" },
+ { "OUT_CH3", NULL, "ANC Path" },
+ { "OUT_CH4", NULL, "ANC Path" },
+
+ { "Feedback ADC", NULL, "Analog Core" },
+ { "Feedback ADC", NULL, "SDOUT Vpredict" },
+ { "Feedback ADC", NULL, "SDOUT Isense" },
+ { "Feedback ADC", NULL, "SPEAKER_LOAD" },
+};
+
+static void tas675x_program_slot_offsets(struct tas675x_priv *tas,
+ int dai_id, int slot_width)
+{
+ int offset = 0;
+
+ switch (dai_id) {
+ case 0:
+ /* Standard Audio on SDIN */
+ if (tas->audio_slot >= 0)
+ offset = tas->audio_slot * slot_width;
+ else if (tas->tx_mask)
+ offset = __ffs(tas->tx_mask) * slot_width;
+ else
+ return;
+ offset += tas->bclk_offset;
+ regmap_update_bits(tas->regmap, TAS675X_SDIN_OFFSET_MSB_REG,
+ TAS675X_SDIN_AUDIO_OFF_MSB_MASK,
+ FIELD_PREP(TAS675X_SDIN_AUDIO_OFF_MSB_MASK, offset >> 8));
+ regmap_write(tas->regmap, TAS675X_SDIN_AUDIO_OFFSET_REG,
+ offset & 0xFF);
+ break;
+ case 1:
+ /*
+ * Low-Latency Playback on SDIN, **only** enabled in LLP mode
+ * and to be mixed with main audio before output amplification
+ * to achieve ANC/RNC.
+ */
+ if (tas->llp_slot >= 0)
+ offset = tas->llp_slot * slot_width;
+ else if (tas->tx_mask)
+ offset = __ffs(tas->tx_mask) * slot_width;
+ else
+ return;
+ offset += tas->bclk_offset;
+ regmap_update_bits(tas->regmap, TAS675X_SDIN_OFFSET_MSB_REG,
+ TAS675X_SDIN_LL_OFF_MSB_MASK,
+ FIELD_PREP(TAS675X_SDIN_LL_OFF_MSB_MASK, offset >> 8));
+ regmap_write(tas->regmap, TAS675X_SDIN_LL_OFFSET_REG,
+ offset & 0xFF);
+ break;
+ case 2:
+ /* SDOUT Data Output (Vpredict + Isense feedback) */
+ if (!tas->slot_width)
+ break;
+ if (tas->vpredict_slot >= 0) {
+ offset = tas->vpredict_slot * slot_width;
+ offset += tas->bclk_offset;
+ regmap_update_bits(tas->regmap, TAS675X_SDOUT_OFFSET_MSB_REG,
+ TAS675X_SDOUT_VP_OFF_MSB_MASK,
+ FIELD_PREP(TAS675X_SDOUT_VP_OFF_MSB_MASK, offset >> 8));
+ regmap_write(tas->regmap, TAS675X_VPREDICT_OFFSET_REG,
+ offset & 0xFF);
+ }
+ if (tas->isense_slot >= 0) {
+ offset = tas->isense_slot * slot_width;
+ offset += tas->bclk_offset;
+ regmap_update_bits(tas->regmap, TAS675X_SDOUT_OFFSET_MSB_REG,
+ TAS675X_SDOUT_IS_OFF_MSB_MASK,
+ FIELD_PREP(TAS675X_SDOUT_IS_OFF_MSB_MASK, offset >> 8));
+ regmap_write(tas->regmap, TAS675X_ISENSE_OFFSET_REG,
+ offset & 0xFF);
+ }
+ break;
+ }
+
+ if (offset > 511)
+ dev_warn(tas->dev,
+ "DAI %d slot offset %d exceeds 511 SCLK limit\n",
+ dai_id, offset);
+}
+
+static int tas675x_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;
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(component);
+ unsigned int rate = params_rate(params);
+ u8 word_length;
+
+ /*
+ * Single clock domain: SDIN and SDOUT share one SCLK/FSYNC pair,
+ * so all active DAIs must use the same sample rate.
+ */
+ if ((tas->active_playback_dais || tas->active_capture_dais) &&
+ tas->rate && tas->rate != rate) {
+ dev_err(component->dev,
+ "Rate %u conflicts with active rate %u\n",
+ rate, tas->rate);
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ word_length = TAS675X_WL_16BIT;
+ break;
+ case 20:
+ word_length = TAS675X_WL_20BIT;
+ break;
+ case 24:
+ word_length = TAS675X_WL_24BIT;
+ break;
+ case 32:
+ word_length = TAS675X_WL_32BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /*
+ * RTLDG is not supported above 96kHz. Auto-disable to
+ * prevent DSP overload and restore when rate drops back.
+ */
+ if (params_rate(params) > 96000) {
+ unsigned int val;
+
+ regmap_read(component->regmap, TAS675X_RTLDG_EN_REG,
+ &val);
+ if (val & TAS675X_RTLDG_CH_EN_MASK) {
+ tas->saved_rtldg_en = val;
+ dev_dbg(component->dev,
+ "Sample rate %dHz > 96kHz: Auto-disabling RTLDG\n",
+ params_rate(params));
+ regmap_update_bits(component->regmap,
+ TAS675X_RTLDG_EN_REG,
+ TAS675X_RTLDG_CH_EN_MASK,
+ 0x00);
+ }
+ } else if (tas->saved_rtldg_en) {
+ unsigned int cur;
+
+ /*
+ * Respect overrides and only restore if RTLDG is still auto-disabled
+ */
+ regmap_read(component->regmap, TAS675X_RTLDG_EN_REG,
+ &cur);
+ if (!(cur & TAS675X_RTLDG_CH_EN_MASK)) {
+ dev_dbg(component->dev,
+ "Restoring RTLDG config after high-rate stream\n");
+ regmap_update_bits(component->regmap,
+ TAS675X_RTLDG_EN_REG,
+ TAS675X_RTLDG_CH_EN_MASK,
+ TAS675X_RTLDG_CH_EN_MASK &
+ tas->saved_rtldg_en);
+ }
+ tas->saved_rtldg_en = 0;
+ }
+
+ /* Set SDIN word length (audio path + low-latency path) */
+ regmap_update_bits(component->regmap, TAS675X_SDIN_CTRL_REG,
+ TAS675X_SDIN_WL_MASK,
+ FIELD_PREP(TAS675X_SDIN_AUDIO_WL_MASK, word_length) |
+ FIELD_PREP(TAS675X_SDIN_LL_WL_MASK, word_length));
+ } else {
+ /* Set SDOUT word length (VPREDICT + ISENSE) for capture */
+ regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
+ TAS675X_SDOUT_WL_MASK,
+ FIELD_PREP(TAS675X_SDOUT_VP_WL_MASK, word_length) |
+ FIELD_PREP(TAS675X_SDOUT_IS_WL_MASK, word_length));
+ }
+
+ tas675x_program_slot_offsets(tas, dai->id,
+ tas->slot_width ?: params_width(params));
+
+ tas->rate = rate;
+
+ return 0;
+}
+
+static int tas675x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(component);
+ bool tdm_mode = false, i2s_mode = false;
+
+ /* Enforce Clocking Direction (Codec is strictly a consumer) */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ default:
+ dev_err(component->dev, "Unsupported clock provider format\n");
+ return -EINVAL;
+ }
+
+ /* SCLK polarity: NB_NF or IB_NF only (no FSYNC inversion support) */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ regmap_update_bits(component->regmap, TAS675X_SCLK_INV_CTRL_REG,
+ TAS675X_SCLK_INV_MASK, 0x00);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regmap_update_bits(component->regmap, TAS675X_SCLK_INV_CTRL_REG,
+ TAS675X_SCLK_INV_MASK, TAS675X_SCLK_INV_MASK);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported clock inversion\n");
+ return -EINVAL;
+ }
+
+ /* Configure Audio Format and TDM Enable */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ i2s_mode = true;
+ tas->bclk_offset = 0;
+ regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
+ TAS675X_FS_PULSE_MASK,
+ TAS675X_SAP_FMT_I2S);
+ regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
+ TAS675X_SDOUT_SELECT_MASK,
+ TAS675X_SDOUT_SELECT_NON_TDM);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ tas->bclk_offset = 0;
+ regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
+ TAS675X_FS_PULSE_MASK,
+ TAS675X_SAP_FMT_RIGHT_J);
+ regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
+ TAS675X_SDOUT_SELECT_MASK,
+ TAS675X_SDOUT_SELECT_NON_TDM);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ tas->bclk_offset = 0;
+ regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
+ TAS675X_FS_PULSE_MASK,
+ TAS675X_SAP_FMT_LEFT_J);
+ regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
+ TAS675X_SDOUT_SELECT_MASK,
+ TAS675X_SDOUT_SELECT_NON_TDM);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ tdm_mode = true;
+ tas->bclk_offset = 1;
+ regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
+ TAS675X_FS_PULSE_MASK,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_TDM |
+ TAS675X_FS_PULSE_SHORT);
+ regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
+ TAS675X_SDOUT_SELECT_MASK,
+ TAS675X_SDOUT_SELECT_TDM_SDOUT1);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ tdm_mode = true;
+ tas->bclk_offset = 0;
+ regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
+ TAS675X_FS_PULSE_MASK,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_TDM |
+ TAS675X_FS_PULSE_SHORT);
+ regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
+ TAS675X_SDOUT_SELECT_MASK,
+ TAS675X_SDOUT_SELECT_TDM_SDOUT1);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ /* Setup Vpredict and Isense outputs */
+ if (dai->id == 2) {
+ unsigned int sdout_en = 0;
+
+ if (tdm_mode) {
+ /* TDM: Vpredict and Isense may coexist on separate slots */
+ if (tas->vpredict_slot >= 0)
+ sdout_en |= TAS675X_SDOUT_EN_VPREDICT;
+ if (tas->isense_slot >= 0)
+ sdout_en |= TAS675X_SDOUT_EN_ISENSE;
+ regmap_update_bits(component->regmap,
+ TAS675X_SDOUT_EN_REG,
+ TAS675X_SDOUT_EN_VPREDICT |
+ TAS675X_SDOUT_EN_ISENSE,
+ sdout_en);
+ if (tas->vpredict_slot >= 0 && tas->isense_slot >= 0 &&
+ abs(tas->vpredict_slot - tas->isense_slot) < 4)
+ dev_warn(component->dev,
+ "ti,vpredict-slot-no and ti,isense-slot-no overlaps (each occupies 4 consecutive slots)\n");
+ } else if (i2s_mode) {
+ /* I2S: only one source at a time; Vpredict takes priority */
+ if (tas->vpredict_slot >= 0)
+ sdout_en = TAS675X_SDOUT_NON_TDM_SEL_VPREDICT |
+ TAS675X_SDOUT_EN_NON_TDM_ALL;
+ else if (tas->isense_slot >= 0)
+ sdout_en = TAS675X_SDOUT_NON_TDM_SEL_ISENSE |
+ TAS675X_SDOUT_EN_NON_TDM_ALL;
+ regmap_update_bits(component->regmap,
+ TAS675X_SDOUT_EN_REG,
+ TAS675X_SDOUT_NON_TDM_SEL_MASK |
+ TAS675X_SDOUT_EN_NON_TDM_ALL,
+ sdout_en);
+ if (sdout_en &&
+ tas->gpio1_func != TAS675X_GPIO_SEL_SDOUT2 &&
+ tas->gpio2_func != TAS675X_GPIO_SEL_SDOUT2)
+ dev_warn(component->dev,
+ "sdout enabled in I2S mode but no GPIO configured as SDOUT2; Ch3/Ch4 will be absent\n");
+ }
+ }
+
+ return 0;
+}
+
+static int tas675x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(dai->component);
+
+ if (slots == 0) {
+ tas->slot_width = 0;
+ tas->tx_mask = 0;
+ return 0;
+ }
+
+ /* No rx_mask as hardware does not support channel muxing for capture */
+ tas->slot_width = slot_width;
+ tas->tx_mask = tx_mask;
+ return 0;
+}
+
+static int tas675x_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(component);
+ unsigned int discard;
+ int ret;
+
+ if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+ if (mute)
+ clear_bit(dai->id, &tas->active_capture_dais);
+ else
+ set_bit(dai->id, &tas->active_capture_dais);
+ return 0;
+ }
+
+ /*
+ * Track which playback DAIs are active.
+ * The TAS675x has two playback DAIs (main audio and LLP).
+ * Only transition to Hi-Z when ALL are muted.
+ */
+ if (mute)
+ clear_bit(dai->id, &tas->active_playback_dais);
+ else
+ set_bit(dai->id, &tas->active_playback_dais);
+
+ /* Last playback stream */
+ if (mute && !tas->active_playback_dais) {
+ ret = tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
+ regmap_read(tas->regmap, TAS675X_CLK_FAULT_LATCHED_REG, &discard);
+ return ret;
+ }
+
+ return tas675x_set_state_all(tas,
+ tas->active_playback_dais ?
+ TAS675X_STATE_PLAY_BOTH :
+ TAS675X_STATE_SLEEP_BOTH);
+}
+
+static const struct snd_soc_dai_ops tas675x_dai_ops = {
+ .hw_params = tas675x_hw_params,
+ .set_fmt = tas675x_set_dai_fmt,
+ .set_tdm_slot = tas675x_set_tdm_slot,
+ .mute_stream = tas675x_mute_stream,
+};
+
+static struct snd_soc_dai_driver tas675x_dais[] = {
+ {
+ .name = "tas675x-audio",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &tas675x_dai_ops,
+ },
+ /* Only available when Low Latency Path (LLP) is enabled */
+ {
+ .name = "tas675x-anc",
+ .id = 1,
+ .playback = {
+ .stream_name = "ANC Playback",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &tas675x_dai_ops,
+ },
+ {
+ .name = "tas675x-feedback",
+ .id = 2,
+ .capture = {
+ .stream_name = "Feedback Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &tas675x_dai_ops,
+ }
+};
+
+/**
+ * Hardware power sequencing
+ * Handles regulator enable and GPIO deassertion.
+ * The device is not be I2C-accessible until boot wait completes.
+ */
+static int tas675x_hw_enable(struct tas675x_priv *tas)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas->supplies), tas->supplies);
+ if (ret) {
+ dev_err(tas->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ if (!IS_ERR(tas->vbat)) {
+ ret = regulator_enable(tas->vbat);
+ if (ret) {
+ dev_err(tas->dev, "Failed to enable vbat: %d\n", ret);
+ regulator_bulk_disable(ARRAY_SIZE(tas->supplies), tas->supplies);
+ return ret;
+ }
+ }
+
+ if (tas->pd_gpio && tas->stby_gpio) {
+ /*
+ * Independent Pin Control
+ * Deassert PD first to boot digital, then STBY for analog.
+ */
+ /* Min 4ms digital boot wait */
+ gpiod_set_value_cansleep(tas->pd_gpio, 0);
+ usleep_range(4000, 5000);
+
+ /* ~2ms analog stabilization */
+ gpiod_set_value_cansleep(tas->stby_gpio, 0);
+ usleep_range(2000, 3000);
+ } else if (tas->pd_gpio) {
+ /*
+ * Simultaneous Pin Release
+ * STBY tied to PD or hardwired HIGH.
+ */
+ /* 6ms wait for simultaneous release transition */
+ gpiod_set_value_cansleep(tas->pd_gpio, 0);
+ usleep_range(6000, 7000);
+ } else {
+ /*
+ * PD hardwired, device in DEEP_SLEEP.
+ * Digital core already booted, I2C active. Deassert STBY
+ * to bring up the analog output stage.
+ */
+ /* ~2ms analog stabilization */
+ gpiod_set_value_cansleep(tas->stby_gpio, 0);
+ usleep_range(2000, 3000);
+ }
+
+ return 0;
+}
+
+static void tas675x_hw_disable(struct tas675x_priv *tas)
+{
+ if (tas->stby_gpio)
+ gpiod_set_value_cansleep(tas->stby_gpio, 1);
+
+ if (tas->pd_gpio)
+ gpiod_set_value_cansleep(tas->pd_gpio, 1);
+
+ /*
+ * Hold PD/STBY asserted for at least 10ms
+ * before removing PVDD, VBAT or DVDD.
+ */
+ usleep_range(10000, 11000);
+
+ if (!IS_ERR(tas->vbat))
+ regulator_disable(tas->vbat);
+
+ regulator_bulk_disable(ARRAY_SIZE(tas->supplies), tas->supplies);
+}
+
+/**
+ * Device start-up defaults
+ * Must be called after tas675x_hw_enable() and after regcache is enabled.
+ */
+static int tas675x_init_device(struct tas675x_priv *tas)
+{
+ struct regmap *regmap = tas->regmap;
+ unsigned int val;
+ int ret, i;
+
+ /* Clear POR fault flag to prevent IRQ storm */
+ regmap_read(regmap, TAS675X_POWER_FAULT_LATCHED_REG, &val);
+
+ /* Bypass DC Load Diagnostics for fast boot */
+ if (tas->fast_boot)
+ regmap_update_bits(regmap, TAS675X_DC_LDG_CTRL_REG,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT);
+
+ tas675x_select_book(regmap, TAS675X_BOOK_DEFAULT);
+
+ /* Enter setup mode */
+ ret = regmap_write(regmap, TAS675X_SETUP_REG1, TAS675X_SETUP_ENTER_VAL1);
+ if (ret)
+ goto err;
+ ret = regmap_write(regmap, TAS675X_SETUP_REG2, TAS675X_SETUP_ENTER_VAL2);
+ if (ret)
+ goto err;
+
+ /* Set all channels to Sleep (required before Page 1 config) */
+ tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
+
+ /* Set DAC clock per TRM startup script */
+ regmap_write(regmap, TAS675X_DAC_CLK_REG, 0x00);
+
+ /*
+ * Switch to Page 1 for safety-critical OC/CBC configuration,
+ * while bypassing regcache. (Page 1 not accessible post setup)
+ */
+ regcache_cache_bypass(regmap, true);
+ ret = regmap_multi_reg_write(regmap, tas675x_page1_init,
+ ARRAY_SIZE(tas675x_page1_init));
+ regcache_cache_bypass(regmap, false);
+ if (ret)
+ goto err_setup;
+
+ /* Resync regmap's cached page selector */
+ regmap_write(regmap, TAS675X_PAGE_CTRL_REG, 0x00);
+
+ /* Exit setup mode */
+ regmap_write(regmap, TAS675X_SETUP_REG1, TAS675X_SETUP_EXIT_VAL);
+ regmap_write(regmap, TAS675X_SETUP_REG2, TAS675X_SETUP_EXIT_VAL);
+
+ /* Write DSP parameters if cached */
+ for (i = 0; i < ARRAY_SIZE(tas->dsp_params); i++) {
+ if (tas->dsp_params[i].val)
+ tas675x_dsp_mem_write(tas,
+ tas->dsp_params[i].page,
+ tas->dsp_params[i].reg,
+ tas->dsp_params[i].val);
+ }
+
+ /*
+ * Route fault events to FAULT pin for IRQ handler
+ *
+ * ROUTING_1: latched CP fault, CP UVLO, OUTM soft short
+ * ROUTING_2: non-latching power fault events (+ default OTSD, CBC)
+ * ROUTING_4: OTW, clip warning, protection shutdown, OC, DC
+ * ROUTING_5: clock, CBC warning, RTLDG, clip detect
+ */
+ regmap_write(regmap, TAS675X_REPORT_ROUTING_1_REG, 0x70);
+ regmap_write(regmap, TAS675X_REPORT_ROUTING_2_REG, 0xA3);
+ regmap_write(regmap, TAS675X_REPORT_ROUTING_4_REG, 0x7E);
+ regmap_write(regmap, TAS675X_REPORT_ROUTING_5_REG, 0xF3);
+
+ /* Configure GPIO pins if specified in DT */
+ if (tas->gpio1_func >= 0 || tas->gpio2_func >= 0) {
+ unsigned int gpio_ctrl = TAS675X_GPIO_CTRL_RSTVAL;
+
+ tas675x_config_gpio_pin(regmap, tas->gpio1_func,
+ TAS675X_GPIO1_OUTPUT_SEL_REG,
+ 0, &gpio_ctrl);
+ tas675x_config_gpio_pin(regmap, tas->gpio2_func,
+ TAS675X_GPIO2_OUTPUT_SEL_REG,
+ 1, &gpio_ctrl);
+ regmap_write(regmap, TAS675X_GPIO_CTRL_REG, gpio_ctrl);
+ }
+
+ /* Clear fast boot bits */
+ if (tas->fast_boot)
+ regmap_update_bits(regmap, TAS675X_DC_LDG_CTRL_REG,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
+ 0);
+
+ /* Clear any stale faults from the boot sequence */
+ regmap_read(regmap, TAS675X_POWER_FAULT_LATCHED_REG, &val);
+ regmap_read(regmap, TAS675X_CLK_FAULT_LATCHED_REG, &val);
+ regmap_write(regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR);
+
+ return 0;
+
+err_setup:
+ regmap_write(regmap, TAS675X_SETUP_REG1, TAS675X_SETUP_EXIT_VAL);
+ regmap_write(regmap, TAS675X_SETUP_REG2, TAS675X_SETUP_EXIT_VAL);
+err:
+ dev_err(tas->dev, "Init device failed: %d\n", ret);
+ return ret;
+}
+
+static void tas675x_power_off(struct tas675x_priv *tas)
+{
+ regcache_cache_only(tas->regmap, true);
+ regcache_mark_dirty(tas->regmap);
+ tas675x_hw_disable(tas);
+}
+
+static int tas675x_power_on(struct tas675x_priv *tas)
+{
+ int ret;
+
+ ret = tas675x_hw_enable(tas);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(tas->regmap, false);
+ regcache_mark_dirty(tas->regmap);
+
+ ret = tas675x_init_device(tas);
+ if (ret)
+ goto err_disable;
+
+ ret = regcache_sync(tas->regmap);
+ if (ret) {
+ dev_err(tas->dev, "Failed to sync regcache: %d\n", ret);
+ goto err_disable;
+ }
+
+ /* Reset fault tracking */
+ memset(tas->last_status, 0, sizeof(tas->last_status));
+
+ return 0;
+
+err_disable:
+ tas675x_power_off(tas);
+ return ret;
+}
+
+static int tas675x_runtime_suspend(struct device *dev)
+{
+ struct tas675x_priv *tas = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&tas->fault_check_work);
+ tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
+
+ return 0;
+}
+
+static int tas675x_runtime_resume(struct device *dev)
+{
+ struct tas675x_priv *tas = dev_get_drvdata(dev);
+
+ tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
+
+ if (!to_i2c_client(dev)->irq)
+ schedule_delayed_work(&tas->fault_check_work,
+ msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS));
+
+ return 0;
+}
+
+static int tas675x_system_suspend(struct device *dev)
+{
+ struct tas675x_priv *tas = dev_get_drvdata(dev);
+ int ret;
+
+ ret = tas675x_runtime_suspend(dev);
+ if (ret)
+ return ret;
+
+ tas675x_power_off(tas);
+ return 0;
+}
+
+static int tas675x_system_resume(struct device *dev)
+{
+ struct tas675x_priv *tas = dev_get_drvdata(dev);
+ int ret;
+
+ ret = tas675x_power_on(tas);
+ if (ret)
+ return ret;
+
+ return tas675x_runtime_resume(dev);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_tas675x = {
+ .controls = tas675x_snd_controls,
+ .num_controls = ARRAY_SIZE(tas675x_snd_controls),
+ .dapm_widgets = tas675x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas675x_dapm_widgets),
+ .dapm_routes = tas675x_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas675x_audio_map),
+ .endianness = 1,
+};
+
+/* Fault register flags */
+#define TAS675X_FAULT_CRITICAL BIT(0) /* causes FAULT state, FAULT_CLEAR required */
+#define TAS675X_FAULT_TRACK BIT(1) /* track last value, only log on change */
+#define TAS675X_FAULT_ACTIVE BIT(2) /* skip when no stream is active */
+
+struct tas675x_fault_reg {
+ unsigned int reg;
+ unsigned int flags;
+ const char *name;
+};
+
+static const struct tas675x_fault_reg tas675x_fault_table[] = {
+ /* Critical */
+ { TAS675X_OTSD_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK,
+ "Overtemperature Shutdown" },
+ { TAS675X_OC_DC_FAULT_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK,
+ "Overcurrent / DC Fault" },
+ { TAS675X_RTLDG_OL_SL_FAULT_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK,
+ "Real-Time Load Diagnostic Fault" },
+ { TAS675X_CBC_FAULT_WARN_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK,
+ "CBC Fault/Warning" },
+ /* Warning */
+ { TAS675X_POWER_FAULT_LATCHED_REG, TAS675X_FAULT_TRACK,
+ "Power Fault" },
+ { TAS675X_CLK_FAULT_LATCHED_REG, TAS675X_FAULT_TRACK | TAS675X_FAULT_ACTIVE,
+ "Clock Fault" },
+ { TAS675X_OTW_LATCHED_REG, TAS675X_FAULT_TRACK,
+ "Overtemperature Warning" },
+ { TAS675X_CLIP_WARN_LATCHED_REG, TAS675X_FAULT_ACTIVE,
+ "Clip Warning" },
+};
+
+static_assert(ARRAY_SIZE(tas675x_fault_table) == TAS675X_FAULT_REGS_NUM);
+
+/**
+ * Read and log all latched fault registers
+ * Shared by both the polled fault_check_work and IRQ handler paths
+ * (which are mutually exclusive, only one is active per device).
+ *
+ * Returns true if any critical fault was detected that needs FAULT_CLEAR.
+ */
+static bool tas675x_check_faults(struct tas675x_priv *tas)
+{
+ struct device *dev = tas->dev;
+ bool is_latched = false;
+ unsigned int reg;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(tas675x_fault_table); i++) {
+ const struct tas675x_fault_reg *f = &tas675x_fault_table[i];
+
+ ret = regmap_read(tas->regmap, f->reg, ®);
+ if (ret) {
+ if (f->flags & TAS675X_FAULT_CRITICAL) {
+ dev_err(dev, "failed to read %s: %d\n", f->name, ret);
+ return is_latched;
+ }
+ continue;
+ }
+
+ if ((f->flags & TAS675X_FAULT_ACTIVE) &&
+ !READ_ONCE(tas->active_playback_dais) &&
+ !READ_ONCE(tas->active_capture_dais))
+ continue;
+
+ if ((f->flags & TAS675X_FAULT_CRITICAL) && reg)
+ is_latched = true;
+
+ /* Log on change or on every non-zero read */
+ if (reg && (!(f->flags & TAS675X_FAULT_TRACK) ||
+ reg != tas->last_status[i])) {
+ if (f->flags & TAS675X_FAULT_CRITICAL)
+ dev_crit(dev, "%s Latched: 0x%02x\n", f->name, reg);
+ else
+ dev_warn(dev, "%s Latched: 0x%02x\n", f->name, reg);
+ }
+
+ if (f->flags & TAS675X_FAULT_TRACK)
+ tas->last_status[i] = reg;
+ }
+
+ return is_latched;
+}
+
+static void tas675x_fault_check_work(struct work_struct *work)
+{
+ struct tas675x_priv *tas = container_of(work, struct tas675x_priv,
+ fault_check_work.work);
+
+ if (tas675x_check_faults(tas))
+ regmap_write(tas->regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR);
+
+ schedule_delayed_work(&tas->fault_check_work,
+ msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS));
+}
+
+static irqreturn_t tas675x_irq_handler(int irq, void *data)
+{
+ struct tas675x_priv *tas = data;
+
+ tas675x_check_faults(tas);
+
+ /* Clear the FAULT pin latch as something latched */
+ regmap_write(tas->regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR);
+
+ return IRQ_HANDLED;
+}
+
+static const struct reg_default tas675x_reg_defaults[] = {
+ { TAS675X_PAGE_CTRL_REG, 0x00 },
+ { TAS675X_OUTPUT_CTRL_REG, 0x00 },
+ { TAS675X_STATE_CTRL_CH1_CH2_REG, TAS675X_STATE_SLEEP_BOTH },
+ { TAS675X_STATE_CTRL_CH3_CH4_REG, TAS675X_STATE_SLEEP_BOTH },
+ { TAS675X_ISENSE_CTRL_REG, 0x0F },
+ { TAS675X_DC_DETECT_CTRL_REG, 0x00 },
+ { TAS675X_SCLK_INV_CTRL_REG, 0x00 },
+ { TAS675X_AUDIO_IF_CTRL_REG, 0x00 },
+ { TAS675X_SDIN_CTRL_REG, 0x0A },
+ { TAS675X_SDOUT_CTRL_REG, 0x1A },
+ { TAS675X_SDIN_OFFSET_MSB_REG, 0x00 },
+ { TAS675X_SDIN_AUDIO_OFFSET_REG, 0x00 },
+ { TAS675X_SDIN_LL_OFFSET_REG, 0x60 },
+ { TAS675X_SDIN_CH_SWAP_REG, 0x00 },
+ { TAS675X_SDOUT_OFFSET_MSB_REG, 0xCF },
+ { TAS675X_VPREDICT_OFFSET_REG, 0xFF },
+ { TAS675X_ISENSE_OFFSET_REG, 0x00 },
+ { TAS675X_SDOUT_EN_REG, 0x00 },
+ { TAS675X_LL_EN_REG, 0x00 },
+ { TAS675X_RTLDG_EN_REG, 0x10 },
+ { TAS675X_DC_BLOCK_BYP_REG, 0x00 },
+ { TAS675X_DSP_CTRL_REG, 0x00 },
+ { TAS675X_PAGE_AUTO_INC_REG, 0x00 },
+ { TAS675X_DIG_VOL_CH1_REG, 0x30 },
+ { TAS675X_DIG_VOL_CH2_REG, 0x30 },
+ { TAS675X_DIG_VOL_CH3_REG, 0x30 },
+ { TAS675X_DIG_VOL_CH4_REG, 0x30 },
+ { TAS675X_DIG_VOL_RAMP_CTRL_REG, 0x77 },
+ { TAS675X_DIG_VOL_COMBINE_CTRL_REG, 0x00 },
+ { TAS675X_AUTO_MUTE_EN_REG, 0x00 },
+ { TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG, 0x00 },
+ { TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG, 0x00 },
+ { TAS675X_ANALOG_GAIN_CH1_CH2_REG, 0x00 },
+ { TAS675X_ANALOG_GAIN_CH3_CH4_REG, 0x00 },
+ { TAS675X_ANALOG_GAIN_RAMP_CTRL_REG, 0x00 },
+ { TAS675X_PULSE_INJECTION_EN_REG, 0x03 },
+ { TAS675X_CBC_CTRL_REG, 0x07 },
+ { TAS675X_CURRENT_LIMIT_CTRL_REG, 0x00 },
+ { TAS675X_ISENSE_CAL_REG, 0x00 },
+ { TAS675X_PWM_PHASE_CTRL_REG, 0x00 },
+ { TAS675X_SS_CTRL_REG, 0x00 },
+ { TAS675X_SS_RANGE_CTRL_REG, 0x00 },
+ { TAS675X_SS_DWELL_CTRL_REG, 0x00 },
+ { TAS675X_RAMP_PHASE_CTRL_GPO_REG, 0x00 },
+ { TAS675X_PWM_PHASE_M_CTRL_CH1_REG, 0x00 },
+ { TAS675X_PWM_PHASE_M_CTRL_CH2_REG, 0x00 },
+ { TAS675X_PWM_PHASE_M_CTRL_CH3_REG, 0x00 },
+ { TAS675X_PWM_PHASE_M_CTRL_CH4_REG, 0x00 },
+ { TAS675X_DC_LDG_CTRL_REG, 0x00 },
+ { TAS675X_DC_LDG_LO_CTRL_REG, 0x00 },
+ { TAS675X_DC_LDG_TIME_CTRL_REG, 0x00 },
+ { TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0x11 },
+ { TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0x11 },
+ { TAS675X_AC_LDG_CTRL_REG, 0x10 },
+ { TAS675X_TWEETER_DETECT_CTRL_REG, 0x08 },
+ { TAS675X_TWEETER_DETECT_THRESH_REG, 0x00 },
+ { TAS675X_AC_LDG_FREQ_CTRL_REG, 0xC8 },
+ { TAS675X_REPORT_ROUTING_1_REG, 0x00 },
+ { TAS675X_OTSD_RECOVERY_EN_REG, 0x00 },
+ { TAS675X_REPORT_ROUTING_2_REG, 0xA2 },
+ { TAS675X_REPORT_ROUTING_3_REG, 0x00 },
+ { TAS675X_REPORT_ROUTING_4_REG, 0x06 },
+ { TAS675X_CLIP_DETECT_CTRL_REG, 0x00 },
+ { TAS675X_REPORT_ROUTING_5_REG, 0x00 },
+ { TAS675X_GPIO1_OUTPUT_SEL_REG, 0x00 },
+ { TAS675X_GPIO2_OUTPUT_SEL_REG, 0x00 },
+ { TAS675X_GPIO_CTRL_REG, TAS675X_GPIO_CTRL_RSTVAL },
+ { TAS675X_OTW_CTRL_CH1_CH2_REG, 0x11 },
+ { TAS675X_OTW_CTRL_CH3_CH4_REG, 0x11 },
+};
+
+static bool tas675x_is_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS675X_RESET_REG:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool tas675x_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS675X_RESET_REG:
+ case TAS675X_BOOK_CTRL_REG:
+ case TAS675X_AUTO_MUTE_STATUS_REG:
+ case TAS675X_STATE_REPORT_CH1_CH2_REG:
+ case TAS675X_STATE_REPORT_CH3_CH4_REG:
+ case TAS675X_PVDD_SENSE_REG:
+ case TAS675X_TEMP_GLOBAL_REG:
+ case TAS675X_TEMP_CH1_CH2_REG:
+ case TAS675X_TEMP_CH3_CH4_REG:
+ case TAS675X_FS_MON_REG:
+ case TAS675X_SCLK_MON_REG:
+ case TAS675X_POWER_FAULT_STATUS_1_REG:
+ case TAS675X_POWER_FAULT_STATUS_2_REG:
+ case TAS675X_OT_FAULT_REG:
+ case TAS675X_OTW_STATUS_REG:
+ case TAS675X_CLIP_WARN_STATUS_REG:
+ case TAS675X_CBC_WARNING_STATUS_REG:
+ case TAS675X_POWER_FAULT_LATCHED_REG:
+ case TAS675X_OTSD_LATCHED_REG:
+ case TAS675X_OTW_LATCHED_REG:
+ case TAS675X_CLIP_WARN_LATCHED_REG:
+ case TAS675X_CLK_FAULT_LATCHED_REG:
+ case TAS675X_RTLDG_OL_SL_FAULT_LATCHED_REG:
+ case TAS675X_CBC_FAULT_WARN_LATCHED_REG:
+ case TAS675X_OC_DC_FAULT_LATCHED_REG:
+ case TAS675X_WARN_OT_MAX_FLAG_REG:
+ case TAS675X_DC_LDG_REPORT_CH1_CH2_REG ... TAS675X_TWEETER_REPORT_REG:
+ case TAS675X_CH1_RTLDG_IMP_MSB_REG ... TAS675X_CH4_DC_LDG_DCR_LSB_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_range_cfg tas675x_ranges[] = {
+ {
+ .name = "Pages",
+ .range_min = 0,
+ .range_max = TAS675X_PAGE_SIZE * TAS675X_PAGE_SIZE - 1,
+ .selector_reg = TAS675X_PAGE_CTRL_REG,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = TAS675X_PAGE_SIZE,
+ },
+};
+
+static void tas675x_regmap_lock(void *lock_arg)
+{
+ struct tas675x_priv *tas = lock_arg;
+
+ mutex_lock(&tas->io_lock);
+}
+
+static void tas675x_regmap_unlock(void *lock_arg)
+{
+ struct tas675x_priv *tas = lock_arg;
+
+ mutex_unlock(&tas->io_lock);
+}
+
+static const struct regmap_config tas675x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TAS675X_PAGE_SIZE * TAS675X_PAGE_SIZE - 1,
+ .ranges = tas675x_ranges,
+ .num_ranges = ARRAY_SIZE(tas675x_ranges),
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = tas675x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas675x_reg_defaults),
+ .readable_reg = tas675x_is_readable_register,
+ .volatile_reg = tas675x_is_volatile_register,
+};
+
+static int tas675x_i2c_probe(struct i2c_client *client)
+{
+ struct regmap_config cfg = tas675x_regmap_config;
+ struct tas675x_priv *tas;
+ u32 val;
+ int i, ret;
+
+ tas = devm_kzalloc(&client->dev, sizeof(*tas), GFP_KERNEL);
+ if (!tas)
+ return -ENOMEM;
+
+ tas->dev = &client->dev;
+ i2c_set_clientdata(client, tas);
+
+ mutex_init(&tas->io_lock);
+ cfg.lock = tas675x_regmap_lock;
+ cfg.unlock = tas675x_regmap_unlock;
+ cfg.lock_arg = tas;
+ tas->regmap = devm_regmap_init_i2c(client, &cfg);
+ if (IS_ERR(tas->regmap))
+ return PTR_ERR(tas->regmap);
+
+ memcpy(tas->dsp_params, tas675x_dsp_defaults, sizeof(tas->dsp_params));
+
+ /* Keep regmap cache-only until hardware is powered on */
+ regcache_cache_only(tas->regmap, true);
+
+ tas->dev_type = (enum tas675x_type)(unsigned long)device_get_match_data(tas->dev);
+ tas->fast_boot = device_property_read_bool(tas->dev, "ti,fast-boot");
+
+ tas->audio_slot = -1;
+ tas->llp_slot = -1;
+ tas->vpredict_slot = -1;
+ tas->isense_slot = -1;
+ if (!device_property_read_u32(tas->dev, "ti,audio-slot-no", &val))
+ tas->audio_slot = val;
+ if (!device_property_read_u32(tas->dev, "ti,llp-slot-no", &val))
+ tas->llp_slot = val;
+ if (!device_property_read_u32(tas->dev, "ti,vpredict-slot-no", &val))
+ tas->vpredict_slot = val;
+ if (!device_property_read_u32(tas->dev, "ti,isense-slot-no", &val))
+ tas->isense_slot = val;
+
+ tas->gpio1_func = tas675x_gpio_func_parse(tas->dev, "ti,gpio1-function");
+ tas->gpio2_func = tas675x_gpio_func_parse(tas->dev, "ti,gpio2-function");
+
+ for (i = 0; i < ARRAY_SIZE(tas675x_supply_names); i++)
+ tas->supplies[i].supply = tas675x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(tas->dev, ARRAY_SIZE(tas->supplies), tas->supplies);
+ if (ret)
+ return dev_err_probe(tas->dev, ret, "Failed to request supplies\n");
+
+ tas->vbat = devm_regulator_get_optional(tas->dev, "vbat");
+ if (IS_ERR(tas->vbat) && PTR_ERR(tas->vbat) != -ENODEV)
+ return dev_err_probe(tas->dev, PTR_ERR(tas->vbat),
+ "Failed to get vbat supply\n");
+
+ tas->pd_gpio = devm_gpiod_get_optional(tas->dev, "pd", GPIOD_OUT_HIGH);
+ if (IS_ERR(tas->pd_gpio))
+ return dev_err_probe(tas->dev, PTR_ERR(tas->pd_gpio), "Failed pd-gpios\n");
+
+ tas->stby_gpio = devm_gpiod_get_optional(tas->dev, "stby", GPIOD_OUT_HIGH);
+ if (IS_ERR(tas->stby_gpio))
+ return dev_err_probe(tas->dev, PTR_ERR(tas->stby_gpio), "Failed stby-gpios\n");
+
+ if (!tas->pd_gpio && !tas->stby_gpio)
+ return dev_err_probe(tas->dev, -EINVAL,
+ "At least one of pd-gpios or stby-gpios is required\n");
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(tas->dev, client->irq, NULL,
+ tas675x_irq_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
+ "tas675x-fault", tas);
+ if (ret)
+ return dev_err_probe(tas->dev, ret, "Failed to request IRQ\n");
+ }
+
+ INIT_DELAYED_WORK(&tas->fault_check_work, tas675x_fault_check_work);
+
+ ret = tas675x_power_on(tas);
+ if (ret)
+ return ret;
+
+ /* Enable runtime PM with 2s autosuspend */
+ pm_runtime_set_autosuspend_delay(tas->dev, 2000);
+ pm_runtime_use_autosuspend(tas->dev);
+ pm_runtime_set_active(tas->dev);
+ pm_runtime_mark_last_busy(tas->dev);
+ pm_runtime_enable(tas->dev);
+
+ ret = devm_snd_soc_register_component(tas->dev, &soc_codec_dev_tas675x,
+ tas675x_dais, ARRAY_SIZE(tas675x_dais));
+ if (ret)
+ goto err_pm_disable;
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_force_suspend(tas->dev);
+ pm_runtime_disable(tas->dev);
+ tas675x_power_off(tas);
+ return ret;
+}
+
+static void tas675x_i2c_remove(struct i2c_client *client)
+{
+ struct tas675x_priv *tas = dev_get_drvdata(&client->dev);
+
+ pm_runtime_force_suspend(&client->dev);
+ pm_runtime_disable(&client->dev);
+ tas675x_power_off(tas);
+}
+
+static const struct dev_pm_ops tas675x_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(tas675x_system_suspend, tas675x_system_resume)
+ RUNTIME_PM_OPS(tas675x_runtime_suspend, tas675x_runtime_resume, NULL)
+};
+
+static const struct of_device_id tas675x_of_match[] = {
+ { .compatible = "ti,tas6754", .data = (void *)TAS6754 },
+ { .compatible = "ti,tas67524", .data = (void *)TAS6754 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tas675x_of_match);
+
+static const struct i2c_device_id tas675x_i2c_id[] = {
+ { "tas6754", TAS6754 },
+ { "tas67524", TAS6754 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas675x_i2c_id);
+
+static struct i2c_driver tas675x_i2c_driver = {
+ .driver = {
+ .name = "tas675x",
+ .of_match_table = tas675x_of_match,
+ .pm = pm_ptr(&tas675x_pm_ops),
+ },
+ .probe = tas675x_i2c_probe,
+ .remove = tas675x_i2c_remove,
+ .id_table = tas675x_i2c_id,
+};
+
+module_i2c_driver(tas675x_i2c_driver);
+
+MODULE_AUTHOR("Sen Wang <sen@ti.com>");
+MODULE_DESCRIPTION("ASoC TAS675x Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas675x.h b/sound/soc/codecs/tas675x.h
new file mode 100644
index 000000000000..db29bb377336
--- /dev/null
+++ b/sound/soc/codecs/tas675x.h
@@ -0,0 +1,367 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ALSA SoC Texas Instruments TAS675x Quad-Channel Audio Amplifier
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Sen Wang <sen@ti.com>
+ */
+
+#ifndef __TAS675X_H__
+#define __TAS675X_H__
+
+/*
+ * Book 0, Page 0 — Register Addresses
+ */
+
+#define TAS675X_PAGE_SIZE 256
+#define TAS675X_PAGE_REG(page, reg) ((page) * TAS675X_PAGE_SIZE + (reg))
+
+/* Page Control & Basic Config */
+#define TAS675X_PAGE_CTRL_REG 0x00
+#define TAS675X_RESET_REG 0x01
+#define TAS675X_OUTPUT_CTRL_REG 0x02
+#define TAS675X_STATE_CTRL_CH1_CH2_REG 0x03
+#define TAS675X_STATE_CTRL_CH3_CH4_REG 0x04
+#define TAS675X_ISENSE_CTRL_REG 0x05
+#define TAS675X_DC_DETECT_CTRL_REG 0x06
+
+/* Serial Audio Port */
+#define TAS675X_SCLK_INV_CTRL_REG 0x20
+#define TAS675X_AUDIO_IF_CTRL_REG 0x21
+#define TAS675X_SDIN_CTRL_REG 0x23
+#define TAS675X_SDOUT_CTRL_REG 0x25
+#define TAS675X_SDIN_OFFSET_MSB_REG 0x27
+#define TAS675X_SDIN_AUDIO_OFFSET_REG 0x28
+#define TAS675X_SDIN_LL_OFFSET_REG 0x29
+#define TAS675X_SDIN_CH_SWAP_REG 0x2A
+#define TAS675X_SDOUT_OFFSET_MSB_REG 0x2C
+#define TAS675X_VPREDICT_OFFSET_REG 0x2D
+#define TAS675X_ISENSE_OFFSET_REG 0x2E
+#define TAS675X_SDOUT_EN_REG 0x31
+#define TAS675X_LL_EN_REG 0x32
+
+/* DSP & Core Audio Control */
+#define TAS675X_RTLDG_EN_REG 0x37
+#define TAS675X_DC_BLOCK_BYP_REG 0x39
+#define TAS675X_DSP_CTRL_REG 0x3A
+#define TAS675X_PAGE_AUTO_INC_REG 0x3B
+
+/* Volume & Mute */
+#define TAS675X_DIG_VOL_CH1_REG 0x40
+#define TAS675X_DIG_VOL_CH2_REG 0x41
+#define TAS675X_DIG_VOL_CH3_REG 0x42
+#define TAS675X_DIG_VOL_CH4_REG 0x43
+#define TAS675X_DIG_VOL_RAMP_CTRL_REG 0x44
+#define TAS675X_DIG_VOL_COMBINE_CTRL_REG 0x46
+#define TAS675X_AUTO_MUTE_EN_REG 0x47
+#define TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG 0x48
+#define TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG 0x49
+
+/* Analog Gain & Power Stage */
+#define TAS675X_ANALOG_GAIN_CH1_CH2_REG 0x4A
+#define TAS675X_ANALOG_GAIN_CH3_CH4_REG 0x4B
+#define TAS675X_ANALOG_GAIN_RAMP_CTRL_REG 0x4E
+#define TAS675X_PULSE_INJECTION_EN_REG 0x52
+#define TAS675X_CBC_CTRL_REG 0x54
+#define TAS675X_CURRENT_LIMIT_CTRL_REG 0x55
+#define TAS675X_DAC_CLK_REG 0x5A
+#define TAS675X_ISENSE_CAL_REG 0x5B
+
+/* Spread Spectrum & PWM Phase */
+#define TAS675X_PWM_PHASE_CTRL_REG 0x60
+#define TAS675X_SS_CTRL_REG 0x61
+#define TAS675X_SS_RANGE_CTRL_REG 0x62
+#define TAS675X_SS_DWELL_CTRL_REG 0x66
+#define TAS675X_RAMP_PHASE_CTRL_GPO_REG 0x68
+#define TAS675X_PWM_PHASE_M_CTRL_CH1_REG 0x69
+#define TAS675X_PWM_PHASE_M_CTRL_CH2_REG 0x6A
+#define TAS675X_PWM_PHASE_M_CTRL_CH3_REG 0x6B
+#define TAS675X_PWM_PHASE_M_CTRL_CH4_REG 0x6C
+
+/* Status & Reporting */
+#define TAS675X_AUTO_MUTE_STATUS_REG 0x71
+#define TAS675X_STATE_REPORT_CH1_CH2_REG 0x72
+#define TAS675X_STATE_REPORT_CH3_CH4_REG 0x73
+#define TAS675X_PVDD_SENSE_REG 0x74
+#define TAS675X_TEMP_GLOBAL_REG 0x75
+#define TAS675X_FS_MON_REG 0x76
+#define TAS675X_SCLK_MON_REG 0x77
+#define TAS675X_REPORT_ROUTING_1_REG 0x7C
+
+/* Memory Paging & Book Control */
+#define TAS675X_SETUP_REG1 0x7D
+#define TAS675X_SETUP_REG2 0x7E
+#define TAS675X_BOOK_CTRL_REG 0x7F
+
+/* Fault Status */
+#define TAS675X_POWER_FAULT_STATUS_1_REG 0x7D
+#define TAS675X_POWER_FAULT_STATUS_2_REG 0x80
+#define TAS675X_OT_FAULT_REG 0x81
+#define TAS675X_OTW_STATUS_REG 0x82
+#define TAS675X_CLIP_WARN_STATUS_REG 0x83
+#define TAS675X_CBC_WARNING_STATUS_REG 0x85
+
+/* Latched Fault Registers */
+#define TAS675X_POWER_FAULT_LATCHED_REG 0x86
+#define TAS675X_OTSD_LATCHED_REG 0x87
+#define TAS675X_OTW_LATCHED_REG 0x88
+#define TAS675X_CLIP_WARN_LATCHED_REG 0x89
+#define TAS675X_CLK_FAULT_LATCHED_REG 0x8A
+#define TAS675X_RTLDG_OL_SL_FAULT_LATCHED_REG 0x8B
+#define TAS675X_CBC_FAULT_WARN_LATCHED_REG 0x8D
+#define TAS675X_OC_DC_FAULT_LATCHED_REG 0x8E
+#define TAS675X_OTSD_RECOVERY_EN_REG 0x8F
+
+/* Protection & Routing Controls */
+#define TAS675X_REPORT_ROUTING_2_REG 0x90
+#define TAS675X_REPORT_ROUTING_3_REG 0x91
+#define TAS675X_REPORT_ROUTING_4_REG 0x92
+#define TAS675X_CLIP_DETECT_CTRL_REG 0x93
+#define TAS675X_REPORT_ROUTING_5_REG 0x94
+
+/* GPIO Pin Configuration */
+#define TAS675X_GPIO1_OUTPUT_SEL_REG 0x95
+#define TAS675X_GPIO2_OUTPUT_SEL_REG 0x96
+#define TAS675X_GPIO_INPUT_SLEEP_HIZ_REG 0x9B
+#define TAS675X_GPIO_INPUT_PLAY_SLEEP_REG 0x9C
+#define TAS675X_GPIO_INPUT_MUTE_REG 0x9D
+#define TAS675X_GPIO_INPUT_SYNC_REG 0x9E
+#define TAS675X_GPIO_INPUT_SDIN2_REG 0x9F
+#define TAS675X_GPIO_CTRL_REG 0xA0
+#define TAS675X_GPIO_INVERT_REG 0xA1
+
+/* Load Diagnostics Config */
+#define TAS675X_DC_LDG_CTRL_REG 0xB0
+#define TAS675X_DC_LDG_LO_CTRL_REG 0xB1
+#define TAS675X_DC_LDG_TIME_CTRL_REG 0xB2
+#define TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG 0xB3
+#define TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG 0xB4
+#define TAS675X_AC_LDG_CTRL_REG 0xB5
+#define TAS675X_TWEETER_DETECT_CTRL_REG 0xB6
+#define TAS675X_TWEETER_DETECT_THRESH_REG 0xB7
+#define TAS675X_AC_LDG_FREQ_CTRL_REG 0xB8
+#define TAS675X_TEMP_CH1_CH2_REG 0xBB
+#define TAS675X_TEMP_CH3_CH4_REG 0xBC
+#define TAS675X_WARN_OT_MAX_FLAG_REG 0xBD
+
+/* DC Load Diagnostic Reports */
+#define TAS675X_DC_LDG_REPORT_CH1_CH2_REG 0xC0
+#define TAS675X_DC_LDG_REPORT_CH3_CH4_REG 0xC1
+#define TAS675X_DC_LDG_RESULT_REG 0xC2
+#define TAS675X_AC_LDG_REPORT_CH1_R_REG 0xC3
+#define TAS675X_AC_LDG_REPORT_CH1_I_REG 0xC4
+#define TAS675X_AC_LDG_REPORT_CH2_R_REG 0xC5
+#define TAS675X_AC_LDG_REPORT_CH2_I_REG 0xC6
+#define TAS675X_AC_LDG_REPORT_CH3_R_REG 0xC7
+#define TAS675X_AC_LDG_REPORT_CH3_I_REG 0xC8
+#define TAS675X_AC_LDG_REPORT_CH4_R_REG 0xC9
+#define TAS675X_AC_LDG_REPORT_CH4_I_REG 0xCA
+#define TAS675X_TWEETER_REPORT_REG 0xCB
+
+/* RTLDG Impedance */
+#define TAS675X_CH1_RTLDG_IMP_MSB_REG 0xD1
+#define TAS675X_CH1_RTLDG_IMP_LSB_REG 0xD2
+#define TAS675X_CH2_RTLDG_IMP_MSB_REG 0xD3
+#define TAS675X_CH2_RTLDG_IMP_LSB_REG 0xD4
+#define TAS675X_CH3_RTLDG_IMP_MSB_REG 0xD5
+#define TAS675X_CH3_RTLDG_IMP_LSB_REG 0xD6
+#define TAS675X_CH4_RTLDG_IMP_MSB_REG 0xD7
+#define TAS675X_CH4_RTLDG_IMP_LSB_REG 0xD8
+
+/* DC Load Diagnostic Resistance */
+#define TAS675X_DC_LDG_DCR_MSB_REG 0xD9
+#define TAS675X_CH1_DC_LDG_DCR_LSB_REG 0xDA
+#define TAS675X_CH2_DC_LDG_DCR_LSB_REG 0xDB
+#define TAS675X_CH3_DC_LDG_DCR_LSB_REG 0xDC
+#define TAS675X_CH4_DC_LDG_DCR_LSB_REG 0xDD
+
+/* Over-Temperature Warning */
+#define TAS675X_OTW_CTRL_CH1_CH2_REG 0xE2
+#define TAS675X_OTW_CTRL_CH3_CH4_REG 0xE3
+
+/* RESET_REG (all bits auto-clear) */
+#define TAS675X_DEVICE_RESET BIT(4)
+#define TAS675X_FAULT_CLEAR BIT(3)
+#define TAS675X_REGISTER_RESET BIT(0)
+
+/* STATE_CTRL and STATE_REPORT — Channel state values */
+#define TAS675X_STATE_DEEPSLEEP 0x00
+#define TAS675X_STATE_LOAD_DIAG 0x01
+#define TAS675X_STATE_SLEEP 0x02
+#define TAS675X_STATE_HIZ 0x03
+#define TAS675X_STATE_PLAY 0x04
+
+/* Additional STATE_REPORT values */
+#define TAS675X_STATE_FAULT 0x05
+#define TAS675X_STATE_AUTOREC 0x06
+
+/* Combined values for both channel pairs in one register */
+#define TAS675X_STATE_DEEPSLEEP_BOTH \
+ (TAS675X_STATE_DEEPSLEEP | (TAS675X_STATE_DEEPSLEEP << 4))
+#define TAS675X_STATE_LOAD_DIAG_BOTH \
+ (TAS675X_STATE_LOAD_DIAG | (TAS675X_STATE_LOAD_DIAG << 4))
+#define TAS675X_STATE_SLEEP_BOTH \
+ (TAS675X_STATE_SLEEP | (TAS675X_STATE_SLEEP << 4))
+#define TAS675X_STATE_HIZ_BOTH \
+ (TAS675X_STATE_HIZ | (TAS675X_STATE_HIZ << 4))
+#define TAS675X_STATE_PLAY_BOTH \
+ (TAS675X_STATE_PLAY | (TAS675X_STATE_PLAY << 4))
+#define TAS675X_STATE_FAULT_BOTH \
+ (TAS675X_STATE_FAULT | (TAS675X_STATE_FAULT << 4))
+
+/* STATE_CTRL_CH1_CH2 / STATE_CTRL_CH3_CH4 — mute bits */
+#define TAS675X_CH1_MUTE_BIT BIT(7)
+#define TAS675X_CH2_MUTE_BIT BIT(3)
+#define TAS675X_CH_MUTE_BOTH (TAS675X_CH1_MUTE_BIT | TAS675X_CH2_MUTE_BIT)
+
+/* SCLK_INV_CTRL_REG */
+#define TAS675X_SCLK_INV_TX_BIT BIT(5)
+#define TAS675X_SCLK_INV_RX_BIT BIT(4)
+#define TAS675X_SCLK_INV_MASK (TAS675X_SCLK_INV_TX_BIT | TAS675X_SCLK_INV_RX_BIT)
+
+/* AUDIO_IF_CTRL_REG */
+#define TAS675X_TDM_EN_BIT BIT(4)
+#define TAS675X_SAP_FMT_MASK GENMASK(3, 2)
+#define TAS675X_SAP_FMT_I2S (0x00 << 2)
+#define TAS675X_SAP_FMT_TDM (0x01 << 2)
+#define TAS675X_SAP_FMT_RIGHT_J (0x02 << 2)
+#define TAS675X_SAP_FMT_LEFT_J (0x03 << 2)
+#define TAS675X_FS_PULSE_MASK GENMASK(1, 0)
+#define TAS675X_FS_PULSE_SHORT 0x01
+
+/* SDIN_CTRL_REG */
+#define TAS675X_SDIN_AUDIO_WL_MASK GENMASK(3, 2)
+#define TAS675X_SDIN_LL_WL_MASK GENMASK(1, 0)
+#define TAS675X_SDIN_WL_MASK (TAS675X_SDIN_AUDIO_WL_MASK | TAS675X_SDIN_LL_WL_MASK)
+
+/* SDOUT_CTRL_REG */
+#define TAS675X_SDOUT_SELECT_MASK GENMASK(7, 4)
+#define TAS675X_SDOUT_SELECT_TDM_SDOUT1 0x00
+#define TAS675X_SDOUT_SELECT_NON_TDM 0x10
+#define TAS675X_SDOUT_VP_WL_MASK GENMASK(3, 2)
+#define TAS675X_SDOUT_IS_WL_MASK GENMASK(1, 0)
+#define TAS675X_SDOUT_WL_MASK (TAS675X_SDOUT_VP_WL_MASK | TAS675X_SDOUT_IS_WL_MASK)
+
+/* SDOUT_EN_REG */
+#define TAS675X_SDOUT_NON_TDM_SEL_MASK GENMASK(5, 4)
+#define TAS675X_SDOUT_NON_TDM_SEL_VPREDICT (0x0 << 4)
+#define TAS675X_SDOUT_NON_TDM_SEL_ISENSE (0x1 << 4)
+#define TAS675X_SDOUT_EN_VPREDICT BIT(0)
+#define TAS675X_SDOUT_EN_ISENSE BIT(1)
+#define TAS675X_SDOUT_EN_NON_TDM_ALL GENMASK(1, 0)
+
+/* Word length values (shared by SDIN_CTRL and SDOUT_CTRL) */
+#define TAS675X_WL_16BIT 0x00
+#define TAS675X_WL_20BIT 0x01
+#define TAS675X_WL_24BIT 0x02
+#define TAS675X_WL_32BIT 0x03
+
+/* SDIN_OFFSET_MSB_REG */
+#define TAS675X_SDIN_AUDIO_OFF_MSB_MASK GENMASK(7, 6)
+#define TAS675X_SDIN_LL_OFF_MSB_MASK GENMASK(5, 4)
+
+/* SDOUT_OFFSET_MSB_REG */
+#define TAS675X_SDOUT_VP_OFF_MSB_MASK GENMASK(7, 6)
+#define TAS675X_SDOUT_IS_OFF_MSB_MASK GENMASK(5, 4)
+
+/* RTLDG_EN_REG */
+#define TAS675X_RTLDG_CLIP_MASK_BIT BIT(4)
+#define TAS675X_RTLDG_CH_EN_MASK GENMASK(3, 0)
+
+/* DC_LDG_CTRL_REG */
+#define TAS675X_LDG_ABORT_BIT BIT(7)
+#define TAS675X_LDG_BUFFER_WAIT_MASK GENMASK(6, 5)
+#define TAS675X_LDG_WAIT_BYPASS_BIT BIT(2)
+#define TAS675X_SLOL_DISABLE_BIT BIT(1)
+#define TAS675X_LDG_BYPASS_BIT BIT(0)
+
+/* DC_LDG_TIME_CTRL_REG */
+#define TAS675X_LDG_RAMP_SLOL_MASK GENMASK(7, 6)
+#define TAS675X_LDG_SETTLING_SLOL_MASK GENMASK(5, 4)
+#define TAS675X_LDG_RAMP_S2PG_MASK GENMASK(3, 2)
+#define TAS675X_LDG_SETTLING_S2PG_MASK GENMASK(1, 0)
+
+/* AC_LDG_CTRL_REG */
+#define TAS675X_AC_DIAG_GAIN_BIT BIT(4)
+#define TAS675X_AC_DIAG_START_MASK GENMASK(3, 0)
+
+/* DC_LDG_RESULT_REG */
+#define TAS675X_DC_LDG_LO_RESULT_MASK GENMASK(7, 4)
+#define TAS675X_DC_LDG_PASS_MASK GENMASK(3, 0)
+
+/* Load Diagnostics Timing Constants */
+#define TAS675X_POLL_INTERVAL_US 10000
+#define TAS675X_STATE_TRANSITION_TIMEOUT_US 50000
+#define TAS675X_DC_LDG_TIMEOUT_US 300000
+#define TAS675X_AC_LDG_TIMEOUT_US 400000
+
+/* GPIO_CTRL_REG */
+#define TAS675X_GPIO1_OUTPUT_EN BIT(7)
+#define TAS675X_GPIO2_OUTPUT_EN BIT(6)
+#define TAS675X_GPIO_CTRL_RSTVAL 0x22
+
+/* GPIO output select values */
+#define TAS675X_GPIO_SEL_LOW 0x00
+#define TAS675X_GPIO_SEL_AUTO_MUTE_ALL 0x02
+#define TAS675X_GPIO_SEL_AUTO_MUTE_CH4 0x03
+#define TAS675X_GPIO_SEL_AUTO_MUTE_CH3 0x04
+#define TAS675X_GPIO_SEL_AUTO_MUTE_CH2 0x05
+#define TAS675X_GPIO_SEL_AUTO_MUTE_CH1 0x06
+#define TAS675X_GPIO_SEL_SDOUT2 0x08
+#define TAS675X_GPIO_SEL_SDOUT1 0x09
+#define TAS675X_GPIO_SEL_WARN 0x0A
+#define TAS675X_GPIO_SEL_FAULT 0x0B
+#define TAS675X_GPIO_SEL_CLOCK_SYNC 0x0E
+#define TAS675X_GPIO_SEL_INVALID_CLK 0x0F
+#define TAS675X_GPIO_SEL_HIGH 0x13
+
+/* GPIO input function encoding (flag bit | function ID) */
+#define TAS675X_GPIO_FUNC_INPUT 0x100
+
+/* Input Function IDs */
+#define TAS675X_GPIO_IN_ID_MUTE 0
+#define TAS675X_GPIO_IN_ID_PHASE_SYNC 1
+#define TAS675X_GPIO_IN_ID_SDIN2 2
+#define TAS675X_GPIO_IN_ID_DEEP_SLEEP 3
+#define TAS675X_GPIO_IN_ID_HIZ 4
+#define TAS675X_GPIO_IN_ID_PLAY 5
+#define TAS675X_GPIO_IN_ID_SLEEP 6
+#define TAS675X_GPIO_IN_NUM 7
+
+#define TAS675X_GPIO_IN_MUTE (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_MUTE)
+#define TAS675X_GPIO_IN_PHASE_SYNC \
+ (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_PHASE_SYNC)
+#define TAS675X_GPIO_IN_SDIN2 (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_SDIN2)
+#define TAS675X_GPIO_IN_DEEP_SLEEP \
+ (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_DEEP_SLEEP)
+#define TAS675X_GPIO_IN_HIZ (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_HIZ)
+#define TAS675X_GPIO_IN_PLAY (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_PLAY)
+#define TAS675X_GPIO_IN_SLEEP (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_SLEEP)
+
+/* GPIO input 3-bit mux field masks */
+#define TAS675X_GPIO_IN_MUTE_MASK GENMASK(2, 0)
+#define TAS675X_GPIO_IN_SYNC_MASK GENMASK(2, 0)
+#define TAS675X_GPIO_IN_SDIN2_MASK GENMASK(6, 4)
+#define TAS675X_GPIO_IN_DEEP_SLEEP_MASK GENMASK(6, 4)
+#define TAS675X_GPIO_IN_HIZ_MASK GENMASK(2, 0)
+#define TAS675X_GPIO_IN_PLAY_MASK GENMASK(6, 4)
+#define TAS675X_GPIO_IN_SLEEP_MASK GENMASK(2, 0)
+
+/* Book addresses for tas675x_select_book() */
+#define TAS675X_BOOK_DEFAULT 0x00
+#define TAS675X_BOOK_DSP 0x8C
+
+/* DSP memory addresses (DSP Book) */
+#define TAS675X_DSP_PAGE_RTLDG 0x22
+#define TAS675X_DSP_RTLDG_OL_THRESH_REG 0x98
+#define TAS675X_DSP_RTLDG_SL_THRESH_REG 0x9C
+
+#define TAS675X_DSP_PARAM_ID_OL_THRESH 0
+#define TAS675X_DSP_PARAM_ID_SL_THRESH 1
+
+/* Setup Mode Entry/Exit*/
+#define TAS675X_SETUP_ENTER_VAL1 0x11
+#define TAS675X_SETUP_ENTER_VAL2 0xFF
+#define TAS675X_SETUP_EXIT_VAL 0x00
+
+#endif /* __TAS675X_H__ */
--
2.43.0
^ permalink raw reply related
* [PATCH 4/4] MAINTAINERS: add entry for TAS675x audio amplifier
From: Sen Wang @ 2026-04-01 2:42 UTC (permalink / raw)
To: linux-sound
Cc: broonie, lgirdwood, robh, krzk+dt, conor+dt, devicetree, perex,
tiwai, shenghao-ding, kevin-lu, baojun.xu, niranjan.hy,
l-badrinarayanan, devarsht, v-singh1, linux-kernel, Sen Wang
In-Reply-To: <20260401024210.28542-1-sen@ti.com>
Add Sen Wang as maintainer and register file patterns for the newly
added TAS675x quad-channel amplifier driver.
Signed-off-by: Sen Wang <sen@ti.com>
---
MAINTAINERS | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index a626dee5c106..ea61bb439652 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26191,17 +26191,20 @@ TEXAS INSTRUMENTS AUDIO (ASoC/HDA) DRIVERS
M: Shenghao Ding <shenghao-ding@ti.com>
M: Kevin Lu <kevin-lu@ti.com>
M: Baojun Xu <baojun.xu@ti.com>
+M: Sen Wang <sen@ti.com>
L: linux-sound@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/sound/ti,tas2552.yaml
F: Documentation/devicetree/bindings/sound/ti,tas2562.yaml
F: Documentation/devicetree/bindings/sound/ti,tas2770.yaml
F: Documentation/devicetree/bindings/sound/ti,tas27xx.yaml
+F: Documentation/devicetree/bindings/sound/ti,tas675x.yaml
F: Documentation/devicetree/bindings/sound/ti,tpa6130a2.yaml
F: Documentation/devicetree/bindings/sound/ti,pcm1681.yaml
F: Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml
F: Documentation/devicetree/bindings/sound/ti,tlv320*.yaml
F: Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml
+F: Documentation/sound/codecs/tas675*
F: include/sound/tas2*.h
F: include/sound/tlv320*.h
F: sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
@@ -26215,6 +26218,7 @@ F: sound/soc/codecs/pcm3168a*.*
F: sound/soc/codecs/pcm5102a.c
F: sound/soc/codecs/pcm512x*.*
F: sound/soc/codecs/tas2*.*
+F: sound/soc/codecs/tas675*.*
F: sound/soc/codecs/tlv320*.*
F: sound/soc/codecs/tpa6130a2.*
--
2.43.0
^ permalink raw reply related
* [PATCH 1/4] dt-bindings: sound: Add ti,tas675x
From: Sen Wang @ 2026-04-01 2:42 UTC (permalink / raw)
To: linux-sound
Cc: broonie, lgirdwood, robh, krzk+dt, conor+dt, devicetree, perex,
tiwai, shenghao-ding, kevin-lu, baojun.xu, niranjan.hy,
l-badrinarayanan, devarsht, v-singh1, linux-kernel, Sen Wang
In-Reply-To: <20260401024210.28542-1-sen@ti.com>
Add device tree binding for the Texas Instruments TAS675x family
of four-channel Class-D audio amplifiers with integrated DSP.
Signed-off-by: Sen Wang <sen@ti.com>
---
.../devicetree/bindings/sound/ti,tas675x.yaml | 278 ++++++++++++++++++
1 file changed, 278 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/ti,tas675x.yaml
diff --git a/Documentation/devicetree/bindings/sound/ti,tas675x.yaml b/Documentation/devicetree/bindings/sound/ti,tas675x.yaml
new file mode 100644
index 000000000000..23e4cc77b4ae
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,tas675x.yaml
@@ -0,0 +1,278 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,tas675x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TAS675x Audio Amplifier
+
+maintainers:
+ - Sen Wang <sen@ti.com>
+
+description: |
+ The TAS675x family (TAS6754, TAS67524) are four-channel, digital-input,
+ automotive Class-D audio amplifiers with load diagnostics and an integrated
+ DSP for audio processing.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - ti,tas6754
+ - ti,tas67524
+
+ reg:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 1
+ description: |
+ The device exposes three DAIs, selected by index.
+ 0 - Standard Audio Path (Playback)
+ 1 - Low-Latency Playback Path (Playback)
+ 2 - Sensory Feedback (Capture - Vpredict and Isense)
+ By default, all four channels of each DAI are active. Runtime
+ reconfiguration is available through DAPM widgets.
+
+ interrupts:
+ maxItems: 1
+ description:
+ Active-low falling-edge interrupt from the FAULT pin. When provided,
+ the driver uses IRQ-driven fault reporting instead of polling.
+
+ pd-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the power-down (PD#) pin, active low. Controls the
+ internal digital circuitry power state. When asserted the device enters
+ full power-down mode and all register state is lost. Can be omitted if
+ PD pin is hardwired or externally controlled.
+
+ stby-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the standby (STBY#) pin, active low. Controls the
+ analog power stage. When asserted the device enters Deep Sleep mode
+ but remains I2C-accessible with registers retained. Can be omitted if
+ STBY pin is tied to PD or hardwired.
+
+ dvdd-supply:
+ description:
+ Digital logic supply (1.62 V to 3.6 V). All three supply rails must
+ be within their recommended operating ranges before the PD pin is
+ released.
+
+ pvdd-supply:
+ description:
+ Output FET power supply (4.5 V to 19 V). All three supply rails must
+ be within their recommended operating ranges before the PD pin is
+ released.
+
+ vbat-supply:
+ description:
+ Battery supply for the Class-D output stage (4.5 V to 19 V). Optional
+ when PVDD and VBAT are connected to the same supply rail. When absent,
+ VBAT is assumed hardwired to PVDD.
+
+ ti,fast-boot:
+ type: boolean
+ description:
+ Skip DC load diagnostic sweep at power-on to reduce boot latency.
+ Automatic diagnostics after fault conditions remain enabled. Hardware
+ overcurrent protection is always active.
+
+ ti,audio-slot-no:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ TDM slot offset for the standard audio playback path via SDIN1. A value
+ of 4 maps to slot 4. If omitted, slot assignment is derived from the
+ tx_mask provided via set_tdm_slot(). Without either property, no slot
+ mapping is configured.
+
+ ti,llp-slot-no:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ TDM slot offset for the low-latency playback path via SDIN1. If omitted,
+ slot assignment is derived from the tx_mask provided via set_tdm_slot().
+ Without either property, no slot mapping is configured. Disabled outside
+ of LLP mode, and only relevant for TDM formats.
+
+ ti,vpredict-slot-no:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ In TDM mode, enables Vpredict output and assigns its starting slot;
+ four consecutive slots carry Vpredict Ch1-4 on SDOUT1. May coexist
+ with ti,isense-slot-no using separate non-overlapping slots.
+
+ In I2S mode, enables Vpredict output on SDOUT1 (Ch1/Ch2) and SDOUT2
+ (Ch3/Ch4). The slot value is unused. Requires a GPIO configured as
+ sdout2 for Ch3/Ch4; without it only Ch1/Ch2 are output. Mutually
+ exclusive with ti,isense-slot-no; if both are set, Vpredict takes
+ priority.
+
+ Irrelevant in Left-J and Right-J modes.
+
+ ti,isense-slot-no:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ In TDM mode, enables Isense output and assigns its starting slot;
+ four consecutive slots carry Isense Ch1-4 on SDOUT1. May coexist
+ with ti,vpredict-slot-no using separate non-overlapping slots.
+
+ In I2S mode, enables Isense output on SDOUT1 (Ch1/Ch2) and SDOUT2
+ (Ch3/Ch4). The slot value is unused. Requires a GPIO configured as
+ sdout2 for Ch3/Ch4; without it only Ch1/Ch2 are output. Mutually
+ exclusive with ti,vpredict-slot-no; Vpredict takes priority if both
+ are set.
+
+ Irrelevant in Left-J and Right-J modes.
+
+ ti,gpio1-function:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: |
+ Function for the GPIO_1 pin. When omitted, GPIO_1 remains in its
+ power-on default state.
+ enum:
+ - low # Output: driven low
+ - auto-mute # Output: high when all channels are auto-muted
+ - auto-mute-ch4 # Output: high when channel 4 is auto-muted
+ - auto-mute-ch3 # Output: high when channel 3 is auto-muted
+ - auto-mute-ch2 # Output: high when channel 2 is auto-muted
+ - auto-mute-ch1 # Output: high when channel 1 is auto-muted
+ - sdout2 # Output: Routes secondary serial data output 2
+ - sdout1 # Output: Re-routes secondary serial data output 1
+ - warn # Output: warning signal (OTW, CBC)
+ - fault # Output: fault signal (OTSD, OC, DC)
+ - clock-sync # Output: clock synchronisation
+ - invalid-clock # Output: high when clock is invalid
+ - high # Output: driven high
+ - mute # Input: external mute control
+ - phase-sync # Input: phase synchronisation
+ - sdin2 # Input: secondary SDIN2 for I2S/LJ/RJ ch3/ch4
+ - deep-sleep # Input: asserted transitions device to Deep Sleep
+ - hiz # Input: asserted transitions device to Hi-Z
+ - play # Input: asserted transitions device to Play
+ - sleep # Input: asserted transitions device to Sleep
+
+ ti,gpio2-function:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: |
+ Function for the GPIO_2 pin. When omitted, GPIO_2 remains in its
+ power-on default state.
+ enum:
+ - low # Output: driven low
+ - auto-mute # Output: high when all channels are auto-muted
+ - auto-mute-ch4 # Output: high when channel 4 is auto-muted
+ - auto-mute-ch3 # Output: high when channel 3 is auto-muted
+ - auto-mute-ch2 # Output: high when channel 2 is auto-muted
+ - auto-mute-ch1 # Output: high when channel 1 is auto-muted
+ - sdout2 # Output: Routes secondary serial data output 2
+ - sdout1 # Output: Re-routes secondary serial data output 1
+ - warn # Output: warning signal (OTW, CBC)
+ - fault # Output: fault signal (OTSD, OC, DC)
+ - clock-sync # Output: clock synchronisation
+ - invalid-clock # Output: high when clock is invalid
+ - high # Output: driven high
+ - mute # Input: external mute control
+ - phase-sync # Input: phase synchronisation
+ - sdin2 # Input: secondary SDIN2 for I2S/LJ/RJ ch3/ch4
+ - deep-sleep # Input: asserted transitions device to Deep Sleep
+ - hiz # Input: asserted transitions device to Hi-Z
+ - play # Input: asserted transitions device to Play
+ - sleep # Input: asserted transitions device to Sleep
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+ properties:
+ port@0:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+ description: Standard audio playback port (DAI 0).
+
+ port@1:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+ description: Low-latency playback port (LLP) (DAI 1).
+
+ port@2:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+ description: Sensory feedback capture port (DAI 2).
+
+ port:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - '#sound-dai-cells'
+ - dvdd-supply
+ - pvdd-supply
+
+anyOf:
+ - required: [pd-gpios]
+ - required: [stby-gpios]
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ amplifier@70 {
+ compatible = "ti,tas67524";
+ reg = <0x70>;
+ #sound-dai-cells = <1>;
+ sound-name-prefix = "TAS0";
+
+ stby-gpios = <&main_gpio0 33 GPIO_ACTIVE_LOW>;
+
+ dvdd-supply = <&dvdd_1v8>;
+ pvdd-supply = <&pvdd_12v>;
+ vbat-supply = <&vbat_12v>;
+
+ ti,audio-slot-no = <0>;
+ ti,llp-slot-no = <4>;
+ ti,vpredict-slot-no = <0>;
+ ti,isense-slot-no = <4>;
+
+ ti,gpio2-function = "warn";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ tas0_audio_ep: endpoint {
+ dai-format = "dsp_b";
+ remote-endpoint = <&be_tas0_audio_ep>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ tas0_anc_ep: endpoint {
+ remote-endpoint = <&be_tas0_anc_ep>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+
+ tas0_fb_ep: endpoint {
+ remote-endpoint = <&be_tas0_fb_ep>;
+ };
+ };
+ };
+ };
+ };
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 0/4] ASoC: Add TAS675x quad-channel Class-D amplifier driver
From: Sen Wang @ 2026-04-01 2:42 UTC (permalink / raw)
To: linux-sound
Cc: broonie, lgirdwood, robh, krzk+dt, conor+dt, devicetree, perex,
tiwai, shenghao-ding, kevin-lu, baojun.xu, niranjan.hy,
l-badrinarayanan, devarsht, v-singh1, linux-kernel, Sen Wang
This series adds support for the TI TAS675x (TAS6754, TAS67524)
quad-channel automotive Class-D amplifiers. The devices have an
integrated DSP and load diagnostics, and are controlled over I2C.
Patch 1 adds the dt-binding, patch 2 the codec driver, patch 3 the
ALSA mixer controls documentation, and patch 4 adds the MAINTAINERS
entry.
Tested on AM62D-EVM with a TAS67CD-AEC daughter card, on setups &
test procedures, refer to the Github repository.
GitHub: https://github.com/SenWang125/tas67-linux
Sen Wang (4):
dt-bindings: sound: Add ti,tas675x
ASoC: codecs: Add TAS675x quad-channel audio amplifier driver
Documentation: sound: Add TAS675x codec mixer controls documentation
MAINTAINERS: add entry for TAS675x audio amplifier
.../devicetree/bindings/sound/ti,tas675x.yaml | 278 +++
Documentation/sound/codecs/index.rst | 1 +
Documentation/sound/codecs/tas675x.rst | 618 +++++
MAINTAINERS | 4 +
sound/soc/codecs/Kconfig | 12 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tas675x.c | 2172 +++++++++++++++++
sound/soc/codecs/tas675x.h | 367 +++
8 files changed, 3454 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/ti,tas675x.yaml
create mode 100644 Documentation/sound/codecs/tas675x.rst
create mode 100644 sound/soc/codecs/tas675x.c
create mode 100644 sound/soc/codecs/tas675x.h
--
2.43.0
^ permalink raw reply
* Re: [PATCH 1/7] cache: ax45mp_cache: refactor cache driver for generic Andes platform support
From: Mina Chou @ 2026-04-01 2:30 UTC (permalink / raw)
To: Conor Dooley
Cc: pjw, palmer, aou, alex, geert+renesas, prabhakar.mahadev-lad.rj,
magnus.damm, ben717, robh, krzk+dt, conor+dt, jonathan.cameron,
devicetree, linux-riscv, linux-kernel, linux-renesas-soc, tim609,
alex749, az70021
In-Reply-To: <20260330-oxford-chevy-5f5a6f0a5be4@spud>
Thank you both, Krzysztof and Conor, for the detailed review.
I appreciate the feedback and admit this series needed more work
before sending. I will address all the issues in the next version.
A bit of background on the motivation: the main goal of this series
was to prepare the Andes cache driver for a SoC Allwinner Avaotaf1 V821,
which uses the Andes A27L2 CPU. We wanted to share a single cache driver
across different Andes CPU variants, which is why we tried to move toward
more generic naming in both the driver and the compatible strings.
We have two questions we'd appreciate guidance on:
a) On compatible string naming: We'll drop patch [5/7][6/7] and won't
rename any existing compatible strings. But we'd like to confirm
the preferred approach for A27L2: would it be acceptable to add
a generic compatible (andestech,andes-llcache) as an addition?
If so, would a CPU-specific compatible (andestech,a27l2-cache)
still be required alongside it?
b) On Avaotaf1 V821 support: We are not in a position to submit the
DTS on behalf of Allwinner. However, we would like to add the
corresponding compatible strings to the existing binding
documents (andestech,andes-llcache.yaml, sifive,plic-1.0.0.yaml,
and riscv/cpus.yaml) in advance, so that the bindings are ready
when Allwinner eventually submits their DTS.
Would it be acceptable to upstream binding-only changes without
an accompanying DTS at this stage?
For the next version, we're thinking of keeping only the changes
needed to generalize the cache driver, and dropping the improvements
for now to keep things focused. If you have any suggestion on how
to approach this, we'd love to hear it.
Thanks again for your patience.
Best regards,
Mina
^ permalink raw reply
* [PATCH 2/2] arm64: dts: qcom: milos: Add qfprom efuse node
From: Alexander Koskovich @ 2026-04-01 2:25 UTC (permalink / raw)
To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: Luca Weiss, linux-arm-msm, devicetree, linux-kernel,
Alexander Koskovich
In-Reply-To: <20260331-milos-qfprom-v1-0-36017cc642db@pm.me>
Add the qfprom efuse node and describe where the GPU speedbin fuse is
located on Milos.
Note that for SM7635-AB at least, the value is "221", the max frequency
for this is 1050MHz. There's another speedbin out there for 1150MHz but
we do not know the value for it so just document in this commit.
Once the value is discovered we should add the speedbins to the A810
Adreno entry and update devicetree.
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
arch/arm64/boot/dts/qcom/milos.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/milos.dtsi b/arch/arm64/boot/dts/qcom/milos.dtsi
index e1a51d43943f..d1d23dfe7806 100644
--- a/arch/arm64/boot/dts/qcom/milos.dtsi
+++ b/arch/arm64/boot/dts/qcom/milos.dtsi
@@ -1168,6 +1168,18 @@ tcsr: clock-controller@1fc0000 {
#reset-cells = <1>;
};
+ qfprom: efuse@221c8000 {
+ compatible = "qcom,milos-qfprom", "qcom,qfprom";
+ reg = <0x0 0x221c8000 0x0 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ gpu_speed_bin: gpu-speed-bin@138 {
+ reg = <0x138 0x2>;
+ bits = <0 9>;
+ };
+ };
+
remoteproc_adsp: remoteproc@3000000 {
compatible = "qcom,milos-adsp-pas";
reg = <0x0 0x03000000 0x0 0x10000>;
--
2.53.0
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: nvmem: qfprom: Add Milos compatible
From: Alexander Koskovich @ 2026-04-01 2:24 UTC (permalink / raw)
To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: Luca Weiss, linux-arm-msm, devicetree, linux-kernel,
Alexander Koskovich
In-Reply-To: <20260331-milos-qfprom-v1-0-36017cc642db@pm.me>
Document compatible string for the QFPROM on Milos platform.
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
index 839513d4b499..936878e74895 100644
--- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
+++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
@@ -26,6 +26,7 @@ properties:
- qcom,ipq8064-qfprom
- qcom,ipq8074-qfprom
- qcom,ipq9574-qfprom
+ - qcom,milos-qfprom
- qcom,msm8226-qfprom
- qcom,msm8916-qfprom
- qcom,msm8917-qfprom
--
2.53.0
^ permalink raw reply related
* [PATCH 0/2] arm64: dts: qcom: milos: Add QFPROM efuse support
From: Alexander Koskovich @ 2026-04-01 2:24 UTC (permalink / raw)
To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: Luca Weiss, linux-arm-msm, devicetree, linux-kernel,
Alexander Koskovich
Add dt-bindings and dt-node for Milos QFPROM efuse. The GPU speed bin child
node nvmem cell contains details of clk frequencies supported by the GPU,
which can then read by the GPU driver to select the correct set of operating
performance points (OPPs) for the device in the future once all the possible
speedbins for Milos are known.
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
Alexander Koskovich (2):
dt-bindings: nvmem: qfprom: Add Milos compatible
arm64: dts: qcom: milos: Add qfprom efuse node
Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 1 +
arch/arm64/boot/dts/qcom/milos.dtsi | 12 ++++++++++++
2 files changed, 13 insertions(+)
---
base-commit: 7aaa8047eafd0bd628065b15757d9b48c5f9c07d
change-id: 20260331-milos-qfprom-1b00c2892171
Best regards,
--
Alexander Koskovich <akoskovich@pm.me>
^ permalink raw reply
* [PATCH 6/6] arm64: dts: qcom: milos: Add Adreno 810 GPU and GMU nodes
From: Alexander Koskovich @ 2026-04-01 2:17 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio, Akhil P Oommen,
Bjorn Andersson
Cc: Luca Weiss, linux-arm-msm, dri-devel, freedreno, devicetree,
linux-kernel, Alexander Koskovich
In-Reply-To: <20260331-adreno-810-v1-0-725801dbb12b@pm.me>
Add GPU and GMU devicetree nodes for the Adreno 810 GPU found on
Qualcomm SM7635 (Milos) based devices.
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
arch/arm64/boot/dts/qcom/milos.dtsi | 148 ++++++++++++++++++++++++++++++++++++
1 file changed, 148 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/milos.dtsi b/arch/arm64/boot/dts/qcom/milos.dtsi
index 621f05820826..a8feb0339804 100644
--- a/arch/arm64/boot/dts/qcom/milos.dtsi
+++ b/arch/arm64/boot/dts/qcom/milos.dtsi
@@ -7,6 +7,7 @@
#include <dt-bindings/clock/qcom,milos-dispcc.h>
#include <dt-bindings/clock/qcom,milos-gcc.h>
#include <dt-bindings/clock/qcom,milos-gpucc.h>
+#include <dt-bindings/clock/qcom,kaanapali-gxclkctl.h>
#include <dt-bindings/clock/qcom,rpmh.h>
#include <dt-bindings/clock/qcom,sm8650-tcsr.h>
#include <dt-bindings/dma/qcom-gpi.h>
@@ -1224,6 +1225,153 @@ lpass_ag_noc: interconnect@3c40000 {
qcom,bcm-voters = <&apps_bcm_voter>;
};
+ gpu: gpu@3d00000 {
+ compatible = "qcom,adreno-44010000", "qcom,adreno";
+ reg = <0x0 0x03d00000 0x0 0x40000>,
+ <0x0 0x03d9e000 0x0 0x2000>,
+ <0x0 0x03d61000 0x0 0x800>;
+ reg-names = "kgsl_3d0_reg_memory",
+ "cx_mem",
+ "cx_dbgc";
+
+ interrupts = <GIC_SPI 300 IRQ_TYPE_LEVEL_HIGH 0>;
+
+ iommus = <&adreno_smmu 0 0x0>;
+
+ operating-points-v2 = <&gpu_opp_table>;
+
+ qcom,gmu = <&gmu>;
+ #cooling-cells = <2>;
+
+ interconnects = <&gem_noc MASTER_GFX3D QCOM_ICC_TAG_ALWAYS
+ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
+ interconnect-names = "gfx-mem";
+
+ status = "disabled";
+
+ gpu_zap_shader: zap-shader {
+ memory-region = <&gpu_microcode_mem>;
+ };
+
+ gpu_opp_table: opp-table {
+ compatible = "operating-points-v2-adreno",
+ "operating-points-v2";
+
+ opp-264000000 {
+ opp-hz = /bits/ 64 <264000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS_D1>;
+ opp-peak-kBps = <2136718>;
+ qcom,opp-acd-level = <0xc8295ffd>;
+ };
+
+ opp-362000000 {
+ opp-hz = /bits/ 64 <362000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS>;
+ opp-peak-kBps = <2136718>;
+ qcom,opp-acd-level = <0xc02c5ffd>;
+ };
+
+ opp-510000000 {
+ opp-hz = /bits/ 64 <510000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_SVS>;
+ opp-peak-kBps = <3972656>;
+ qcom,opp-acd-level = <0x882b5ffd>;
+ };
+
+ opp-644000000 {
+ opp-hz = /bits/ 64 <644000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_SVS_L1>;
+ opp-peak-kBps = <5285156>;
+ qcom,opp-acd-level = <0x882a5ffd>;
+ };
+
+ opp-688000000 {
+ opp-hz = /bits/ 64 <688000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_SVS_L2>;
+ opp-peak-kBps = <6074218>;
+ qcom,opp-acd-level = <0x882a5ffd>;
+ };
+
+ opp-763000000 {
+ opp-hz = /bits/ 64 <763000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_NOM>;
+ opp-peak-kBps = <6671875>;
+ qcom,opp-acd-level = <0xa8295ffd>;
+ };
+
+ opp-895000000 {
+ opp-hz = /bits/ 64 <895000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_NOM_L1>;
+ opp-peak-kBps = <8171875>;
+ qcom,opp-acd-level = <0x88295ffd>;
+ };
+
+ opp-960000000 {
+ opp-hz = /bits/ 64 <960000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_TURBO>;
+ opp-peak-kBps = <8171875>;
+ qcom,opp-acd-level = <0xa8285ffd>;
+ };
+
+ opp-1050000000 {
+ opp-hz = /bits/ 64 <1050000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_TURBO_L1>;
+ opp-peak-kBps = <18597656>;
+ qcom,opp-acd-level = <0x88285ffd>;
+ };
+ };
+ };
+
+ gmu: gmu@3d37000 {
+ compatible = "qcom,adreno-gmu-810.0", "qcom,adreno-gmu";
+ reg = <0x0 0x03d37000 0x0 0x68000>;
+ reg-names = "gmu";
+
+ interrupts = <GIC_SPI 304 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 305 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "hfi", "gmu";
+
+ clocks = <&gpucc GPU_CC_AHB_CLK>,
+ <&gpucc GPU_CC_CX_GMU_CLK>,
+ <&gpucc GPU_CC_CXO_CLK>,
+ <&gcc GCC_DDRSS_GPU_AXI_CLK>,
+ <&gcc GCC_GPU_MEMNOC_GFX_CLK>,
+ <&gpucc GPU_CC_HUB_CX_INT_CLK>,
+ <&gpucc GPU_CC_DEMET_CLK>;
+ clock-names = "ahb",
+ "gmu",
+ "cxo",
+ "axi",
+ "memnoc",
+ "hub",
+ "demet";
+
+ power-domains = <&gpucc GPU_CC_CX_GDSC>,
+ <&gxclkctl GX_CLKCTL_GX_GDSC>;
+ power-domain-names = "cx",
+ "gx";
+
+ iommus = <&adreno_smmu 5 0x0>;
+
+ qcom,qmp = <&aoss_qmp>;
+
+ operating-points-v2 = <&gmu_opp_table>;
+
+ gmu_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-350000000 {
+ opp-hz = /bits/ 64 <350000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS>;
+ };
+
+ opp-650000000 {
+ opp-hz = /bits/ 64 <650000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_SVS>;
+ };
+ };
+ };
+
gxclkctl: clock-controller@3d64000 {
compatible = "qcom,milos-gxclkctl";
reg = <0x0 0x03d64000 0x0 0x6000>;
--
2.53.0
^ permalink raw reply related
* [PATCH 5/6] drm/msm/adreno: add Adreno 810 GPU support
From: Alexander Koskovich @ 2026-04-01 2:17 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio, Akhil P Oommen,
Bjorn Andersson
Cc: Luca Weiss, linux-arm-msm, dri-devel, freedreno, devicetree,
linux-kernel, Alexander Koskovich
In-Reply-To: <20260331-adreno-810-v1-0-725801dbb12b@pm.me>
Add catalog entry and register configuration for the Adreno 810
found in Qualcomm SM7635 (Milos) based devices.
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 271 ++++++++++++++++++++++++++++++
drivers/gpu/drm/msm/adreno/adreno_gpu.h | 5 +
2 files changed, 276 insertions(+)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
index 550ff3a9b82e..0d7d9c86205e 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
@@ -1799,6 +1799,240 @@ static const struct adreno_reglist_pipe x285_dyn_pwrup_reglist_regs[] = {
};
DECLARE_ADRENO_REGLIST_PIPE_LIST(x285_dyn_pwrup_reglist);
+static const struct adreno_reglist_pipe a810_nonctxt_regs[] = {
+ { REG_A8XX_CP_SMMU_STREAM_ID_LPAC, 0x00000101, BIT(PIPE_NONE) },
+ { REG_A8XX_GRAS_DBG_ECO_CNTL, 0x00f80800, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A6XX_PC_AUTO_VERTEX_STRIDE, 0x00000001, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_VIS_STREAM_CNTL, 0x10010000, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CONTEXT_SWITCH_STABILIZE_CNTL_1, 0x00000002, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_1, 0x00000003, BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_1, 0x00000023, BIT(PIPE_BV) }, /* Avoid partial waves at VFD */
+ { REG_A8XX_PC_CHICKEN_BITS_2, 0x00000200, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_3, 0x00500000, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_4, 0x00500050, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A7XX_RB_CCU_CNTL, 0x00000068, BIT(PIPE_BR) },
+ { REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0x00000007, BIT(PIPE_BR) },
+ { REG_A8XX_RB_CMP_DBG_ECO_CNTL, 0x00004000, BIT(PIPE_BR) },
+ { REG_A8XX_RBBM_NC_MODE_CNTL, 0x00000001, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_SLICE_NC_MODE_CNTL, 0x00000001, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_WAIT_IDLE_CLOCKS_CNTL, 0x00000030, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_WAIT_IDLE_CLOCKS_CNTL2, 0x00000030, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_GBIF_GX_CONFIG, 0x010240e0, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_GBIF_CLIENT_QOS_CNTL, 0x22122212, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_CGC_P2S_CNTL, 0x00000040, BIT(PIPE_NONE) },
+ /*
+ * BIT(22): Disable PS out of order retire
+ * BIT(23): Enable half wave mode and MM instruction src&dst is half precision
+ */
+ { REG_A7XX_SP_CHICKEN_BITS_2, BIT(22) | BIT(23), BIT(PIPE_NONE) },
+ { REG_A7XX_SP_CHICKEN_BITS_3, 0x00300000, BIT(PIPE_NONE) },
+ { REG_A6XX_SP_PERFCTR_SHADER_MASK, 0x0000003f, BIT(PIPE_NONE) },
+ { REG_A7XX_SP_HLSQ_TIMEOUT_THRESHOLD_DP, 0x00000080, BIT(PIPE_NONE) },
+ { REG_A7XX_SP_READ_SEL, 0x0001ff00, BIT(PIPE_NONE) },
+ { REG_A6XX_TPL1_DBG_ECO_CNTL, 0x10000000, BIT(PIPE_NONE) },
+ /* BIT(26): Disable final clamp for bicubic filtering */
+ { REG_A6XX_TPL1_DBG_ECO_CNTL1, 0x04000720, BIT(PIPE_NONE) },
+ { REG_A6XX_UCHE_MODE_CNTL, 0x80080000, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_CCHE_MODE_CNTL, 0x00001000, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_CCHE_CACHE_WAYS, 0x00000800, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_CACHE_WAYS, 0x00080000, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_VARB_IDLE_TIMEOUT, 0x00000020, BIT(PIPE_NONE) },
+ { REG_A7XX_VFD_DBG_ECO_CNTL, 0x00008000, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_BV_THRESHOLD, 0x00500050, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_BR_THRESHOLD, 0x00600060, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_BUSY_REQ_CNT, 0x00200020, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_LP_REQ_CNT, 0x00100020, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VPC_FLATSHADE_MODE_CNTL, 0x00000001, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_RB_GC_GMEM_PROTECT, 0x00900000, BIT(PIPE_BR) },
+ { },
+};
+
+static const u32 a810_protect_regs[] = {
+ A6XX_PROTECT_RDONLY(0x00000, 0x03a3),
+ A6XX_PROTECT_RDONLY(0x003b4, 0x008b),
+ A6XX_PROTECT_NORDWR(0x00440, 0x001f),
+ A6XX_PROTECT_RDONLY(0x00580, 0x005f),
+ A6XX_PROTECT_NORDWR(0x005e0, 0x011f),
+ A6XX_PROTECT_RDONLY(0x0074a, 0x0005),
+ A6XX_PROTECT_RDONLY(0x00759, 0x0026),
+ A6XX_PROTECT_RDONLY(0x00789, 0x0000),
+ A6XX_PROTECT_RDONLY(0x0078c, 0x0013),
+ A6XX_PROTECT_NORDWR(0x00800, 0x0029),
+ A6XX_PROTECT_NORDWR(0x00837, 0x00af),
+ A6XX_PROTECT_RDONLY(0x008e7, 0x00c9),
+ A6XX_PROTECT_NORDWR(0x008ec, 0x00c3),
+ A6XX_PROTECT_NORDWR(0x009b1, 0x0250),
+ A6XX_PROTECT_RDONLY(0x00ce0, 0x0001),
+ A6XX_PROTECT_RDONLY(0x00df0, 0x0000),
+ A6XX_PROTECT_NORDWR(0x00df1, 0x0000),
+ A6XX_PROTECT_NORDWR(0x00e01, 0x0000),
+ A6XX_PROTECT_NORDWR(0x00e03, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x03c00, 0x00c5),
+ A6XX_PROTECT_RDONLY(0x03cc6, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x08600, 0x01ff),
+ A6XX_PROTECT_NORDWR(0x08e00, 0x00ff),
+ A6XX_PROTECT_RDONLY(0x08f00, 0x0000),
+ A6XX_PROTECT_NORDWR(0x08f01, 0x01be),
+ A6XX_PROTECT_NORDWR(0x09600, 0x01ff),
+ A6XX_PROTECT_RDONLY(0x0981a, 0x02e5),
+ A6XX_PROTECT_NORDWR(0x09e00, 0x01ff),
+ A6XX_PROTECT_NORDWR(0x0a600, 0x01ff),
+ A6XX_PROTECT_NORDWR(0x0ae00, 0x0006),
+ A6XX_PROTECT_NORDWR(0x0ae08, 0x0006),
+ A6XX_PROTECT_NORDWR(0x0ae10, 0x036f),
+ A6XX_PROTECT_NORDWR(0x0b600, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x0dc00, 0x1fff),
+ A6XX_PROTECT_RDONLY(0x0fc00, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x18400, 0x003f),
+ A6XX_PROTECT_RDONLY(0x18440, 0x013f),
+ A6XX_PROTECT_NORDWR(0x18580, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x1b400, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x1f400, 0x0477),
+ A6XX_PROTECT_RDONLY(0x1f878, 0x0787),
+ A6XX_PROTECT_NORDWR(0x1f930, 0x0329),
+ A6XX_PROTECT_NORDWR(0x20000, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x27800, 0x007f),
+ A6XX_PROTECT_RDONLY(0x27880, 0x0381),
+ A6XX_PROTECT_NORDWR(0x27882, 0x0001),
+ /* CP_PROTECT_REG[46, 62] are left untouched! */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ A6XX_PROTECT_NORDWR(0x27c02, 0x0000),
+};
+DECLARE_ADRENO_PROTECT(a810_protect, 64);
+
+static const uint32_t a810_pwrup_reglist_regs[] = {
+ REG_A6XX_UCHE_MODE_CNTL,
+ REG_A8XX_UCHE_VARB_IDLE_TIMEOUT,
+ REG_A8XX_UCHE_GBIF_GX_CONFIG,
+ REG_A8XX_UCHE_CACHE_WAYS,
+ REG_A8XX_UCHE_CCHE_MODE_CNTL,
+ REG_A8XX_UCHE_CCHE_CACHE_WAYS,
+ REG_A8XX_UCHE_CCHE_GC_GMEM_RANGE_MIN,
+ REG_A8XX_UCHE_CCHE_GC_GMEM_RANGE_MIN + 1,
+ REG_A8XX_UCHE_WRITE_THRU_BASE,
+ REG_A8XX_UCHE_WRITE_THRU_BASE + 1,
+ REG_A8XX_UCHE_TRAP_BASE,
+ REG_A8XX_UCHE_TRAP_BASE + 1,
+ REG_A8XX_UCHE_CLIENT_PF,
+ REG_A8XX_VSC_BIN_SIZE,
+ REG_A8XX_RB_CMP_NC_MODE_CNTL,
+ REG_A7XX_SP_HLSQ_TIMEOUT_THRESHOLD_DP,
+ REG_A8XX_SP_HLSQ_GC_GMEM_RANGE_MIN,
+ REG_A8XX_SP_HLSQ_GC_GMEM_RANGE_MIN + 1,
+ REG_A7XX_SP_READ_SEL,
+};
+DECLARE_ADRENO_REGLIST_LIST(a810_pwrup_reglist);
+
+static const u32 a810_ifpc_reglist_regs[] = {
+ REG_A8XX_RBBM_NC_MODE_CNTL,
+ REG_A8XX_RBBM_SLICE_INTERFACE_HANG_INT_CNTL,
+ REG_A8XX_RBBM_SLICE_NC_MODE_CNTL,
+ REG_A6XX_SP_NC_MODE_CNTL,
+ REG_A7XX_SP_CHICKEN_BITS_2,
+ REG_A7XX_SP_CHICKEN_BITS_3,
+ REG_A6XX_SP_PERFCTR_SHADER_MASK,
+ REG_A6XX_TPL1_NC_MODE_CNTL,
+ REG_A6XX_TPL1_DBG_ECO_CNTL,
+ REG_A6XX_TPL1_DBG_ECO_CNTL1,
+ REG_A8XX_RBBM_PERFCTR_CNTL,
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(1),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(2),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(3),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(4),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(5),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(6),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(7),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(8),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(9),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(10),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(11),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(12),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(13),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(14),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(15),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(16),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(17),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(18),
+ REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(19),
+ REG_A8XX_CP_PROTECT_GLOBAL(0),
+ REG_A8XX_CP_PROTECT_GLOBAL(1),
+ REG_A8XX_CP_PROTECT_GLOBAL(2),
+ REG_A8XX_CP_PROTECT_GLOBAL(3),
+ REG_A8XX_CP_PROTECT_GLOBAL(4),
+ REG_A8XX_CP_PROTECT_GLOBAL(5),
+ REG_A8XX_CP_PROTECT_GLOBAL(6),
+ REG_A8XX_CP_PROTECT_GLOBAL(7),
+ REG_A8XX_CP_PROTECT_GLOBAL(8),
+ REG_A8XX_CP_PROTECT_GLOBAL(9),
+ REG_A8XX_CP_PROTECT_GLOBAL(10),
+ REG_A8XX_CP_PROTECT_GLOBAL(11),
+ REG_A8XX_CP_PROTECT_GLOBAL(12),
+ REG_A8XX_CP_PROTECT_GLOBAL(13),
+ REG_A8XX_CP_PROTECT_GLOBAL(14),
+ REG_A8XX_CP_PROTECT_GLOBAL(15),
+ REG_A8XX_CP_PROTECT_GLOBAL(16),
+ REG_A8XX_CP_PROTECT_GLOBAL(17),
+ REG_A8XX_CP_PROTECT_GLOBAL(18),
+ REG_A8XX_CP_PROTECT_GLOBAL(19),
+ REG_A8XX_CP_PROTECT_GLOBAL(20),
+ REG_A8XX_CP_PROTECT_GLOBAL(21),
+ REG_A8XX_CP_PROTECT_GLOBAL(22),
+ REG_A8XX_CP_PROTECT_GLOBAL(23),
+ REG_A8XX_CP_PROTECT_GLOBAL(24),
+ REG_A8XX_CP_PROTECT_GLOBAL(25),
+ REG_A8XX_CP_PROTECT_GLOBAL(26),
+ REG_A8XX_CP_PROTECT_GLOBAL(27),
+ REG_A8XX_CP_PROTECT_GLOBAL(28),
+ REG_A8XX_CP_PROTECT_GLOBAL(29),
+ REG_A8XX_CP_PROTECT_GLOBAL(30),
+ REG_A8XX_CP_PROTECT_GLOBAL(31),
+ REG_A8XX_CP_PROTECT_GLOBAL(32),
+ REG_A8XX_CP_PROTECT_GLOBAL(33),
+ REG_A8XX_CP_PROTECT_GLOBAL(34),
+ REG_A8XX_CP_PROTECT_GLOBAL(35),
+ REG_A8XX_CP_PROTECT_GLOBAL(36),
+ REG_A8XX_CP_PROTECT_GLOBAL(37),
+ REG_A8XX_CP_PROTECT_GLOBAL(38),
+ REG_A8XX_CP_PROTECT_GLOBAL(39),
+ REG_A8XX_CP_PROTECT_GLOBAL(40),
+ REG_A8XX_CP_PROTECT_GLOBAL(41),
+ REG_A8XX_CP_PROTECT_GLOBAL(42),
+ REG_A8XX_CP_PROTECT_GLOBAL(43),
+ REG_A8XX_CP_PROTECT_GLOBAL(44),
+ REG_A8XX_CP_PROTECT_GLOBAL(45),
+ REG_A8XX_CP_PROTECT_GLOBAL(63),
+};
+DECLARE_ADRENO_REGLIST_LIST(a810_ifpc_reglist);
+
+static const struct adreno_reglist_pipe a810_dyn_pwrup_reglist_regs[] = {
+ { REG_A8XX_CP_PROTECT_CNTL_PIPE, 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(15), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_GRAS_TSEFE_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_GRAS_NC_MODE_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_GRAS_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A7XX_RB_CCU_CNTL, 0, BIT(PIPE_BR) },
+ { REG_A8XX_RB_CCU_NC_MODE_CNTL, 0, BIT(PIPE_BR) },
+ { REG_A8XX_RB_CMP_NC_MODE_CNTL, 0, BIT(PIPE_BR) },
+ { REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0, BIT(PIPE_BR) },
+ { REG_A8XX_RB_CMP_DBG_ECO_CNTL, 0, BIT(PIPE_BR) },
+ { REG_A8XX_RB_GC_GMEM_PROTECT, 0, BIT(PIPE_BR) },
+ { REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE_ENABLE, 0, BIT(PIPE_BR) },
+ { REG_A8XX_VPC_FLATSHADE_MODE_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_1, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_2, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_3, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_4, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A6XX_PC_AUTO_VERTEX_STRIDE, 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_PC_VIS_STREAM_CNTL, 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_PC_CONTEXT_SWITCH_STABILIZE_CNTL_1, 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_VFD_CB_BV_THRESHOLD, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_BR_THRESHOLD, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_BUSY_REQ_CNT, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_LP_REQ_CNT, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A7XX_VFD_DBG_ECO_CNTL, 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+};
+DECLARE_ADRENO_REGLIST_PIPE_LIST(a810_dyn_pwrup_reglist);
+
static const struct adreno_reglist_pipe a840_nonctxt_regs[] = {
{ REG_A8XX_CP_SMMU_STREAM_ID_LPAC, 0x00000101, BIT(PIPE_NONE) },
{ REG_A8XX_GRAS_DBG_ECO_CNTL, 0x00000800, BIT(PIPE_BV) | BIT(PIPE_BR) },
@@ -2193,6 +2427,43 @@ static const struct adreno_info a8xx_gpus[] = {
{ 252, 2 },
{ 221, 3 },
),
+ }, {
+ .chip_ids = ADRENO_CHIP_IDS(0x44010000),
+ .family = ADRENO_8XX_GEN1,
+ .fw = {
+ [ADRENO_FW_SQE] = "gen80300_sqe.fw",
+ [ADRENO_FW_GMU] = "gen80300_gmu.bin",
+ },
+ .gmem = SZ_512K + SZ_64K,
+ .inactive_period = DRM_MSM_INACTIVE_PERIOD,
+ .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
+ ADRENO_QUIRK_HAS_HW_APRIV |
+ ADRENO_QUIRK_PREEMPTION |
+ ADRENO_QUIRK_IFPC,
+ .funcs = &a8xx_gpu_funcs,
+ .zapfw = "gen80300_zap.mbn",
+ .a6xx = &(const struct a6xx_info) {
+ .protect = &a810_protect,
+ .nonctxt_reglist = a810_nonctxt_regs,
+ .pwrup_reglist = &a810_pwrup_reglist,
+ .dyn_pwrup_reglist = &a810_dyn_pwrup_reglist,
+ .ifpc_reglist = &a810_ifpc_reglist,
+ .gbif_cx = a840_gbif,
+ .max_slices = 1,
+ .gmu_chipid = 0x8030000,
+ .bcms = (const struct a6xx_bcm[]) {
+ { .name = "SH0", .buswidth = 16 },
+ { .name = "MC0", .buswidth = 4 },
+ {
+ .name = "ACV",
+ .fixed = true,
+ .perfmode = BIT(2),
+ .perfmode_bw = 10687500,
+ },
+ { /* sentinel */ },
+ },
+ },
+ .preempt_record_size = 4558 * SZ_1K,
}
};
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index c0ee544ce257..d474d88b9152 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -596,6 +596,11 @@ static inline int adreno_is_a8xx(struct adreno_gpu *gpu)
return gpu->info->family >= ADRENO_8XX_GEN1;
}
+static inline int adreno_is_a810(struct adreno_gpu *gpu)
+{
+ return gpu->info->chip_ids[0] == 0x44010000;
+}
+
static inline int adreno_is_x285(struct adreno_gpu *gpu)
{
return gpu->info->chip_ids[0] == 0x44070001;
--
2.53.0
^ permalink raw reply related
* [PATCH 4/6] drm/msm/a8xx: use pipe protect slot 15 for last-span-unbound feature
From: Alexander Koskovich @ 2026-04-01 2:17 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio, Akhil P Oommen,
Bjorn Andersson
Cc: Luca Weiss, linux-arm-msm, dri-devel, freedreno, devicetree,
linux-kernel, Alexander Koskovich
In-Reply-To: <20260331-adreno-810-v1-0-725801dbb12b@pm.me>
A8XX GPUs have two sets of protect registers: 64 global slots and 16
pipe specific slots. The last-span-unbound feature is only available
on pipe protect registers, and should always target pipe slot 15.
This matches the downstream driver which hardcodes pipe slot 15 for
all A8XX GPUs (GRAPHICS.LA.15.0.r1) and resolves protect errors on
A810.
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
index 5af82d43f1e4..63387ee9b04a 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
@@ -252,11 +252,12 @@ static void a8xx_set_cp_protect(struct msm_gpu *gpu)
}
/*
- * Last span feature is only supported on PIPE specific register.
- * So update those here
+ * Last span setting is only being applied to the last pipe specific
+ * register. Hence duplicate the last span from protect reg into the
+ * BR and BV protect reg pipe 15.
*/
- a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_CP_PROTECT_PIPE(protect->count_max), final_cfg);
- a8xx_write_pipe(gpu, PIPE_BV, REG_A8XX_CP_PROTECT_PIPE(protect->count_max), final_cfg);
+ a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_CP_PROTECT_PIPE(15), final_cfg);
+ a8xx_write_pipe(gpu, PIPE_BV, REG_A8XX_CP_PROTECT_PIPE(15), final_cfg);
a8xx_aperture_clear(gpu);
}
--
2.53.0
^ permalink raw reply related
* [PATCH 2/6] drm/msm/adreno: rename llc_mmio to cx_mmio
From: Alexander Koskovich @ 2026-04-01 2:17 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio, Akhil P Oommen,
Bjorn Andersson
Cc: Luca Weiss, linux-arm-msm, dri-devel, freedreno, devicetree,
linux-kernel, Alexander Koskovich
In-Reply-To: <20260331-adreno-810-v1-0-725801dbb12b@pm.me>
This region is used for more than just LLCC, it also provides access to
software fuse values (raytracing, etc).
Rename relevant symbols from _llc to _cx for use in a follow up change
that decouples this from LLCC.
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 8 ++++----
drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 16 ++++++++--------
drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 14 +++++++-------
drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 2 +-
4 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
index 916c5d99c4d1..e2f66ca0e319 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
@@ -947,7 +947,7 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
/* Turn on TCM (Tightly Coupled Memory) retention */
if (adreno_is_a7xx(adreno_gpu))
- a6xx_llc_write(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL, 1);
+ a6xx_cx_write(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL, 1);
else if (!adreno_is_a8xx(adreno_gpu))
gmu_write(gmu, REG_A6XX_GMU_GENERAL_7, 1);
@@ -1215,7 +1215,7 @@ static int a6xx_gmu_secure_init(struct a6xx_gpu *a6xx_gpu)
if (!qcom_scm_is_available()) {
dev_warn_once(gpu->dev->dev,
"SCM is not available, poking fuse register\n");
- a6xx_llc_write(a6xx_gpu, REG_A7XX_CX_MISC_SW_FUSE_VALUE,
+ a6xx_cx_write(a6xx_gpu, REG_A7XX_CX_MISC_SW_FUSE_VALUE,
A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING |
A7XX_CX_MISC_SW_FUSE_VALUE_FASTBLEND |
A7XX_CX_MISC_SW_FUSE_VALUE_LPAC);
@@ -1236,7 +1236,7 @@ static int a6xx_gmu_secure_init(struct a6xx_gpu *a6xx_gpu)
* firmware, find out whether that's the case. The scm call
* above sets the fuse register.
*/
- fuse_val = a6xx_llc_read(a6xx_gpu,
+ fuse_val = a6xx_cx_read(a6xx_gpu,
REG_A7XX_CX_MISC_SW_FUSE_VALUE);
adreno_gpu->has_ray_tracing =
!!(fuse_val & A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING);
@@ -1299,7 +1299,7 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
/* Check to see if we are doing a cold or warm boot */
if (adreno_is_a7xx(adreno_gpu) || adreno_is_a8xx(adreno_gpu)) {
- status = a6xx_llc_read(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL) == 1 ?
+ status = a6xx_cx_read(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL) == 1 ?
GMU_WARM_BOOT : GMU_COLD_BOOT;
} else if (gmu->legacy) {
status = gmu_read(gmu, REG_A6XX_GMU_GENERAL_7) == 1 ?
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index e1eae6cb1e40..6ce14407007e 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -2039,7 +2039,7 @@ static void a6xx_llc_activate(struct a6xx_gpu *a6xx_gpu)
struct msm_gpu *gpu = &adreno_gpu->base;
u32 cntl1_regval = 0;
- if (IS_ERR(a6xx_gpu->llc_mmio))
+ if (IS_ERR(a6xx_gpu->cx_mmio))
return;
if (!llcc_slice_activate(a6xx_gpu->llc_slice)) {
@@ -2078,14 +2078,14 @@ static void a6xx_llc_activate(struct a6xx_gpu *a6xx_gpu)
* pagetables
*/
if (!a6xx_gpu->have_mmu500) {
- a6xx_llc_write(a6xx_gpu,
+ a6xx_cx_write(a6xx_gpu,
REG_A6XX_CX_MISC_SYSTEM_CACHE_CNTL_1, cntl1_regval);
/*
* Program cacheability overrides to not allocate cache
* lines on a write miss
*/
- a6xx_llc_rmw(a6xx_gpu,
+ a6xx_cx_rmw(a6xx_gpu,
REG_A6XX_CX_MISC_SYSTEM_CACHE_CNTL_0, 0xF, 0x03);
return;
}
@@ -2098,7 +2098,7 @@ static void a7xx_llc_activate(struct a6xx_gpu *a6xx_gpu)
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct msm_gpu *gpu = &adreno_gpu->base;
- if (IS_ERR(a6xx_gpu->llc_mmio))
+ if (IS_ERR(a6xx_gpu->cx_mmio))
return;
if (!llcc_slice_activate(a6xx_gpu->llc_slice)) {
@@ -2151,15 +2151,15 @@ static void a6xx_llc_slices_init(struct platform_device *pdev,
of_node_put(phandle);
if (is_a7xx || !a6xx_gpu->have_mmu500)
- a6xx_gpu->llc_mmio = msm_ioremap(pdev, "cx_mem");
+ a6xx_gpu->cx_mmio = msm_ioremap(pdev, "cx_mem");
else
- a6xx_gpu->llc_mmio = NULL;
+ a6xx_gpu->cx_mmio = NULL;
a6xx_gpu->llc_slice = llcc_slice_getd(LLCC_GPU);
a6xx_gpu->htw_llc_slice = llcc_slice_getd(LLCC_GPUHTW);
if (IS_ERR_OR_NULL(a6xx_gpu->llc_slice) && IS_ERR_OR_NULL(a6xx_gpu->htw_llc_slice))
- a6xx_gpu->llc_mmio = ERR_PTR(-EINVAL);
+ a6xx_gpu->cx_mmio = ERR_PTR(-EINVAL);
}
#define GBIF_CLIENT_HALT_MASK BIT(0)
@@ -2560,7 +2560,7 @@ static int a6xx_read_speedbin(struct device *dev, struct a6xx_gpu *a6xx_gpu,
return ret;
if (info->quirks & ADRENO_QUIRK_SOFTFUSE) {
- *speedbin = a6xx_llc_read(a6xx_gpu, REG_A8XX_CX_MISC_SW_FUSE_FREQ_LIMIT_STATUS);
+ *speedbin = a6xx_cx_read(a6xx_gpu, REG_A8XX_CX_MISC_SW_FUSE_FREQ_LIMIT_STATUS);
*speedbin = A8XX_CX_MISC_SW_FUSE_FREQ_LIMIT_STATUS_FINALFREQLIMIT(*speedbin);
return 0;
}
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
index eb431e5e00b1..e254d9711b30 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
@@ -102,7 +102,7 @@ struct a6xx_gpu {
bool has_whereami;
- void __iomem *llc_mmio;
+ void __iomem *cx_mmio;
void *llc_slice;
void *htw_llc_slice;
bool have_mmu500;
@@ -240,19 +240,19 @@ static inline bool a6xx_has_gbif(struct adreno_gpu *gpu)
return true;
}
-static inline void a6xx_llc_rmw(struct a6xx_gpu *a6xx_gpu, u32 reg, u32 mask, u32 or)
+static inline void a6xx_cx_rmw(struct a6xx_gpu *a6xx_gpu, u32 reg, u32 mask, u32 or)
{
- return msm_rmw(a6xx_gpu->llc_mmio + (reg << 2), mask, or);
+ return msm_rmw(a6xx_gpu->cx_mmio + (reg << 2), mask, or);
}
-static inline u32 a6xx_llc_read(struct a6xx_gpu *a6xx_gpu, u32 reg)
+static inline u32 a6xx_cx_read(struct a6xx_gpu *a6xx_gpu, u32 reg)
{
- return readl(a6xx_gpu->llc_mmio + (reg << 2));
+ return readl(a6xx_gpu->cx_mmio + (reg << 2));
}
-static inline void a6xx_llc_write(struct a6xx_gpu *a6xx_gpu, u32 reg, u32 value)
+static inline void a6xx_cx_write(struct a6xx_gpu *a6xx_gpu, u32 reg, u32 value)
{
- writel(value, a6xx_gpu->llc_mmio + (reg << 2));
+ writel(value, a6xx_gpu->cx_mmio + (reg << 2));
}
#define shadowptr(_a6xx_gpu, _ring) ((_a6xx_gpu)->shadow_iova + \
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
index 9e6f2ed69247..5af82d43f1e4 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
@@ -103,7 +103,7 @@ void a8xx_gpu_get_slice_info(struct msm_gpu *gpu)
return;
}
- slice_mask &= a6xx_llc_read(a6xx_gpu,
+ slice_mask &= a6xx_cx_read(a6xx_gpu,
REG_A8XX_CX_MISC_SLICE_ENABLE_FINAL);
a6xx_gpu->slice_mask = slice_mask;
--
2.53.0
^ permalink raw reply related
* [PATCH 3/6] drm/msm/adreno: set cx_mmio regardless of if platform has LLCC
From: Alexander Koskovich @ 2026-04-01 2:17 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio, Akhil P Oommen,
Bjorn Andersson
Cc: Luca Weiss, linux-arm-msm, dri-devel, freedreno, devicetree,
linux-kernel, Alexander Koskovich
In-Reply-To: <20260331-adreno-810-v1-0-725801dbb12b@pm.me>
Platforms without a LLCC (e.g. milos) still need to be able to read and
write to the cx_mem region. Previously if LLCC slices were unavailable
the cx_mmio mapping was overwritten with ERR_PTR, causing a crash when
the GMU later accessed cx_mem.
Move the cx_mmio mapping out of a6xx_llc_slices_init() into
a6xx_gpu_init() so that cx_mem mapping is independent of LLCC.
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 38 ++++++++++++++++-------------------
1 file changed, 17 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index 6ce14407007e..eea18ae7c019 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -2039,7 +2039,7 @@ static void a6xx_llc_activate(struct a6xx_gpu *a6xx_gpu)
struct msm_gpu *gpu = &adreno_gpu->base;
u32 cntl1_regval = 0;
- if (IS_ERR(a6xx_gpu->cx_mmio))
+ if (IS_ERR_OR_NULL(a6xx_gpu->llc_slice) && IS_ERR_OR_NULL(a6xx_gpu->htw_llc_slice))
return;
if (!llcc_slice_activate(a6xx_gpu->llc_slice)) {
@@ -2098,7 +2098,7 @@ static void a7xx_llc_activate(struct a6xx_gpu *a6xx_gpu)
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct msm_gpu *gpu = &adreno_gpu->base;
- if (IS_ERR(a6xx_gpu->cx_mmio))
+ if (IS_ERR_OR_NULL(a6xx_gpu->llc_slice) && IS_ERR_OR_NULL(a6xx_gpu->htw_llc_slice))
return;
if (!llcc_slice_activate(a6xx_gpu->llc_slice)) {
@@ -2135,31 +2135,12 @@ static void a6xx_llc_slices_destroy(struct a6xx_gpu *a6xx_gpu)
static void a6xx_llc_slices_init(struct platform_device *pdev,
struct a6xx_gpu *a6xx_gpu, bool is_a7xx)
{
- struct device_node *phandle;
-
/* No LLCC on non-RPMh (and by extension, non-GMU) SoCs */
if (adreno_has_gmu_wrapper(&a6xx_gpu->base))
return;
- /*
- * There is a different programming path for A6xx targets with an
- * mmu500 attached, so detect if that is the case
- */
- phandle = of_parse_phandle(pdev->dev.of_node, "iommus", 0);
- a6xx_gpu->have_mmu500 = (phandle &&
- of_device_is_compatible(phandle, "arm,mmu-500"));
- of_node_put(phandle);
-
- if (is_a7xx || !a6xx_gpu->have_mmu500)
- a6xx_gpu->cx_mmio = msm_ioremap(pdev, "cx_mem");
- else
- a6xx_gpu->cx_mmio = NULL;
-
a6xx_gpu->llc_slice = llcc_slice_getd(LLCC_GPU);
a6xx_gpu->htw_llc_slice = llcc_slice_getd(LLCC_GPUHTW);
-
- if (IS_ERR_OR_NULL(a6xx_gpu->llc_slice) && IS_ERR_OR_NULL(a6xx_gpu->htw_llc_slice))
- a6xx_gpu->cx_mmio = ERR_PTR(-EINVAL);
}
#define GBIF_CLIENT_HALT_MASK BIT(0)
@@ -2621,6 +2602,7 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev)
struct platform_device *pdev = priv->gpu_pdev;
struct adreno_platform_config *config = pdev->dev.platform_data;
const struct adreno_info *info = config->info;
+ struct device_node *phandle;
struct device_node *node;
struct a6xx_gpu *a6xx_gpu;
struct adreno_gpu *adreno_gpu;
@@ -2656,6 +2638,20 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev)
a6xx_llc_slices_init(pdev, a6xx_gpu, is_a7xx);
+ /*
+ * There is a different programming path for A6xx targets with an
+ * mmu500 attached, so detect if that is the case
+ */
+ phandle = of_parse_phandle(pdev->dev.of_node, "iommus", 0);
+ a6xx_gpu->have_mmu500 = (phandle &&
+ of_device_is_compatible(phandle, "arm,mmu-500"));
+ of_node_put(phandle);
+
+ if (is_a7xx || !a6xx_gpu->have_mmu500)
+ a6xx_gpu->cx_mmio = msm_ioremap(pdev, "cx_mem");
+ else
+ a6xx_gpu->cx_mmio = NULL;
+
ret = a6xx_set_supported_hw(&pdev->dev, a6xx_gpu, info);
if (ret) {
a6xx_llc_slices_destroy(a6xx_gpu);
--
2.53.0
^ permalink raw reply related
* [PATCH 1/6] dt-bindings: display/msm/gmu: Document Adreno 810 GMU
From: Alexander Koskovich @ 2026-04-01 2:16 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio, Akhil P Oommen,
Bjorn Andersson
Cc: Luca Weiss, linux-arm-msm, dri-devel, freedreno, devicetree,
linux-kernel, Alexander Koskovich
In-Reply-To: <20260331-adreno-810-v1-0-725801dbb12b@pm.me>
Document Adreno 810 GMU in the dt-binding specification.
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
.../devicetree/bindings/display/msm/gmu.yaml | 32 ++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/msm/gmu.yaml b/Documentation/devicetree/bindings/display/msm/gmu.yaml
index e32056ae0f5d..2853f6aef966 100644
--- a/Documentation/devicetree/bindings/display/msm/gmu.yaml
+++ b/Documentation/devicetree/bindings/display/msm/gmu.yaml
@@ -299,6 +299,38 @@ allOf:
required:
- qcom,qmp
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: qcom,adreno-gmu-810.0
+ then:
+ properties:
+ reg:
+ items:
+ - description: Core GMU registers
+ reg-names:
+ items:
+ - const: gmu
+ clocks:
+ items:
+ - description: GPU AHB clock
+ - description: GMU clock
+ - description: GPU CX clock
+ - description: GPU AXI clock
+ - description: GPU MEMNOC clock
+ - description: GMU HUB clock
+ - description: GPUSS DEMET clock
+ clock-names:
+ items:
+ - const: ahb
+ - const: gmu
+ - const: cxo
+ - const: axi
+ - const: memnoc
+ - const: hub
+ - const: demet
+
- if:
properties:
compatible:
--
2.53.0
^ permalink raw reply related
* [PATCH 0/6] Add support for Adreno 810 GPU
From: Alexander Koskovich @ 2026-04-01 2:16 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio, Akhil P Oommen,
Bjorn Andersson
Cc: Luca Weiss, linux-arm-msm, dri-devel, freedreno, devicetree,
linux-kernel, Alexander Koskovich
Adreno 810 is present in the Milos SoC and is the first GPU to be released in
the A8x family.
Note that the OPP table is limited to 1050MHz to start with as the only Milos
device I have is limited to that speed in GPU_CC_FREQ_LIMIT_VAL.
This series depends on a couple other in review series, batch 2 for A8x [1] and
the GXCLKCTL block for Milos [2].
There is also currently an issue on Milos with gx_clkctl_gx_gdsc being stuck on
during runtime PM [3]. The proper fix is to only toggle the GX GDSC during GMU
recovery, as the firmware manages it in all other cases. This is the same issue
seen on SM8750 and is being worked on by Qualcomm. Right now I am just working
around this locally by not collapsing the GX GDSC during runtime suspend.
[1]: https://lore.kernel.org/linux-arm-msm/20260327-a8xx-gpu-batch2-v2-0-2b53c38d2101@oss.qualcomm.com
[2]: https://lore.kernel.org/linux-arm-msm/20260306-milos-gxclkctl-v1-0-00b09ee159a7@fairphone.com
[3]: https://lore.kernel.org/linux-arm-msm/5409e13e-280c-47b6-a29f-351cb609bc6f@oss.qualcomm.com
Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
Alexander Koskovich (6):
dt-bindings: display/msm/gmu: Document Adreno 810 GMU
drm/msm/adreno: rename llc_mmio to cx_mmio
drm/msm/adreno: set cx_mmio regardless of if platform has LLCC
drm/msm/a8xx: use pipe protect slot 15 for last-span-unbound feature
drm/msm/adreno: add Adreno 810 GPU support
arm64: dts: qcom: milos: Add Adreno 810 GPU and GMU nodes
.../devicetree/bindings/display/msm/gmu.yaml | 32 +++
arch/arm64/boot/dts/qcom/milos.dtsi | 148 +++++++++++
drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 271 +++++++++++++++++++++
drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 8 +-
drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 44 ++--
drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 14 +-
drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 11 +-
drivers/gpu/drm/msm/adreno/adreno_gpu.h | 5 +
8 files changed, 493 insertions(+), 40 deletions(-)
---
base-commit: 128d2eccd20bd74fd104b412d949d869aa48f108
change-id: 20260330-adreno-810-5a47525522cd
Best regards,
--
Alexander Koskovich <akoskovich@pm.me>
^ permalink raw reply
* [PATCH 2/2] Revert "arm64: dts: imx8mp-kontron: Add support for reading SD_VSEL signal"
From: Peng Fan (OSS) @ 2026-04-01 2:05 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Frieder Schrempf
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Peng Fan
In-Reply-To: <20260401-imx8m-ldo5-v1-0-1b1c1381babd@nxp.com>
From: Peng Fan <peng.fan@nxp.com>
This reverts commit 39e4189d9d63a0b6fc15458ce0136e99ecdfb1b8.
The board uses SDHC VSELECT to automatically switch between 1.8v and
3.3v. It does not use GPIO to control the PMIC SD_VSEL signal.
The original commit intends to read back SD_VSEL value from GPIO,
but it is wrong. When MUX is configured as SDHC VSELECT, it is
impossible to read back the value from GPIO controller. Setting SION
could only enable the input path for the mux function. It could not
redirect the input to GPIO.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi
index bc1a261bb000ed0e8071d214111c12689bc394d0..ea69c639b30b8f7e05bb103074c6481d88560c53 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi
@@ -311,7 +311,6 @@ reg_nvcc_sd: LDO5 {
regulator-name = "NVCC_SD (LDO5)";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
- sd-vsel-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
};
};
};
@@ -815,7 +814,7 @@ MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 0x1d0 /* SDIO_A_D0 */
MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d0 /* SDIO_A_D1 */
MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d0 /* SDIO_A_D2 */
MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d0 /* SDIO_A_D3 */
- MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x400001d0
+ MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x1d0
>;
};
@@ -827,7 +826,7 @@ MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 0x1d4 /* SDIO_A_D0 */
MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d4 /* SDIO_A_D1 */
MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d4 /* SDIO_A_D2 */
MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d4 /* SDIO_A_D3 */
- MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x400001d0
+ MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x1d0
>;
};
@@ -839,7 +838,7 @@ MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 0x1d6 /* SDIO_A_D0 */
MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d6 /* SDIO_A_D1 */
MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d6 /* SDIO_A_D2 */
MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d6 /* SDIO_A_D3 */
- MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x400001d0
+ MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x1d0
>;
};
--
2.37.1
^ permalink raw reply related
* [PATCH 1/2] Revert "arm64: dts: imx8mm-kontron: Add support for reading SD_VSEL signal"
From: Peng Fan (OSS) @ 2026-04-01 2:05 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Frieder Schrempf
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Peng Fan
In-Reply-To: <20260401-imx8m-ldo5-v1-0-1b1c1381babd@nxp.com>
From: Peng Fan <peng.fan@nxp.com>
This reverts commit 8472751c4d96b558d60d0f6aede6b24b64bcb3c9.
The board uses SDHC VSELECT to automatically switch between 1.8v and
3.3v. It does not use GPIO to control the PMIC SD_VSEL signal.
The original commit intends to read back SD_VSEL value from GPIO,
but it is wrong. When MUX is configured as SDHC VSELECT, it is
impossible to read back the value from GPIO controller. Setting SION
could only enable the input path for the mux function. It could not
redirect the input to GPIO.
And value "0x40000d0" is wrong, SION is BIT30, not BIT26.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts | 10 +++-------
arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi | 7 +++----
2 files changed, 6 insertions(+), 11 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts b/arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts
index e756fe5db56b6a19c309fcbb94475629e5f2b2a0..dd59af0ebaae55ede743d4187b1165041d655cf2 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts
@@ -254,10 +254,6 @@ &pwm2 {
status = "okay";
};
-®_nvcc_sd {
- sd-vsel-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
-};
-
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1>;
@@ -466,7 +462,7 @@ MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d0
MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0
MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0
MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 0x19
- MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000d0
+ MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0xd0
>;
};
@@ -479,7 +475,7 @@ MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d4
MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4
MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4
MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 0x19
- MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000d0
+ MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0xd0
>;
};
@@ -492,7 +488,7 @@ MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d6
MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6
MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6
MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 0x19
- MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000d0
+ MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0xd0
>;
};
};
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi
index 96987910609f1b0083f5ae0c957f0baf10bfa826..4fb13d8ecfd45a8587e169b7d0a08e811745b5a9 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi
@@ -342,7 +342,6 @@ reg_nvcc_sd: LDO5 {
regulator-name = "NVCC_SD (LDO5)";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
- sd-vsel-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
};
};
};
@@ -795,7 +794,7 @@ MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d0 /* SDIO_A_D1 */
MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0 /* SDIO_A_D2 */
MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0 /* SDIO_A_D3 */
MX8MM_IOMUXC_SD2_WP_USDHC2_WP 0x400000d6 /* SDIO_A_WP */
- MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000090
+ MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x90
>;
};
@@ -808,7 +807,7 @@ MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d4 /* SDIO_A_D1 */
MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4 /* SDIO_A_D2 */
MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4 /* SDIO_A_D3 */
MX8MM_IOMUXC_SD2_WP_USDHC2_WP 0x400000d6 /* SDIO_A_WP */
- MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000090
+ MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x90
>;
};
@@ -821,7 +820,7 @@ MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d6 /* SDIO_A_D1 */
MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6 /* SDIO_A_D2 */
MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6 /* SDIO_A_D3 */
MX8MM_IOMUXC_SD2_WP_USDHC2_WP 0x400000d6 /* SDIO_A_WP */
- MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000090
+ MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x90
>;
};
--
2.37.1
^ permalink raw reply related
* [PATCH 0/2] arm64: dts: imx8m-kontron: Revert reading SD_VSEL signal
From: Peng Fan (OSS) @ 2026-04-01 2:05 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Frieder Schrempf
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Peng Fan
When MUX is configured as SDHC VSELECT, enabling SION is not able
to read back the SD_VSEL value. SION is used for force input path,
not to redirect the PAD value to GPIO(the other mux).
This has been confirmed by reading i.MX8MP RTL. we have not check
i.MX8MM RTL, but it should be same.
Not sure whether need to add Fixes commit for the patches, just revert
patches.
For the U-Boot support, either drop vqmmc-supply or switch to use gpio
control to replace vselect control.
And below patch should also be revisited.
commit 3ce6f4f943ddd9edc03e450a2a0d89cb025b165b
Author: Frieder Schrempf <frieder.schrempf@kontron.de>
Date: Wed Dec 18 16:27:27 2024 +0100
regulator: pca9450: Fix control register for LDO5
To supporting read back signal, need the MUX set as GPIO and support
in/out, not set mux as VSELECT.
TBH: I have not test setting MUX as GPIO, anyway we need to fix DT.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
Peng Fan (2):
Revert "arm64: dts: imx8mm-kontron: Add support for reading SD_VSEL signal"
Revert "arm64: dts: imx8mp-kontron: Add support for reading SD_VSEL signal"
arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts | 10 +++-------
arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi | 7 +++----
arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi | 7 +++----
3 files changed, 9 insertions(+), 15 deletions(-)
---
base-commit: 3b058d1aeeeff27a7289529c4944291613b364e9
change-id: 20260329-imx8m-ldo5-90e369066213
Best regards,
--
Peng Fan <peng.fan@nxp.com>
^ permalink raw reply
* RE: [PATCH v2 2/3] remoteproc: imx_rproc: Pass bootaddr to SM CPU/LMM reset vector
From: Peng Fan @ 2026-04-01 1:31 UTC (permalink / raw)
To: Peng Fan (OSS), Bjorn Andersson, Mathieu Poirier, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Baluta
Cc: linux-remoteproc@vger.kernel.org, devicetree@vger.kernel.org,
imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
In-Reply-To: <20260327-imx943-rproc-v2-2-a547a3588730@nxp.com>
Hi Mathieu,
> Subject: [PATCH v2 2/3] remoteproc: imx_rproc: Pass bootaddr to SM
> CPU/LMM reset vector
I had an follow-up patch to update reset-vector-mask for i.MX95-CM7
to make it could boot from non-TCM area, not included in this pachset
to avoid unnecessary churn for i.MX94 support, but I realize that
that patch should be squashed into this patch 2/3 to avoid breaking
i.MX95 M7 TCM booting.
Although in my test, cm7 luckily boots up without crash[1], but in theory,
below change should be included in patch 2/3. I will include below change
in v3 after we clear the reset-vector-mask question.
Daniel, please see whether this is ok for you, since you reviewed this
patch.
diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
index 525a92e03e8ab..930dd9eca6fb5 100644
--- a/drivers/remoteproc/imx_rproc.c
+++ b/drivers/remoteproc/imx_rproc.c
@@ -1541,6 +1541,7 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx95_m7 = {
/* Must align with System Manager Firmware */
.cpuid = 1, /* Use 1 as cpu id for M7 core */
.lmid = 1, /* Use 1 as Logical Machine ID where M7 resides */
+ .reset_vector_mask = GENMASK_U32(31, 16),
};
static const struct of_device_id imx_rproc_of_match[] = {
[1]
[ 135.738523] remoteproc remoteproc0: powering up imx-rproc
[ 135.748464] remoteproc remoteproc0: Booting fw image imx95-19x19-evk_m7_TCM_rpmsg_lite_str_echo_rtos.elf, size 98996
[ 135.759229] imx-rproc imx95-cm7: lmm(1) powered on by Linux
[ 135.764900] ============== entry 0x91d
[ 135.772597] xxxxxxxxxxxx bootaddr 0x91d
[ 135.775818] rproc-virtio rproc-virtio.2.auto: assigned reserved memory node vdevbuffer@88020000
[ 135.780278] /soc/bus@42000000/i2c@426d0000/tcpc@50/connector: Fixed dependency cycle(s) with /soc/usb@4c010010/usb@4c100000
[ 135.795970] /soc/usb@4c010010/usb@4c100000: Fixed dependency cycle(s) with /soc/bus@42000000/i2c@426d0000/tcpc@50/connector
[ 135.807727] virtio_rpmsg_bus virtio0: rpmsg host is online
[ 135.811546] virtio_rpmsg_bus virtio0: creating channel rpmsg-i2c-channel addr 0x1
Thanks,
Peng.
>
> From: Peng Fan <peng.fan@nxp.com>
>
> Cortex-M[7,33] processors use a fixed reset vector table format:
>
> 0x00 Initial SP value
> 0x04 Reset vector
> 0x08 NMI
> 0x0C ...
> ...
> IRQ[n]
>
> In ELF images, the corresponding layout is:
>
> reset_vectors: --> hardware reset address
> .word __stack_end__
> .word Reset_Handler
> .word NMI_Handler
> .word HardFault_Handler
> ...
> .word UART_IRQHandler
> .word SPI_IRQHandler
> ...
>
> Reset_Handler: --> ELF entry point address
> ...
>
> The hardware fetches the first two words from reset_vectors and
> populates SP with __stack_end__ and PC with Reset_Handler.
> Execution proceeds from Reset_Handler.
>
> However, the ELF entry point does not always match the hardware
> reset address. For example, on i.MX94 CM33S:
>
> ELF entry point: 0x0ffc211d
> hardware reset base: 0x0ffc0000 (default reset value, sw
> programmable)
>
> To derive the correct hardware reset address, the unused lower bits
> must be masked off. The boot code should apply a SoC-specific mask
> before programming the reset address registers, e.g.:
>
> reset_address = entry & reset_vector_mask
>
> Current driver always programs the reset vector as 0. But i.MX94
> CM33S's default reset base is 0x0ffc0000, so the correct reset vector
> must be passed to the SM API; otherwise the M33 Sync core cannot
> boot successfully.
>
> rproc_elf_get_boot_addr() returns the ELF entry point, which is not the
> hardware reset vector address. To derive the proper reset vector, this
> patch introduces imx_rproc_get_boot_addr(), which masks the ELF
> entry point using the SoC‑specific 'reset_vector_mask'. The resulting
> reset vector address is then passed to the SM CPU/LMM reset vector
> API calls.
>
> Signed-off-by: Peng Fan <peng.fan@nxp.com>
> ---
> drivers/remoteproc/imx_rproc.c | 17 ++++++++++++++---
> drivers/remoteproc/imx_rproc.h | 2 ++
> 2 files changed, 16 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/remoteproc/imx_rproc.c
> b/drivers/remoteproc/imx_rproc.c index
> 0dd80e688b0ea3df4c66e5726884dc86c8a5a881..d8ead42640881bd5
> 23d605fa7002935ef6e98077 100644
> --- a/drivers/remoteproc/imx_rproc.c
> +++ b/drivers/remoteproc/imx_rproc.c
> @@ -345,7 +345,7 @@ static int imx_rproc_sm_cpu_start(struct rproc
> *rproc)
> const struct imx_rproc_dcfg *dcfg = priv->dcfg;
> int ret;
>
> - ret = scmi_imx_cpu_reset_vector_set(dcfg->cpuid, 0, true, false,
> false);
> + ret = scmi_imx_cpu_reset_vector_set(dcfg->cpuid, rproc-
> >bootaddr,
> +true, false, false);
> if (ret) {
> dev_err(priv->dev, "Failed to set reset vector
> cpuid(%u): %d\n", dcfg->cpuid, ret);
> return ret;
> @@ -365,7 +365,7 @@ static int imx_rproc_sm_lmm_start(struct
> rproc *rproc)
> * If the remoteproc core can't start the M7, it will already be
> * handled in imx_rproc_sm_lmm_prepare().
> */
> - ret = scmi_imx_lmm_reset_vector_set(dcfg->lmid, dcfg->cpuid,
> 0, 0);
> + ret = scmi_imx_lmm_reset_vector_set(dcfg->lmid, dcfg->cpuid,
> 0,
> +rproc->bootaddr);
> if (ret) {
> dev_err(dev, "Failed to set reset vector lmid(%u),
> cpuid(%u): %d\n",
> dcfg->lmid, dcfg->cpuid, ret);
> @@ -739,6 +739,17 @@ imx_rproc_elf_find_loaded_rsc_table(struct
> rproc *rproc, const struct firmware *
> return rproc_elf_find_loaded_rsc_table(rproc, fw); }
>
> +static u64 imx_rproc_get_boot_addr(struct rproc *rproc, const struct
> +firmware *fw) {
> + struct imx_rproc *priv = rproc->priv;
> + u32 reset_vector_mask = GENMASK_U32(31, 0);
> +
> + if (priv->dcfg->reset_vector_mask)
> + reset_vector_mask = priv->dcfg->reset_vector_mask;
> +
> + return rproc_elf_get_boot_addr(rproc, fw) &
> reset_vector_mask; }
> +
> static const struct rproc_ops imx_rproc_ops = {
> .prepare = imx_rproc_prepare,
> .attach = imx_rproc_attach,
> @@ -752,7 +763,7 @@ static const struct rproc_ops imx_rproc_ops = {
> .find_loaded_rsc_table = imx_rproc_elf_find_loaded_rsc_table,
> .get_loaded_rsc_table = imx_rproc_get_loaded_rsc_table,
> .sanity_check = rproc_elf_sanity_check,
> - .get_boot_addr = rproc_elf_get_boot_addr,
> + .get_boot_addr = imx_rproc_get_boot_addr,
> };
>
> static int imx_rproc_addr_init(struct imx_rproc *priv, diff --git
> a/drivers/remoteproc/imx_rproc.h b/drivers/remoteproc/imx_rproc.h
> index
> d37e6f90548cec727b4aeb874680b42af85bdbb4..0d7d48352a1091ad
> 24e8e083172ce6da6d26ae10 100644
> --- a/drivers/remoteproc/imx_rproc.h
> +++ b/drivers/remoteproc/imx_rproc.h
> @@ -41,6 +41,8 @@ struct imx_rproc_dcfg {
> /* For System Manager(SM) based SoCs */
> u32 cpuid; /* ID of the remote
> core */
> u32 lmid; /* ID of the Logcial
> Machine */
> + /* reset_vector = elf_entry_addr & reset_vector_mask */
> + u32 reset_vector_mask;
> };
>
> #endif /* _IMX_RPROC_H */
>
> --
> 2.37.1
^ permalink raw reply related
* Re: [RFC PATCH 1/1] dt-bindings: iommu: riscv: Add bindings for Tenstorrent RISC-V IOMMU
From: Joel Stanley @ 2026-04-01 1:27 UTC (permalink / raw)
To: Nicholas Piggin, Drew Fustini
Cc: devicetree, Tomasz Jeznach, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, linux-riscv, linux-kernel, Michael Ellerman
In-Reply-To: <20260310003850.3837030-1-npiggin@gmail.com>
On Tue, 10 Mar 2026 at 11:09, Nicholas Piggin <npiggin@gmail.com> wrote:
>
> Extend the binding to cover details specific to the Tenstorrent RISC-V
> IOMMU. In particular, a second register range is added which contains
> M-privileged registers, e.g., PMAs and PMPs.
>
> The RISC-V spec S-privileged registers remain in the first register
> range and are compatible with "riscv,iommu" so the Linux driver does not
> notice any difference, but the binding will be used by OpenSBI and
> potentially other M-mode software.
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Drew, will you take this through the the tt soc tree?
^ permalink raw reply
* Re: [PATCH v4 7/9] regulator: Add MediaTek MT6392 regulator
From: kernel test robot @ 2026-04-01 1:21 UTC (permalink / raw)
To: Luca Leonardo Scorcia, linux-mediatek
Cc: llvm, oe-kbuild-all, Fabien Parent, Val Packett,
Luca Leonardo Scorcia, AngeloGioacchino Del Regno,
Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sen Chu, Sean Wang, Macpaul Lin, Lee Jones, Matthias Brugger,
Linus Walleij, Liam Girdwood, Mark Brown, Julien Massot,
Louis-Alexis Eyraud, Gary Bisson, Chen Zhong, linux-input,
devicetree, linux-kernel, linux-pm, linux-arm-kernel, linux-gpio
In-Reply-To: <20260330083429.359819-8-l.scorcia@gmail.com>
Hi Luca,
kernel test robot noticed the following build warnings:
[auto build test WARNING on lee-mfd/for-mfd-next]
[also build test WARNING on broonie-regulator/for-next linusw-pinctrl/devel linusw-pinctrl/for-next lee-mfd/for-mfd-fixes linus/master v7.0-rc6 next-20260330]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Luca-Leonardo-Scorcia/dt-bindings-mfd-mt6397-Add-MT6392-PMIC/20260331-081127
base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
patch link: https://lore.kernel.org/r/20260330083429.359819-8-l.scorcia%40gmail.com
patch subject: [PATCH v4 7/9] regulator: Add MediaTek MT6392 regulator
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20260401/202604010924.UuETwSKZ-lkp@intel.com/config)
compiler: clang version 17.0.6 (https://github.com/llvm/llvm-project 6009708b4367171ccdbf4b5905cb6a803753fe18)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260401/202604010924.UuETwSKZ-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202604010924.UuETwSKZ-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/regulator/mt6392-regulator.c:181:18: warning: unused variable 'ldo_volt_table1b' [-Wunused-const-variable]
181 | static const u32 ldo_volt_table1b[] = {
| ^~~~~~~~~~~~~~~~
1 warning generated.
vim +/ldo_volt_table1b +181 drivers/regulator/mt6392-regulator.c
180
> 181 static const u32 ldo_volt_table1b[] = {
182 1500000, 1800000, 2500000, 2800000,
183 };
184
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH 2/2] hwmon: pmbus: Add support for Sony APS-379
From: Guenter Roeck @ 2026-04-01 1:15 UTC (permalink / raw)
To: Chris Packham, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org
Cc: devicetree@vger.kernel.org, linux-hwmon@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <c41a0423-d81d-44a4-bee6-79f5bc66e961@alliedtelesis.co.nz>
On 3/31/26 17:26, Chris Packham wrote:
>
> On 01/04/2026 12:56, Guenter Roeck wrote:
>> Hi Chris,
>>
>> On 3/31/26 16:19, Chris Packham wrote:
>>> Add pmbus support for Sony APS-379 power supplies. There are a few PMBUS
>>> commands that return data that is undocumented/invalid so these need to
>>> be rejected with -ENXIO. The READ_VOUT command returns data in linear11
>>> format instead of linear16 so we need to workaround this.
>>>
>>> Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
>>
>> Feedback inline.
>>
>> Thanks,
>> Guenter
>>
>>> ---
>>> drivers/hwmon/pmbus/Kconfig | 6 ++
>>> drivers/hwmon/pmbus/Makefile | 1 +
>>> drivers/hwmon/pmbus/aps-379.c | 196 ++++++++++++++++++++++++++++++++++
>>> 3 files changed, 203 insertions(+)
>>> create mode 100644 drivers/hwmon/pmbus/aps-379.c
>>>
>>> diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
>>> index fc1273abe357..29076921e330 100644
>>> --- a/drivers/hwmon/pmbus/Kconfig
>>> +++ b/drivers/hwmon/pmbus/Kconfig
>>> @@ -77,6 +77,12 @@ config SENSORS_ADP1050_REGULATOR
>>> µModule regulators that can provide microprocessor power from
>>> 54V
>>> power distribution architecture.
>>> +config SENSORS_APS_379
>>> + tristate "Sony APS-379 Power Supplies"
>>> + help
>>> + If you say yes here you get hardware monitoring support for Sony
>>> + APS-379 Power Supplies.
>>> +
>>> config SENSORS_BEL_PFE
>>> tristate "Bel PFE Compatible Power Supplies"
>>> help
>>> diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
>>> index d6c86924f887..94f36c7069ec 100644
>>> --- a/drivers/hwmon/pmbus/Makefile
>>> +++ b/drivers/hwmon/pmbus/Makefile
>>> @@ -9,6 +9,7 @@ obj-$(CONFIG_SENSORS_ACBEL_FSG032) += acbel-fsg032.o
>>> obj-$(CONFIG_SENSORS_ADM1266) += adm1266.o
>>> obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
>>> obj-$(CONFIG_SENSORS_ADP1050) += adp1050.o
>>> +obj-$(CONFIG_SENSORS_APS_379) += aps-379.o
>>> obj-$(CONFIG_SENSORS_BEL_PFE) += bel-pfe.o
>>> obj-$(CONFIG_SENSORS_BPA_RS600) += bpa-rs600.o
>>> obj-$(CONFIG_SENSORS_DELTA_AHE50DC_FAN) += delta-ahe50dc-fan.o
>>> diff --git a/drivers/hwmon/pmbus/aps-379.c
>>> b/drivers/hwmon/pmbus/aps-379.c
>>> new file mode 100644
>>> index 000000000000..e4c4c2d12bc9
>>> --- /dev/null
>>> +++ b/drivers/hwmon/pmbus/aps-379.c
>>
>> Driver documentation is missing.
>>
>> This power supply does not seem to be documented anywhere, so this is
>> actually quite
>> important.
>
> I'll see what I can add. I do have a list of the actual supported PMBus
> commands so that would be helpful to share.
>
>> Having said this, the behavior seems quite similar to BluTek
>> BPA-RS600. Are those
>> power supplies from the same OEM ?
>
> The BPA-RS600 is a smaller form factor. BluTek do make PSUs in the same
> from factor (there's a DC input one we use that I might get around to
> upstreaming one day), but they are genuinely different suppliers and the
> definitely don't interop electrically. Part of the reason for us using
> the APS-379 is manufactured by a Japanese company which lets us sell it
> into markets that have country of origin restrictions (that's as much
> politics as I care to discuss). They all have different
> quirks/deviations from the PMBus spec.
>
> I did of course refer to the bpa-rs600 driver as I was familiar with it.
> I pulled the linear11 vout workaround from mp5990 and adapted it for my
> needs.
>
I asked the AI version of Google search if they use the same OEM. It suggested
that they probably use the same or a similar controller chip and use the
firmware provided from that manufacturer as base for their firmware
implementation. Guess that makes sense.
Guenter
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox