* [PATCH v2 2/2] arm64: dts: qcom: Add Motorola Edge 30 (dubai) DTS
From: Val Packett @ 2026-04-03 5:33 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Kees Cook, Tony Luck, Guilherme G. Piccoli
Cc: Val Packett, ~postmarketos/upstreaming, phone-devel,
linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <20260403054417.167917-1-val@packett.cool>
The Motorola Edge 30 is a smartphone released in 2022.
This commit has the following features working:
- Display (simplefb)
- Touchscreen
- Power and volume buttons
- Storage (UFS 3.1)
- Battery (ADSP battmgr)
- USB (Type-C, 2.0, dual-role)
- Wi-Fi and Bluetooth (WCN6750 hw1.0)
Signed-off-by: Val Packett <val@packett.cool>
---
v2: Apply suggestions from Konrad
v1: https://lore.kernel.org/all/20260329103055.96649-2-val@packett.cool/
---
arch/arm64/boot/dts/qcom/Makefile | 1 +
.../boot/dts/qcom/sm7325-motorola-dubai.dts | 1456 +++++++++++++++++
2 files changed, 1457 insertions(+)
create mode 100644 arch/arm64/boot/dts/qcom/sm7325-motorola-dubai.dts
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index c46d94bb6dd5..c4d5dba342e3 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -334,6 +334,7 @@ dtb-$(CONFIG_ARCH_QCOM) += sm6375-sony-xperia-murray-pdx225.dtb
dtb-$(CONFIG_ARCH_QCOM) += sm7125-xiaomi-curtana.dtb
dtb-$(CONFIG_ARCH_QCOM) += sm7125-xiaomi-joyeuse.dtb
dtb-$(CONFIG_ARCH_QCOM) += sm7225-fairphone-fp4.dtb
+dtb-$(CONFIG_ARCH_QCOM) += sm7325-motorola-dubai.dtb
dtb-$(CONFIG_ARCH_QCOM) += sm7325-nothing-spacewar.dtb
dtb-$(CONFIG_ARCH_QCOM) += sm8150-hdk.dtb
dtb-$(CONFIG_ARCH_QCOM) += sm8150-microsoft-surface-duo.dtb
diff --git a/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai.dts b/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai.dts
new file mode 100644
index 000000000000..3c836e196b19
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai.dts
@@ -0,0 +1,1456 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright (c) 2026, Val Packett <val@packett.cool>
+ */
+
+/dts-v1/;
+
+/* PM7250B is configured to use SID8/9 */
+#define PM7250B_SID 8
+#define PM7250B_SID1 9
+
+#include <dt-bindings/arm/qcom,ids.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/iio/qcom,spmi-adc7-pm7325.h>
+#include <dt-bindings/iio/qcom,spmi-adc7-pmk8350.h>
+#include <dt-bindings/leds/common.h>
+#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+#include "sm7325.dtsi"
+#include "pm7325.dtsi"
+#include "pm7250b.dtsi"
+#include "pm8350c.dtsi" /* PM7350C */
+#include "pmk8350.dtsi" /* PMK7325 */
+
+/ {
+ model = "Motorola Edge 30";
+ compatible = "motorola,dubai", "qcom,sm7325";
+ chassis-type = "handset";
+
+ aliases {
+ bluetooth0 = &bluetooth;
+ serial0 = &uart5;
+ serial1 = &uart7;
+ wifi0 = &wifi;
+ };
+
+ chosen {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ stdout-path = "serial0:115200n8";
+
+ framebuffer {
+ compatible = "simple-framebuffer";
+ memory-region = <&framebuffer_mem>;
+ width = <1080>;
+ height = <2400>;
+ stride = <(1080 * 4)>;
+ format = "a8r8g8b8";
+
+ clocks = <&gcc GCC_DISP_HF_AXI_CLK>,
+ <&dispcc DISP_CC_MDSS_MDP_CLK>,
+ <&dispcc DISP_CC_MDSS_BYTE0_CLK>,
+ <&dispcc DISP_CC_MDSS_BYTE0_INTF_CLK>,
+ <&dispcc DISP_CC_MDSS_PCLK0_CLK>,
+ <&dispcc DISP_CC_MDSS_VSYNC_CLK>;
+ power-domains = <&dispcc DISP_CC_MDSS_CORE_GDSC>;
+ };
+ };
+
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ pinctrl-0 = <&kypd_vol_up_n>;
+ pinctrl-names = "default";
+
+ key-volume-up {
+ label = "Volume Up";
+ gpios = <&pm7325_gpios 6 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_VOLUMEUP>;
+ };
+ };
+
+ pmic-glink {
+ compatible = "qcom,sm7325-pmic-glink",
+ "qcom,qcm6490-pmic-glink",
+ "qcom,pmic-glink";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ orientation-gpios = <&tlmm 140 GPIO_ACTIVE_HIGH>;
+
+ connector@0 {
+ compatible = "usb-c-connector";
+ reg = <0>;
+
+ power-role = "dual";
+ data-role = "dual";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ pmic_glink_hs_in: endpoint {
+ remote-endpoint = <&usb_1_dwc3_hs>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+
+ pmic_glink_sbu: endpoint {
+ remote-endpoint = <&fsa4480_sbu_mux>;
+ };
+ };
+ };
+ };
+ };
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ ramoops@ae000000 {
+ compatible = "ramoops";
+ reg = <0x0 0xae000000 0x0 0xc0000>;
+ console-size = <0x40000>;
+ mem-type = <2>;
+ pmsg-size = <0x40000>;
+ record-size = <0x3f800>;
+ };
+
+ removed@c0000000 {
+ reg = <0x0 0xc0000000 0x0 0x5100000>;
+ no-map;
+ };
+
+ framebuffer_mem: framebuffer@e1000000 {
+ reg = <0x0 0xe1000000 0x0 (1080 * 2400 * 4)>;
+ no-map;
+ };
+
+ linux,cma {
+ compatible = "shared-dma-pool";
+ size = <0x0 0x8000000>;
+ reusable;
+ linux,cma-default;
+ };
+ };
+
+ thermal-zones {
+ cam-flash-thermal {
+ thermal-sensors = <&pmk8350_adc_tm 2>;
+
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ chg-skin-thermal {
+ thermal-sensors = <&pm7250b_adc_tm 0>;
+
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ chg-thermal {
+ thermal-sensors = <&pmk8350_adc_tm 4>;
+
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ conn-thermal {
+ thermal-sensors = <&pm7250b_adc_tm 1>;
+
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ pa-1-thermal {
+ thermal-sensors = <&pmk8350_adc_tm 5>;
+
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ pa-2-thermal {
+ thermal-sensors = <&pmk8350_adc_tm 6>;
+
+ /* Reports negative temperature. */
+ status = "disabled";
+
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ quiet-thermal {
+ thermal-sensors = <&pmk8350_adc_tm 1>;
+
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ sdm-skin-thermal {
+ thermal-sensors = <&pmk8350_adc_tm 3>;
+
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ xo-thermal {
+ thermal-sensors = <&pmk8350_adc_tm 0>;
+
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+ };
+
+ // S2B is really ebi.lvl but it's there for supply map completeness sake.
+ vreg_s2b_0p7: smpb2-regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "vreg_s2b_0p7";
+
+ regulator-min-microvolt = <65535>;
+ regulator-max-microvolt = <65535>;
+ regulator-always-on;
+ vin-supply = <&vph_pwr>;
+ };
+
+ vph_pwr: vph-pwr-regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "vph_pwr";
+ regulator-min-microvolt = <3700000>;
+ regulator-max-microvolt = <3700000>;
+ };
+
+ wcn6750-pmu {
+ compatible = "qcom,wcn6750-pmu";
+ pinctrl-0 = <&bt_en>;
+ pinctrl-names = "default";
+ vddaon-supply = <&mos_0p95_aon_s10c>;
+ vddasd-supply = <&vreg_l11c_2p8>;
+ vddpmu-supply = <&mos_0p95_dig_s10c>;
+ vddio-supply = <&vdd18_io>;
+ vddrfa0p8-supply = <&vdd09_pmu_rfa_i>;
+ vddrfa1p2-supply = <&vdd13_pmu_rfa_i>;
+ vddrfa1p7-supply = <&vdd19_pmu_rfa_i>;
+ vddrfa2p2-supply = <&vdd22_wlpa_s1c>;
+
+ bt-enable-gpios = <&tlmm 85 GPIO_ACTIVE_HIGH>;
+
+ regulators {
+ vreg_pmu_rfa_cmn: ldo0 {
+ regulator-name = "vreg_pmu_rfa_cmn";
+ };
+
+ vreg_pmu_aon_0p59: ldo1 {
+ regulator-name = "vreg_pmu_aon_0p59";
+ };
+
+ vreg_pmu_wlcx_0p8: ldo2 {
+ regulator-name = "vreg_pmu_wlcx_0p8";
+ };
+
+ vreg_pmu_wlmx_0p85: ldo3 {
+ regulator-name = "vreg_pmu_wlmx_0p85";
+ };
+
+ vreg_pmu_btcmx_0p85: ldo4 {
+ regulator-name = "vreg_pmu_btcmx_0p85";
+ };
+
+ vreg_pmu_rfa_0p8: ldo5 {
+ regulator-name = "vreg_pmu_rfa_0p8";
+ };
+
+ vreg_pmu_rfa_1p2: ldo6 {
+ regulator-name = "vreg_pmu_rfa_1p2";
+ };
+
+ vreg_pmu_rfa_1p7: ldo7 {
+ regulator-name = "vreg_pmu_rfa_1p7";
+ };
+
+ vreg_pmu_pcie_0p9: ldo8 {
+ regulator-name = "vreg_pmu_pcie_0p9";
+ };
+
+ vreg_pmu_pcie_1p8: ldo9 {
+ regulator-name = "vreg_pmu_pcie_1p8";
+ };
+ };
+ };
+};
+
+&apps_rsc {
+ regulators-0 {
+ compatible = "qcom,pm7325-rpmh-regulators";
+ qcom,pmic-id = "b";
+
+ vdd-s1-supply = <&vph_pwr>;
+ vdd-s2-supply = <&vph_pwr>;
+ vdd-s7-supply = <&vph_pwr>;
+ vdd-s8-supply = <&vph_pwr>;
+
+ vdd-l1-l4-l12-l15-supply = <&vreg_s7b_0p952>;
+ vdd-l2-l7-supply = <&vreg_bob>;
+ vdd-l3-supply = <&vreg_s2b_0p7>;
+ vdd-l5-supply = <&vreg_s2b_0p7>;
+ vdd-l6-l9-l10-supply = <&vreg_s8b_1p256>;
+ vdd-l8-supply = <&vreg_s7b_0p952>;
+ vdd-l11-l17-l18-l19-supply = <&vreg_s1b_1p856>;
+ vdd-l13-supply = <&vreg_s7b_0p952>;
+ vdd-l14-l16-supply = <&vreg_s8b_1p256>;
+
+ /*
+ * S2, L4-L5 are ARCs:
+ * S2 - ebi.lvl,
+ * L4 - lmx.lvl,
+ * l5 - lcx.lvl.
+ *
+ * L10 are unused.
+ */
+
+ vdd19_pmu_rfa_i:
+ vreg_s1b_1p856: smps1 {
+ regulator-name = "vreg_s1b_1p856";
+ regulator-min-microvolt = <1840000>;
+ regulator-max-microvolt = <2040000>;
+ };
+
+ mos_0p95_aon_s10c:
+ mos_0p95_dig_s10c:
+ vdd09_pmu_rfa_i:
+ vreg_s7b_0p952: smps7 {
+ regulator-name = "vreg_s7b_0p952";
+ regulator-min-microvolt = <535000>;
+ regulator-max-microvolt = <1120000>;
+ };
+
+ vdd13_pmu_rfa_i:
+ vreg_s8b_1p256: smps8 {
+ regulator-name = "vreg_s8b_1p256";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_RET>;
+ };
+
+ vreg_l1b_0p912: ldo1 {
+ regulator-name = "vreg_l1b_0p912";
+ regulator-min-microvolt = <825000>;
+ regulator-max-microvolt = <925000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vdd_a_usbhs_3p1:
+ vreg_l2b_3p072: ldo2 {
+ regulator-name = "vreg_l2b_3p072";
+ regulator-min-microvolt = <2700000>;
+ regulator-max-microvolt = <3544000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l3b_0p6: ldo3 {
+ regulator-name = "vreg_l3b_0p6";
+ regulator-min-microvolt = <312000>;
+ regulator-max-microvolt = <910000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vdd_a_dsi_0_1p2:
+ vdd_a_ufs_0_1p2:
+ vreg_l6b_1p2: ldo6 {
+ regulator-name = "vreg_l6b_1p2";
+ regulator-min-microvolt = <1140000>;
+ regulator-max-microvolt = <1260000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ regulator-allow-set-load;
+ regulator-allowed-modes = <RPMH_REGULATOR_MODE_LPM
+ RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l7b_2p96: ldo7 {
+ regulator-name = "vreg_l7b_2p96";
+ regulator-min-microvolt = <2400000>;
+ regulator-max-microvolt = <3544000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ regulator-allow-set-load;
+ regulator-allowed-modes = <RPMH_REGULATOR_MODE_LPM
+ RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l8b_0p904: ldo8 {
+ regulator-name = "vreg_l8b_0p904";
+ regulator-min-microvolt = <870000>;
+ regulator-max-microvolt = <970000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l9b_1p2: ldo9 {
+ regulator-name = "vreg_l9b_1p2";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1304000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ regulator-allow-set-load;
+ regulator-allowed-modes = <RPMH_REGULATOR_MODE_LPM
+ RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l11b_1p776: ldo11 {
+ regulator-name = "vreg_l11b_1p776";
+ regulator-min-microvolt = <1504000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l12b_0p8: ldo12 {
+ regulator-name = "vreg_l12b_0p8";
+ regulator-min-microvolt = <751000>;
+ regulator-max-microvolt = <824000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l13b_0p8: ldo13 {
+ regulator-name = "vreg_l13b_0p8";
+ regulator-min-microvolt = <530000>;
+ regulator-max-microvolt = <824000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l14b_1p2: ldo14 {
+ regulator-name = "vreg_l14b_1p2";
+ regulator-min-microvolt = <1080000>;
+ regulator-max-microvolt = <1304000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l15b_0p88: ldo15 {
+ regulator-name = "vreg_l15b_0p88";
+ regulator-min-microvolt = <765000>;
+ regulator-max-microvolt = <1020000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l16b_1p2: ldo16 {
+ regulator-name = "vreg_l16b_1p2";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1300000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l17b_1p8: ldo17 {
+ regulator-name = "vreg_l17b_1p8";
+ regulator-min-microvolt = <1700000>;
+ regulator-max-microvolt = <1900000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l18b_1p8: ldo18 {
+ regulator-name = "vreg_l18b_1p8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ vdd18_io:
+ vreg_l19b_1p8: ldo19 {
+ regulator-name = "vreg_l19b_1p8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+ };
+
+ regulators-1 {
+ compatible = "qcom,pm8350c-rpmh-regulators";
+ qcom,pmic-id = "c";
+
+ vdd-s1-supply = <&vph_pwr>;
+ vdd-s2-supply = <&vph_pwr>;
+ vdd-s5-supply = <&vph_pwr>;
+ vdd-s7-supply = <&vph_pwr>;
+ vdd-s9-supply = <&vph_pwr>;
+ vdd-s10-supply = <&vph_pwr>;
+
+ vdd-l1-l12-supply = <&vreg_s1b_1p856>;
+ vdd-l2-l8-supply = <&vreg_s1b_1p856>;
+ vdd-l3-l4-l5-l7-l13-supply = <&vreg_bob>;
+ vdd-l6-l9-l11-supply = <&vreg_bob>;
+ vdd-l10-supply = <&vreg_s7b_0p952>;
+
+ vdd-bob-supply = <&vph_pwr>;
+
+ /*
+ * S2, S5, S7, S10 are ARCs:
+ * S2 - cx.lvl,
+ * S5 - mss.lvl,
+ * S7 - gfx.lvl,
+ * S10 - mx.lvl.
+ */
+
+ vdd22_wlpa_s1c:
+ vreg_s1c_2p2: smps1 {
+ regulator-name = "vreg_s1c_2p2";
+ regulator-min-microvolt = <2190000>;
+ regulator-max-microvolt = <2210000>;
+ };
+
+ vreg_s9c_0p676: smps9 {
+ regulator-name = "vreg_s9c_0p676";
+ regulator-min-microvolt = <1010000>;
+ regulator-max-microvolt = <1170000>;
+ };
+
+ vdd_a_usbhs_1p8:
+ vdd_qfprom:
+ vreg_l1c_1p8: ldo1 {
+ regulator-name = "vreg_l1c_1p8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1980000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ iovdd_ts:
+ vreg_l2c_1p8: ldo2 {
+ regulator-name = "vreg_l2c_1p8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vdd_ts:
+ vreg_l3c_3p0: ldo3 {
+ regulator-name = "vreg_l3c_3p0";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3304000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l4c_1p8_3p0: ldo4 {
+ regulator-name = "vreg_l4c_1p8_3p0";
+ regulator-min-microvolt = <1620000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l5c_1p8_3p0: ldo5 {
+ regulator-name = "vreg_l5c_1p8_3p0";
+ regulator-min-microvolt = <1620000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l6c_2p96: ldo6 {
+ regulator-name = "vreg_l6c_2p96";
+ regulator-min-microvolt = <1650000>;
+ regulator-max-microvolt = <3544000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l7c_3p0: ldo7 {
+ regulator-name = "vreg_l7c_3p0";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3544000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l8c_1p8: ldo8 {
+ regulator-name = "vreg_l8c_1p8";
+ regulator-min-microvolt = <1620000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l9c_3p3: ldo9 {
+ regulator-name = "vreg_l9c_2p96";
+ regulator-min-microvolt = <3008000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vdd_a_dsi_0_0p9:
+ vdd_a_ufs_0_core:
+ vdd_a_usbhs_core:
+ vreg_l10c_0p88: ldo10 {
+ regulator-name = "vreg_l10c_0p88";
+ regulator-min-microvolt = <720000>;
+ regulator-max-microvolt = <1050000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ regulator-allow-set-load;
+ regulator-allowed-modes = <RPMH_REGULATOR_MODE_LPM
+ RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l11c_2p8: ldo11 {
+ regulator-name = "vreg_l11c_2p8";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <3544000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ disp_iovcc_1p8:
+ vreg_l12c_1p8: ldo12 {
+ regulator-name = "vreg_l12c_1p8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ disp_vci_3p0:
+ vreg_l13c_3p0: ldo13 {
+ regulator-name = "vreg_l13c_3p0";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ audio_sw_vcc:
+ vreg_bob: bob {
+ regulator-name = "vreg_bob";
+ regulator-min-microvolt = <3008000>;
+ regulator-max-microvolt = <3960000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_AUTO>;
+ };
+ };
+};
+
+&gcc {
+ protected-clocks = <GCC_CFG_NOC_LPASS_CLK>,
+ <GCC_MSS_CFG_AHB_CLK>,
+ <GCC_MSS_OFFLINE_AXI_CLK>,
+ <GCC_MSS_Q6SS_BOOT_CLK_SRC>,
+ <GCC_MSS_Q6_MEMNOC_AXI_CLK>,
+ <GCC_MSS_SNOC_AXI_CLK>,
+ <GCC_QSPI_CNOC_PERIPH_AHB_CLK>,
+ <GCC_QSPI_CORE_CLK>,
+ <GCC_QSPI_CORE_CLK_SRC>,
+ <GCC_SEC_CTRL_CLK_SRC>,
+ <GCC_WPSS_AHB_BDG_MST_CLK>,
+ <GCC_WPSS_AHB_CLK>,
+ <GCC_WPSS_RSCP_CLK>;
+};
+
+&gpi_dma0 {
+ status = "okay";
+};
+
+&gpi_dma1 {
+ status = "okay";
+};
+
+&gpu {
+ status = "okay";
+};
+
+&gpu_zap_shader {
+ firmware-name = "qcom/sm7325/motorola/dubai/a660_zap.mbn";
+};
+
+&i2c4 {
+ clock-frequency = <100000>;
+
+ status = "okay";
+
+ typec-mux@42 {
+ compatible = "fcs,fsa4480";
+ reg = <0x42>;
+
+ interrupts-extended = <&tlmm 6 IRQ_TYPE_LEVEL_LOW>;
+
+ vcc-supply = <&audio_sw_vcc>;
+
+ mode-switch;
+ orientation-switch;
+
+ port {
+ fsa4480_sbu_mux: endpoint {
+ remote-endpoint = <&pmic_glink_sbu>;
+ };
+ };
+ };
+};
+
+&ipa {
+ firmware-name = "qcom/sm7325/motorola/dubai/yupik_ipa_fws.mbn";
+ memory-region = <&ipa_fw_mem>;
+
+ qcom,gsi-loader = "self";
+
+ status = "okay";
+};
+
+&pm7250b_adc {
+ channel@4e {
+ reg = <ADC5_AMUX_THM2_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time = <200>;
+ qcom,pre-scaling = <1 1>;
+ label = "chg_skin_therm";
+ };
+
+ channel@4f {
+ reg = <ADC5_AMUX_THM3_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time = <200>;
+ qcom,pre-scaling = <1 1>;
+ label = "conn_therm";
+ };
+};
+
+&pm7250b_adc_tm {
+ status = "okay";
+
+ chg-skin-therm@0 {
+ reg = <0>;
+ io-channels = <&pm7250b_adc ADC5_AMUX_THM2_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time-us = <200>;
+ };
+
+ conn-therm@1 {
+ reg = <1>;
+ io-channels = <&pm7250b_adc ADC5_AMUX_THM3_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time-us = <200>;
+ };
+};
+
+&pm7250b_gpios {
+ gpio-line-names = "FG_ALERT_N", /* GPIO_1 */
+ "SLAVECP_INT",
+ "NC",
+ "NC",
+ "NC",
+ "DOUBLER_STAT",
+ "NC",
+ "NC",
+ "NC",
+ "NC", /* GPIO_10 */
+ "NC",
+ "NC";
+};
+
+&pm7325_gpios {
+ gpio-line-names = "PA_THERM1", /* GPIO_1 */
+ "NC",
+ "NC",
+ "PA_THERM2",
+ "CBL_PWR_N",
+ "KYPD_VOL_UP_N",
+ "NC",
+ "NC",
+ "NC",
+ "NC"; /* GPIO_10 */
+
+ kypd_vol_up_n: kypd-volp-n-state {
+ pins = "gpio6";
+ function = PMIC_GPIO_FUNC_NORMAL;
+ bias-pull-up;
+ input-enable;
+ power-source = <1>;
+ };
+};
+
+&pm8350c_gpios {
+ gpio-line-names = "NC", /* GPIO_1 */
+ "NC",
+ "NC",
+ "NC",
+ "NC",
+ "NC",
+ "NC",
+ "NC",
+ "NC"; /* GPIO_9 */
+};
+
+&pm8350c_flash {
+ status = "okay";
+
+ led-0 {
+ function = LED_FUNCTION_FLASH;
+ color = <LED_COLOR_ID_WHITE>;
+ led-sources = <1>, <4>;
+ led-max-microamp = <500000>;
+ flash-max-microamp = <1500000>;
+ flash-max-timeout-us = <400000>;
+ };
+
+ led-1 {
+ function = LED_FUNCTION_FLASH;
+ color = <LED_COLOR_ID_WHITE>;
+ led-sources = <2>, <3>;
+ led-max-microamp = <500000>;
+ flash-max-microamp = <1500000>;
+ flash-max-timeout-us = <400000>;
+ };
+};
+
+&pmk8350_adc_tm {
+ status = "okay";
+
+ xo-therm@0 {
+ reg = <0>;
+ io-channels = <&pmk8350_vadc PMK8350_ADC7_AMUX_THM1_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time-us = <200>;
+ };
+
+ quiet-therm@1 {
+ reg = <1>;
+ io-channels = <&pmk8350_vadc PM7325_ADC7_AMUX_THM1_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time-us = <200>;
+ };
+
+ cam-flash-therm@2 {
+ reg = <2>;
+ io-channels = <&pmk8350_vadc PM7325_ADC7_AMUX_THM2_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time-us = <200>;
+ };
+
+ sdm-skin-therm@3 {
+ reg = <3>;
+ io-channels = <&pmk8350_vadc PM7325_ADC7_AMUX_THM3_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time-us = <200>;
+ };
+
+ chg-therm@4 {
+ reg = <4>;
+ io-channels = <&pmk8350_vadc PM7325_ADC7_AMUX_THM4_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time-us = <200>;
+ };
+
+ pa-therm-1@5 {
+ reg = <5>;
+ io-channels = <&pmk8350_vadc PM7325_ADC7_GPIO1_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time-us = <200>;
+ };
+
+ pa-therm-2@6 {
+ reg = <6>;
+ io-channels = <&pmk8350_vadc PM7325_ADC7_GPIO4_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time-us = <200>;
+ };
+};
+
+&pmk8350_gpios {
+ gpio-line-names = "NC", /* GPIO_0 */
+ "NC",
+ "TP_PMK_GPIO_3",
+ "PMK_OPTION"; /* GPIO_4 */
+};
+
+&pmk8350_rtc {
+ status = "okay";
+};
+
+&pmk8350_vadc {
+ status = "okay";
+
+ channel@44 {
+ reg = <PMK8350_ADC7_AMUX_THM1_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time = <200>;
+ qcom,pre-scaling = <1 1>;
+ label = "pmk8350_xo_therm";
+ };
+
+ channel@144 {
+ reg = <PM7325_ADC7_AMUX_THM1_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time = <200>;
+ qcom,pre-scaling = <1 1>;
+ label = "pm7325_quiet_therm";
+ };
+
+ channel@145 {
+ reg = <PM7325_ADC7_AMUX_THM2_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time = <200>;
+ qcom,pre-scaling = <1 1>;
+ label = "pm7325_cam_flash_therm";
+ };
+
+ channel@146 {
+ reg = <PM7325_ADC7_AMUX_THM3_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time = <200>;
+ qcom,pre-scaling = <1 1>;
+ label = "pm7325_sdm_skin_therm";
+ };
+
+ channel@147 {
+ reg = <PM7325_ADC7_AMUX_THM4_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time = <200>;
+ qcom,pre-scaling = <1 1>;
+ label = "pm7325_chg_therm";
+ };
+
+ channel@14a {
+ reg = <PM7325_ADC7_GPIO1_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time = <200>;
+ qcom,pre-scaling = <1 1>;
+ label = "pm7325_pa_therm1";
+ };
+
+ channel@14d {
+ reg = <PM7325_ADC7_GPIO4_100K_PU>;
+ qcom,ratiometric;
+ qcom,hw-settle-time = <200>;
+ qcom,pre-scaling = <1 1>;
+ label = "pm7325_pa_therm2";
+ };
+};
+
+&pon_pwrkey {
+ status = "okay";
+};
+
+&pon_resin {
+ linux,code = <KEY_VOLUMEDOWN>;
+
+ status = "okay";
+};
+
+&qfprom {
+ vcc-supply = <&vdd_qfprom>;
+};
+
+&qup_spi13_cs {
+ drive-strength = <6>;
+ bias-pull-down;
+};
+
+&qup_spi13_data_clk {
+ drive-strength = <6>;
+ bias-pull-down;
+};
+
+&qup_uart5_rx {
+ drive-strength = <2>;
+ bias-disable;
+};
+
+&qup_uart5_tx {
+ drive-strength = <2>;
+ bias-disable;
+};
+
+&qup_uart7_cts {
+ /*
+ * Configure a bias-bus-hold on CTS to lower power
+ * usage when Bluetooth is turned off. Bus hold will
+ * maintain a low power state regardless of whether
+ * the Bluetooth module drives the pin in either
+ * direction or leaves the pin fully unpowered.
+ */
+ bias-bus-hold;
+};
+
+&qup_uart7_rts {
+ /* We'll drive RTS, so no pull */
+ drive-strength = <2>;
+ bias-disable;
+};
+
+&qup_uart7_rx {
+ /*
+ * Configure a pull-up on RX. This is needed to avoid
+ * garbage data when the TX pin of the Bluetooth module is
+ * in tri-state (module powered off or not driving the
+ * signal yet).
+ */
+ bias-pull-up;
+};
+
+&qup_uart7_tx {
+ /* We'll drive TX, so no pull */
+ drive-strength = <2>;
+ bias-disable;
+};
+
+&qupv3_id_0 {
+ status = "okay";
+};
+
+&qupv3_id_1 {
+ status = "okay";
+};
+
+&remoteproc_adsp {
+ firmware-name = "qcom/sm7325/motorola/dubai/adsp.mbn";
+
+ status = "okay";
+};
+
+&remoteproc_cdsp {
+ firmware-name = "qcom/sm7325/motorola/dubai/cdsp.mbn";
+
+ status = "okay";
+};
+
+&remoteproc_mpss {
+ firmware-name = "qcom/sm7325/motorola/dubai/modem.mbn";
+
+ status = "okay";
+};
+
+&remoteproc_wpss {
+ firmware-name = "qcom/sm7325/motorola/dubai/wpss.mbn";
+
+ status = "okay";
+};
+
+&rmtfs_mem {
+ qcom,vmid = <QCOM_SCM_VMID_MSS_MSA>,
+ <QCOM_SCM_VMID_NAV>;
+};
+
+&spi13 {
+ status = "okay";
+
+ touchscreen@0 {
+ compatible = "goodix,gt9916";
+ reg = <0>;
+
+ interrupts-extended = <&tlmm 81 IRQ_TYPE_LEVEL_LOW>;
+
+ reset-gpios = <&tlmm 105 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&vdd_ts>;
+ vddio-supply = <&iovdd_ts>;
+
+ spi-max-frequency = <1000000>;
+
+ touchscreen-size-x = <1080>;
+ touchscreen-size-y = <2400>;
+
+ pinctrl-0 = <&ts_int_n>, <&ts_reset_n>;
+ pinctrl-names = "default";
+ };
+};
+
+&tlmm {
+ gpio-line-names = "NC", /* GPIO_0 */
+ "NC",
+ "NC",
+ "NC",
+ "SMARTPA_I2C_SDA",
+ "SMARTPA_I2C_SCK",
+ "AUD_4480_DET",
+ "AUD_4480_INT",
+ "FASTCHARGE_I2C_SDA",
+ "FASTCHARGE_I2C_SCL",
+ "SM_GPSLNA_EN_GPIO10", /* GPIO_10 */
+ "NC",
+ "PERI_I2C_SDA",
+ "PERI_I2C_SCL",
+ "NC",
+ "NC",
+ "APPS_I2C_SDA",
+ "APPS_I2C_SCL",
+ "NC",
+ "DISP_LDO_EN",
+ "CAM_FW_RST_N", /* GPIO_20 */
+ "CAM_RM_RST_N",
+ "DBG_UART_TXD",
+ "DBG_UART_RXD",
+ "TOPSB_INT_N",
+ "MCAM_LDO_EN",
+ "HOST2WLAN_SOL",
+ "WLAN2HOST_SOL",
+ "UART_BT_RTS_AP_CTS",
+ "UART_BT_CTS_AP_RTS",
+ "UART_BT_RX_AP_TX", /* GPIO_30 */
+ "UART_BT_TX_AP_RX",
+ "FRONT_60M_INT",
+ "FRONT_60M_RST",
+ "FP_INT",
+ "FP_RST",
+ "NFC_I2C_SDA",
+ "NFC_I2C_SCL",
+ "NFC_EN",
+ "NFC_CLK_REQ",
+ "NFC_SE_SPI_NREST", /* GPIO_40 */
+ "NFC_IRQ",
+ "RF_LDO_GPIO42_EN",
+ "GOOGLE_KEY_INT", /* no such key exists */
+ "DISP_RST_N",
+ "SB_INT_N",
+ "HAP_RST_N",
+ "HAP_INT",
+ "ESE_SPI_MISO",
+ "ESE_SPI_MOSI",
+ "ESE_SPI_CLK", /* GPIO_50 */
+ "ESE_SPI_CS",
+ "TP_SPI_MISO",
+ "TP_SPI_MOSI",
+ "TP_SPI_CLK",
+ "TP_SPI_CS_N",
+ "FP_SPI_MISO",
+ "FP_SPI_MOSI",
+ "FP_SPI_CLK",
+ "FP_SPI_CS_N",
+ "HW_ID_1", /* GPIO_60 */
+ "HW_ID_2",
+ "CAM_PMU_EN",
+ "WIDE_DVDD_LDO_EN",
+ "CAM_MCLK0",
+ "CAM_MCLK1",
+ "CAM_MCLK2",
+ "CAM_MCLK3",
+ "NC",
+ "CCI_I2C_SDA0",
+ "CCI_I2C_SCL0", /* GPIO_70 */
+ "CCI_I2C_SDA1",
+ "CCI_I2C_SCL1",
+ "CCI_I2C_SDA2",
+ "CCI_I2C_SCL2",
+ "CCI_I2C_SDA3",
+ "CCI_I2C_SCL3",
+ "CAM_RT_RST_N",
+ "CAM_RU_RST_N",
+ "FCAM_LDO_EN",
+ "DISP_TE", /* GPIO_80 */
+ "TP_INT_N",
+ "FORCED_USB_BOOT",
+ "SM_CDC_RST_N",
+ "WLAN_EN",
+ "BT_EN",
+ "WCN_SW_CTRL",
+ "PCIE0_RESET_N",
+ "PCIE0_CLK_REQ_N",
+ "PCIE0_WAKE_N",
+ "MOS_AS_EN", /* GPIO_90 */
+ "NC",
+ "",
+ "NC",
+ "BT_FM_SLIMBUS_CLK",
+ "BT_FM_SLIMBUS_DATA",
+ "NC",
+ "RFCONN_DET_1",
+ "RF_CON_DET_2",
+ "RF_CON_DET_3",
+ "NC", /* GPIO_100 */
+ "NC",
+ "NC",
+ "ACCEL_INT",
+ "NC",
+ "TP_RST_N",
+ "NC",
+ "NC",
+ "NC",
+ "UIM2_DATA",
+ "UIM2_CLK", /* GPIO_110 */
+ "UIM2_RESET",
+ "UIM2_PRESENT",
+ "UIM1_DATA",
+ "UIM1_CLK",
+ "UIM1_RESET",
+ "UIM1_PRESENT",
+ "SM_RFFE0_CLK",
+ "SM_RFFE0_DATA",
+ "SM_RFFE1_CLK",
+ "SM_RFFE1_DATA", /* GPIO_120 */
+ "PA_MUTING",
+ "SM_GRFC5",
+ "LAA_RX",
+ "SM_GRFC7",
+ "SM_RFFE4_CLK",
+ "SM_RFFE4_DATA",
+ "WLAN_COEX_UART_RX",
+ "WLAN_COEX_UART_TX",
+ "NC",
+ "NC", /* GPIO_130 */
+ "SM_GRFC12",
+ "NC",
+ "QLINK0_REQUEST",
+ "QLINK0_ENABLE",
+ "QLINK0_WMSS_RESET_N",
+ "NC",
+ "NC",
+ "NC",
+ "NC",
+ "USB_CC_DIR", /* GPIO_140 */
+ "SAR_INT_N",
+ "PROX_INT_N",
+ "",
+ "SM_SWR_TX_CLK",
+ "SM_SWR_TX_DATA0",
+ "SM_SWR_TX_DATA1",
+ "SM_SWR_RX_CLK",
+ "SM_SWR_RX_DATA0",
+ "SM_SWR_RX_DATA1",
+ "NC", /* GPIO_150 */
+ "NC",
+ "NC",
+ "NC",
+ "SB_MI2S_SCK",
+ "SB_MI2S_WS",
+ "SB_MI2S_RXDAT_AP_TX",
+ "SB_MI2S_TXDAT_AP_RX",
+ "NC",
+ "SNS_I3C0_SDA",
+ "SNS_I3C0_SCL", /* GPIO_160 */
+ "SSC_I2C4_SDA",
+ "SSC_I2C4_SCL",
+ "MAG_I2C_SDA",
+ "MAG_I2C_SCL",
+ "NC",
+ "NC",
+ "",
+ "",
+ "",
+ "", /* GPIO_170 */
+ "SSC_BT_UART4_TX",
+ "SSC_BT_UART4_RX",
+ "NC",
+ "NC";
+ gpio-reserved-ranges = <48 4>, /* SPI (eSE - embedded Secure Element) */
+ <56 4>; /* SPI (fingerprint reader) */
+
+ qup_uart7_sleep_cts: qup-uart7-sleep-cts-state {
+ pins = "gpio28";
+ function = "gpio";
+ /*
+ * Configure a bias-bus-hold on CTS to lower power
+ * usage when Bluetooth is turned off. Bus hold will
+ * maintain a low power state regardless of whether
+ * the Bluetooth module drives the pin in either
+ * direction or leaves the pin fully unpowered.
+ */
+ bias-bus-hold;
+ };
+
+ qup_uart7_sleep_rts: qup-uart7-sleep-rts-state {
+ pins = "gpio29";
+ function = "gpio";
+ /*
+ * Configure pull-down on RTS. As RTS is active low
+ * signal, pull it low to indicate the BT SoC that it
+ * can wakeup the system anytime from suspend state by
+ * pulling RX low (by sending wakeup bytes).
+ */
+ bias-pull-down;
+ };
+
+ qup_uart7_sleep_tx: qup-uart7-sleep-tx-state {
+ pins = "gpio30";
+ function = "gpio";
+ /*
+ * Configure pull-up on TX when it isn't actively driven
+ * to prevent BT SoC from receiving garbage during sleep.
+ */
+ bias-pull-up;
+ };
+
+ qup_uart7_sleep_rx: qup-uart7-sleep-rx-state {
+ pins = "gpio31";
+ function = "gpio";
+ /*
+ * Configure a pull-up on RX. This is needed to avoid
+ * garbage data when the TX pin of the Bluetooth module
+ * is floating which may cause spurious wakeups.
+ */
+ bias-pull-up;
+ };
+
+ oled_reset_n: oled-reset-n-state {
+ pins = "gpio44";
+ function = "gpio";
+ drive-strength = <8>;
+ bias-disable;
+ };
+
+ aw86224_reset_default: aw86224-reset-default-state {
+ pins = "gpio46";
+ function = "gpio";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+
+ aw86224_int_default: aw86224-int-default-state {
+ pins = "gpio47";
+ function = "gpio";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+
+ mdp_vsync_p: mdp-vsync-p-state {
+ pins = "gpio80";
+ function = "mdp_vsync";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+
+ ts_int_n: ts-int-n-state {
+ pins = "gpio81";
+ function = "gpio";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+
+ bt_en: bt-en-state {
+ pins = "gpio85";
+ function = "gpio";
+ output-low;
+ bias-disable;
+ };
+
+ ts_reset_n: ts-int-n-state {
+ pins = "gpio105";
+ function = "gpio";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+};
+
+&uart5 {
+ status = "okay";
+};
+
+&uart7 {
+ /delete-property/ interrupts;
+ interrupts-extended = <&intc GIC_SPI 608 IRQ_TYPE_LEVEL_HIGH>,
+ <&tlmm 31 IRQ_TYPE_EDGE_FALLING>;
+ pinctrl-1 = <&qup_uart7_sleep_cts>,
+ <&qup_uart7_sleep_rts>,
+ <&qup_uart7_sleep_tx>,
+ <&qup_uart7_sleep_rx>;
+ pinctrl-names = "default",
+ "sleep";
+
+ status = "okay";
+
+ bluetooth: bluetooth {
+ compatible = "qcom,wcn6750-bt";
+
+ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>;
+ vddaon-supply = <&vreg_pmu_aon_0p59>;
+ vddbtcmx-supply = <&vreg_pmu_btcmx_0p85>;
+ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>;
+ vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>;
+ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>;
+
+ max-speed = <3200000>;
+
+ qcom,local-bd-address-broken;
+ };
+};
+
+&ufs_mem_hc {
+ reset-gpios = <&tlmm 175 GPIO_ACTIVE_LOW>;
+
+ vcc-supply = <&vreg_l7b_2p96>;
+ vcc-max-microamp = <800000>;
+
+ vccq-supply = <&vreg_l9b_1p2>;
+ vccq-max-microamp = <900000>;
+
+ status = "okay";
+};
+
+&ufs_mem_phy {
+ vdda-phy-supply = <&vdd_a_ufs_0_core>;
+ vdda-pll-supply = <&vdd_a_ufs_0_1p2>;
+
+ status = "okay";
+};
+
+&usb_1 {
+ /* USB 2.0 only */
+ qcom,select-utmi-as-pipe-clk;
+ maximum-speed = "high-speed";
+
+ /* Remove USB3 phy */
+ phys = <&usb_1_hsphy>;
+ phy-names = "usb2-phy";
+
+ status = "okay";
+};
+
+&usb_1_dwc3_hs {
+ remote-endpoint = <&pmic_glink_hs_in>;
+};
+
+&usb_1_hsphy {
+ vdda-pll-supply = <&vdd_a_usbhs_core>;
+ vdda18-supply = <&vdd_a_usbhs_1p8>;
+ vdda33-supply = <&vdd_a_usbhs_3p1>;
+
+ status = "okay";
+};
+
+&venus {
+ firmware-name = "qcom/sm7325/motorola/dubai/vpu20_1v.mbn";
+
+ status = "okay";
+};
+
+&wifi {
+ qcom,calibration-variant = "Motorola_dubai";
+
+ status = "okay";
+};
--
2.53.0
^ permalink raw reply related
* [PATCH v2 1/2] dt-bindings: arm: qcom: Add SM7325 Motorola Edge 30 (dubai)
From: Val Packett @ 2026-04-03 5:33 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Val Packett, ~postmarketos/upstreaming, phone-devel,
Krzysztof Kozlowski, linux-arm-msm, devicetree, linux-kernel
Motorola Edge 30 (motorola,dubai) is a smartphone based on the
SM7325 SoC.
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Val Packett <val@packett.cool>
---
v2: Pull Acked-by
v1: https://lore.kernel.org/all/20260329103055.96649-1-val@packett.cool/
---
Documentation/devicetree/bindings/arm/qcom.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/arm/qcom.yaml b/Documentation/devicetree/bindings/arm/qcom.yaml
index ca880c105f3b..58f1621bb855 100644
--- a/Documentation/devicetree/bindings/arm/qcom.yaml
+++ b/Documentation/devicetree/bindings/arm/qcom.yaml
@@ -1038,6 +1038,7 @@ properties:
- items:
- enum:
+ - motorola,dubai
- nothing,spacewar
- const: qcom,sm7325
--
2.53.0
^ permalink raw reply related
* [PATCH v3 0/4] ASoC: Add TAS67524 quad-channel Class-D amplifier driver
From: Sen Wang @ 2026-04-03 5:06 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
Changes in v3:
- Renamed ALL tas675x filenames to tas67524, removed tas6754 compatible instance
- Changed pd-gpios to powerdown-gpios, cleanup unnessary .yaml formatting (1/4)
- Opt to use disable delayed_work and re-enable on runtime suspend/resume,
similarly, use disable/enable IRQ on system suspend/resume. (2/4)
- Include IRQ_NONE on ISR returns. (2/4)
- Clarify _check_faults() function which now returns need_clear boolean (2/4)
- Added register section (3/4)
- Added addintional notes: for clarification (3/4)
- Links to v2: https://lore.kernel.org/all/20260401223239.1638881-1-sen@ti.com/
Changes in v2:
- Remove redundant DAPM event function (2/4)
- Move IRQ request past power_on, so regs can be set in a clean state (2/4)
- Add delayed_work at probe time to accomdate no PM configs (2/4)
- Change .set_fmt and .dapm_routes callbacks to the same tas675x_set_fmt name (2/4)
- Links to v1: https://lore.kernel.org/all/20260401024210.28542-1-sen@ti.com/
Sen Wang (4):
ASoC: dt-bindings: Add ti,tas67524
ASoC: codecs: Add TAS67524 quad-channel audio amplifier driver
Documentation: sound: Add TAS675x codec mixer controls documentation
MAINTAINERS: add entry for TAS67524 audio amplifier
.../bindings/sound/ti,tas67524.yaml | 277 +++
Documentation/sound/codecs/index.rst | 1 +
Documentation/sound/codecs/tas67524.rst | 686 ++++++
MAINTAINERS | 4 +
sound/soc/codecs/Kconfig | 11 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tas67524.c | 2173 +++++++++++++++++
sound/soc/codecs/tas67524.h | 367 +++
8 files changed, 3521 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/ti,tas67524.yaml
create mode 100644 Documentation/sound/codecs/tas67524.rst
create mode 100644 sound/soc/codecs/tas67524.c
create mode 100644 sound/soc/codecs/tas67524.h
--
2.43.0
^ permalink raw reply
* [PATCH v3 3/4] Documentation: sound: Add TAS675x codec mixer controls documentation
From: Sen Wang @ 2026-04-03 5:06 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: <20260403050627.635591-1-sen@ti.com>
Add ALSA mixer controls documentation for the TAS67524 driver, covering
DSP signal path modes, volume controls, load diagnostics, and fault
monitoring.
Signed-off-by: Sen Wang <sen@ti.com>
---
Changes in v3:
- Added register section
- Added few addintional notes for clarification
Changes in v2:
- None
Documentation/sound/codecs/index.rst | 1 +
Documentation/sound/codecs/tas67524.rst | 686 ++++++++++++++++++++++++
2 files changed, 687 insertions(+)
create mode 100644 Documentation/sound/codecs/tas67524.rst
diff --git a/Documentation/sound/codecs/index.rst b/Documentation/sound/codecs/index.rst
index 2cb95d87bbef..f7e842c68407 100644
--- a/Documentation/sound/codecs/index.rst
+++ b/Documentation/sound/codecs/index.rst
@@ -7,3 +7,4 @@ Codec-Specific Information
:maxdepth: 2
cs35l56
+ tas67524
diff --git a/Documentation/sound/codecs/tas67524.rst b/Documentation/sound/codecs/tas67524.rst
new file mode 100644
index 000000000000..3b99c31593cf
--- /dev/null
+++ b/Documentation/sound/codecs/tas67524.rst
@@ -0,0 +1,686 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================
+TAS675x Codec Mixer Controls
+======================================
+
+This document describes the ALSA mixer controls for the TAS675x
+4-channel amplifier driver.
+
+For device tree bindings, see:
+Documentation/devicetree/bindings/sound/ti,tas67524.yaml
+
+DSP Signal Path Mode
+====================
+
+DSP Signal Path Mode
+--------------------
+
+:Description: Signal processing mode selection.
+:Type: Enumerated
+:Default: Normal
+:Options: Normal, LLP, FFLP
+:Register: 0x32 bits [1:0]
+
+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)
+:Register: 0x4A (CH1/CH2), 0x4B (CH3/CH4)
+
+Analog Gain Ramp Step
+---------------------
+
+:Description: Anti-pop ramp step duration for analog gain transitions.
+:Type: Enumerated
+:Default: 15us
+:Options: 15us, 60us, 200us, 400us
+:Register: 0x4E bits [3:2]
+
+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)
+:Register: 0x40 (CH1), 0x41 (CH2), 0x42 (CH3), 0x43 (CH4)
+
+Volume Ramp Down Rate
+---------------------
+
+:Description: Update frequency during mute transition.
+:Type: Enumerated
+:Default: 16 FS
+:Options: 4 FS, 16 FS, 32 FS, Instant
+:Register: 0x44 bits [7:6]
+
+Volume Ramp Down Step
+---------------------
+
+:Description: dB change per update during mute.
+:Type: Enumerated
+:Default: 0.5dB
+:Options: 4dB, 2dB, 1dB, 0.5dB
+:Register: 0x44 bits [5:4]
+
+Volume Ramp Up Rate
+-------------------
+
+:Description: Update frequency during unmute transition.
+:Type: Enumerated
+:Default: 16 FS
+:Options: 4 FS, 16 FS, 32 FS, Instant
+:Register: 0x44 bits [3:2]
+
+Volume Ramp Up Step
+-------------------
+
+:Description: dB change per update during unmute.
+:Type: Enumerated
+:Default: 0.5dB
+:Options: 4dB, 2dB, 1dB, 0.5dB
+:Register: 0x44 bits [1:0]
+
+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
+:Register: 0x46 bits [1:0]
+
+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
+:Register: 0x46 bits [3:2]
+
+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)
+:Register: 0x47 bit [0] (CH1), bit [1] (CH2), bit [2] (CH3), bit [3] (CH4)
+
+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.
+:Register: 0x47 bit [4]
+
+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
+:Register: 0x48 bits [7:4] (CH1), bits [3:0] (CH2),
+ 0x49 bits [7:4] (CH3), bits [3:0] (CH4)
+: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
+:Register: 0x61 bits [1:0]
+
+SS Triangle Range
+-----------------
+
+:Description: Frequency deviation range for Triangle spread spectrum.
+:Type: Enumerated
+:Default: 6.5%
+:Options: 6.5%, 13.5%, 5%, 10%
+:Register: 0x62 bits [1:0]
+: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%
+:Register: 0x62 bits [6:4]
+: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
+:Register: 0x62 bits [3:2]
+:Note: Applies only when Spread Spectrum Mode includes Random.
+
+SS Triangle Dwell Min
+---------------------
+
+:Description: Minimum dwell time at Triangle spread spectrum frequency extremes.
+:Type: Integer
+:Default: 0
+:Range: 0 to 15 (0 = feature disabled)
+:Register: 0x66 bits [7:4]
+:Note: Counts in FSS clock cycles. The modulator holds the extreme
+ frequency for at least this many FSS cycles before reversing.
+ When Dwell Min equals Dwell Max, the dwell feature is inactive.
+ For FSS values at each PWM frequency refer to the "Spread
+ Spectrum" section of the TRM.
+
+SS Triangle Dwell Max
+---------------------
+
+:Description: Maximum dwell time at Triangle spread spectrum frequency extremes.
+:Type: Integer
+:Default: 0
+:Range: 0 to 15 (0 = feature disabled)
+:Register: 0x66 bits [3:0]
+:Note: Counts in FSS clock cycles. Must be >= Dwell Min. When Dwell Max
+ equals Dwell Min, the dwell feature is inactive.
+
+Hardware Protection
+===================
+
+OTSD Auto Recovery Switch
+--------------------------
+
+:Description: Enables automatic recovery from over-temperature shutdown.
+:Type: Boolean Switch
+:Default: Disabled (0)
+:Register: 0x8F bit [1]
+: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
+:Options: Level 4, Level 3, Level 2, Level 1
+:Register: 0x55 bits [1:0]
+:Note: Level 4 is the least sensitive (highest trip current); Level 1 is
+ the most sensitive. The exact ILIM values depend on operating
+ conditions (PVDD voltage, switching frequency, and temperature).
+ Refer to the Electrical Characteristics table and the
+ "Overcurrent Limit (Cycle-By-Cycle)" section of the TRM.
+
+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
+:Register: 0xE2 bits [6:4] (CH1), bits [2:0] (CH2),
+ 0xE3 bits [6:4] (CH3), bits [2:0] (CH4)
+
+Temperature and Voltage Monitoring
+===================================
+
+PVDD Sense
+----------
+
+:Description: Supply voltage sense register.
+:Type: Integer (read-only)
+:Range: 0 to 255
+:Conversion: value × 0.19 V
+:Register: 0x74
+
+Global Temperature
+------------------
+
+:Description: Global die temperature sense register.
+:Type: Integer (read-only)
+:Range: 0 to 255
+:Conversion: (value × 0.5 °C) − 50 °C
+:Register: 0x75
+
+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
+:Register: 0xBB bits [7:6] (CH1), bits [5:4] (CH2),
+ 0xBC bits [3:2] (CH3), bits [1:0] (CH4)
+
+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 initialization 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.
+
+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)
+:Register: 0xB0 bit [0]
+:Note: Active-low, 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)
+:Register: 0xB1 bit [3] (CH1), bit [2] (CH2), bit [1] (CH3), bit [0] (CH4)
+: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
+:Register: 0xB2 bits [7:6]
+
+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
+:Register: 0xB2 bits [5:4]
+
+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
+:Register: 0xB2 bits [3:2]
+
+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
+:Register: 0xB2 bits [1:0]
+
+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
+:Register: 0xB3 bits [7:4] (CH1), bits [3:0] (CH2),
+ 0xB4 bits [7:4] (CH3), bits [3:0] (CH4)
+
+DC LDG Result
+~~~~~~~~~~~~~
+
+:Description: Overall DC diagnostic result register.
+:Type: Integer (read-only)
+:Range: 0x00 to 0xFF
+:Register: 0xC2
+: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
+:Register: 0xC0 bits [7:4] (CH1), bits [3:0] (CH2),
+ 0xC1 bits [7:4] (CH3), bits [3:0] (CH4)
+: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
+:Register: 0xC2 bit [7] (CH1), bit [6] (CH2), bit [5] (CH3), bit [4] (CH4)
+
+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
+:Register: 0xD9 bits [7:6]/[5:4]/[3:2]/[1:0] (MSB for CH1–CH4),
+ 0xDA (CH1 LSB), 0xDB (CH2 LSB), 0xDC (CH3 LSB), 0xDD (CH4 LSB)
+
+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 LDG 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)
+:Register: 0xB5 bit [4]
+: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
+:Register: 0xB8
+
+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)
+:Register: 0xC3 (CH1 Real), 0xC4 (CH1 Imag), 0xC5 (CH2 Real), 0xC6 (CH2 Imag),
+ 0xC7 (CH3 Real), 0xC8 (CH3 Imag), 0xC9 (CH4 Real), 0xCA (CH4 Imag)
+:Note: Scale set by AC LDG Gain.
+
+Speaker Protection & Detection
+-------------------------------
+
+Tweeter Detection Switch
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Enables tweeter detection using the AC impedance magnitude comparator.
+:Type: Boolean Switch
+:Default: Enabled (1)
+:Register: 0xB6 bit [0]
+:Note: The underlying register bit is TWEETER DETECT DISABLE (active-low).
+ Control value 1 = detection enabled (register bit 0), 0 = disabled
+ (register bit 1).
+
+Tweeter Detect Threshold
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Description: Magnitude threshold for tweeter detection.
+:Type: Integer
+:Default: 0
+:Range: 0x00 to 0xFF
+:Resolution: 0.8 ohm/code (AC LDG Gain=0) or 0.1 ohm/code (AC LDG Gain=1)
+:Register: 0xB7
+
+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
+:Register: 0xCB bit [3] (CH1), bit [2] (CH2), bit [1] (CH3), bit [0] (CH4)
+
+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)
+:Register: 0x3A bit [0]
+
+PVDD Foldback Switch
+--------------------
+
+:Description: Enables automatic gain limiting when supply voltage drops
+ (Automatic Gain Limiter).
+:Type: Boolean Switch
+:Default: Disabled (0)
+:Register: 0x3A bit [4]
+
+DC Blocker Bypass Switch
+------------------------
+
+:Description: Bypasses the DC-blocking high-pass filter.
+:Type: Boolean Switch
+:Default: Not bypassed (0)
+:Register: 0x39 bit [0]
+
+Clip Detect Switch
+------------------
+
+:Description: Enables DSP-based clip detection (Pseudo-Analog Clip Detect).
+:Type: Boolean Switch
+:Default: Disabled (0)
+:Register: 0x93 bit [6]
+
+Audio SDOUT Switch
+------------------
+
+:Description: Routes post-processed audio to the SDOUT pin instead of
+ Vpredict data.
+:Type: Boolean Switch
+:Default: Disabled (0)
+:Register: 0x3A bit [5]
+:Note: When enabled, replaces Vpredict data on SDOUT with post-processed
+ SDIN. All SDOUT configurations that apply to Vpredict also apply
+ to SDIN-to-SDOUT transmission.
+
+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)
+:Register: 0x37 bit [3] (CH1), bit [2] (CH2), bit [1] (CH3), bit [0] (CH4)
+
+RTLDG Clip Mask Switch
+----------------------
+
+:Description: Suppresses impedance updates during clipping events.
+:Type: Boolean Switch
+:Default: Enabled (1)
+:Register: 0x37 bit [4]
+
+ISENSE Calibration Switch
+--------------------------
+
+:Description: Enables current sense calibration for accurate impedance
+ measurements.
+:Type: Boolean Switch
+:Default: Disabled (0)
+:Register: 0x5B bit [3]
+
+RTLDG Open Load Threshold
+--------------------------
+
+:Description: DSP coefficient for open load fault detection threshold.
+:Type: DSP coefficient (extended control)
+:Register: DSP Book 0x8C, page 0x22, 0x98
+
+RTLDG Short Load Threshold
+---------------------------
+
+:Description: DSP coefficient for shorted load fault detection threshold.
+:Type: DSP coefficient (extended control)
+:Register: DSP Book 0x8C, page 0x22, 0x9C
+
+CHx RTLDG Impedance
+-------------------
+
+:Description: Real-time load impedance per channel (x = 1, 2, 3, 4).
+:Type: Float (read-only, displayed in ohms)
+:Register: 0xD1–0xD2 (CH1), 0xD3–0xD4 (CH2), 0xD5–0xD6 (CH3), 0xD7–0xD8 (CH4)
+: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
+:Register: 0x8B
+:Note: See bit encoding table at the start of this section.
+ Reading the register clears all latched bits.
+
+Driver 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,tas67524.yaml
+- ALSA Control Name Conventions: Documentation/sound/designs/control-names.rst
--
2.43.0
^ permalink raw reply related
* [PATCH v3 4/4] MAINTAINERS: add entry for TAS67524 audio amplifier
From: Sen Wang @ 2026-04-03 5:06 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: <20260403050627.635591-1-sen@ti.com>
Add Sen Wang as maintainer and register file patterns for the newly
added TAS67524 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..a3bf005e38b1 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,tas67524.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/tas67524*
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/tas67524*.*
F: sound/soc/codecs/tlv320*.*
F: sound/soc/codecs/tpa6130a2.*
--
2.43.0
^ permalink raw reply related
* [PATCH v3 2/4] ASoC: codecs: Add TAS67524 quad-channel audio amplifier driver
From: Sen Wang @ 2026-04-03 5:06 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: <20260403050627.635591-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>
---
Changes in v3:
- Use disable delayed_work and re-enable on runtime suspend/resume
- Similarly, use disable/enable IRQ on system suspend/resume
- Include IRQ_NONE on ISR returns
- Clarify _check_faults() which now returns need_clear boolean
Changes in v2:
- Remove redundant DAPM event function
- Move IRQ request past power_on(2/4)
- Add delayed_work at probe time to accomdate no PM config
- Change .set_fmt and .dapm_routes callbacks to the same tas675x_set_fmt name
sound/soc/codecs/Kconfig | 11 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tas67524.c | 2173 +++++++++++++++++++++++++++++++++++
sound/soc/codecs/tas67524.h | 367 ++++++
4 files changed, 2553 insertions(+)
create mode 100644 sound/soc/codecs/tas67524.c
create mode 100644 sound/soc/codecs/tas67524.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index f9e6a83e55c6..167abe626ecb 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_TAS67524
imply SND_SOC_TDA7419
imply SND_SOC_TFA9879
imply SND_SOC_TFA989X
@@ -2240,6 +2241,16 @@ config SND_SOC_TAS6424
Enable support for Texas Instruments TAS6424 high-efficiency
digital input quad-channel Class-D audio power amplifiers.
+config SND_SOC_TAS67524
+ tristate "Texas Instruments TAS67524 Quad-Channel Audio Amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for Texas Instruments TAS67524 quad-channel Class-D
+ audio power amplifier. The device supports I2S and TDM interfaces
+ with real-time voltage and current sense feedback via SDOUT, and
+ provides 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..04769532afa4 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-tas67524-y := tas67524.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_TAS67524) += snd-soc-tas67524.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/tas67524.c b/sound/soc/codecs/tas67524.c
new file mode 100644
index 000000000000..2a1b4a7daaa2
--- /dev/null
+++ b/sound/soc/codecs/tas67524.c
@@ -0,0 +1,2173 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Texas Instruments TAS67524 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 "tas67524.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 9
+ 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 LDG 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 const struct snd_soc_dapm_widget tas675x_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Analog Core", SND_SOC_NOPM, 0, 0, NULL, 0),
+ 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_dapm_routes[] = {
+ { "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_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 SLEEP 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_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);
+ }
+
+ /*
+ * Configure fault and warning event routing:
+ *
+ * ROUTING_1: CP fault/UVLO latch, OUTM soft short latch
+ * ROUTING_2: CBC latch, OTSD latch, OTSD, power fault
+ * ROUTING_3: CBC latch, OTSD latch, power latch, DC LDG,
+ * OTSD, power warnings
+ * ROUTING_4: OC latch, DC latch, protection shutdown
+ * OTW latch, OTW, clip latch
+ * ROUTING_5: clock latch+non-latch, RTLDG latch
+ * CBC warning, clip warning
+ */
+ regmap_write(regmap, TAS675X_REPORT_ROUTING_1_REG, 0x70);
+ regmap_write(regmap, TAS675X_REPORT_ROUTING_2_REG, 0xA3);
+ regmap_write(regmap, TAS675X_REPORT_ROUTING_3_REG, 0xBB);
+ 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_STATUS_1_REG, &val);
+ 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);
+
+ disable_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) {
+ enable_delayed_work(&tas->fault_check_work);
+ 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;
+
+ if (to_i2c_client(dev)->irq)
+ disable_irq(to_i2c_client(dev)->irq);
+
+ 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;
+
+ if (to_i2c_client(dev)->irq)
+ enable_irq(to_i2c_client(dev)->irq);
+
+ 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_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tas675x_dapm_routes),
+ .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_STATUS_1_REG, TAS675X_FAULT_TRACK,
+ "CP / OUTM Fault" },
+ { 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 fault register needs to be cleared.
+ */
+static bool tas675x_check_faults(struct tas675x_priv *tas)
+{
+ struct device *dev = tas->dev;
+ bool needs_clear = 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 needs_clear;
+ }
+ continue;
+ }
+
+ if (reg)
+ needs_clear = true;
+
+ /* Skip logging stream-dependent events when no stream is active */
+ if ((f->flags & TAS675X_FAULT_ACTIVE) &&
+ !READ_ONCE(tas->active_playback_dais) &&
+ !READ_ONCE(tas->active_capture_dais))
+ continue;
+
+ /* 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 needs_clear;
+}
+
+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;
+
+ if (!tas675x_check_faults(tas))
+ return IRQ_NONE;
+
+ 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;
+
+ memcpy(tas->dsp_params, tas675x_dsp_defaults, sizeof(tas->dsp_params));
+ INIT_DELAYED_WORK(&tas->fault_check_work, tas675x_fault_check_work);
+
+ tas->regmap = devm_regmap_init_i2c(client, &cfg);
+ if (IS_ERR(tas->regmap))
+ return PTR_ERR(tas->regmap);
+
+ /* 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, "powerdown", GPIOD_OUT_HIGH);
+ if (IS_ERR(tas->pd_gpio))
+ return dev_err_probe(tas->dev, PTR_ERR(tas->pd_gpio), "Failed powerdown-gpios\n");
+
+ tas->stby_gpio = devm_gpiod_get_optional(tas->dev, "standby", GPIOD_OUT_HIGH);
+ if (IS_ERR(tas->stby_gpio))
+ return dev_err_probe(tas->dev, PTR_ERR(tas->stby_gpio), "Failed standby-gpios\n");
+
+ if (!tas->pd_gpio && !tas->stby_gpio)
+ return dev_err_probe(tas->dev, -EINVAL,
+ "At least one of powerdown-gpios or standby-gpios is required\n");
+
+ ret = tas675x_power_on(tas);
+ if (ret)
+ return ret;
+
+ 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) {
+ tas675x_power_off(tas);
+ return dev_err_probe(tas->dev, ret, "Failed to request IRQ\n");
+ }
+ } else {
+ /* Schedule delayed work for fault checking at probe and runtime resume */
+ schedule_delayed_work(&tas->fault_check_work,
+ msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS));
+ }
+
+ /* 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,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 = "tas67524",
+ .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 TAS67524 Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas67524.h b/sound/soc/codecs/tas67524.h
new file mode 100644
index 000000000000..95330064b686
--- /dev/null
+++ b/sound/soc/codecs/tas67524.h
@@ -0,0 +1,367 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ALSA SoC Texas Instruments TAS67524 Quad-Channel Audio Amplifier
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Sen Wang <sen@ti.com>
+ */
+
+#ifndef __TAS67524_H__
+#define __TAS67524_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 v3 1/4] ASoC: dt-bindings: Add ti,tas67524
From: Sen Wang @ 2026-04-03 5:06 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: <20260403050627.635591-1-sen@ti.com>
Add device tree binding for the Texas Instruments TAS67524 family
of four-channel Class-D audio amplifiers with integrated DSP.
Signed-off-by: Sen Wang <sen@ti.com>
---
Changes in v3:
- Renamed ti,tas675x to ti,tas67524.yaml
- Removed tas6754 compatible instance
- Changed pd-gpios to powerdown-gpios
- Cleanup unnessary "|" formatting
Changes in v2:
- None
.../bindings/sound/ti,tas67524.yaml | 277 ++++++++++++++++++
1 file changed, 277 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/ti,tas67524.yaml
diff --git a/Documentation/devicetree/bindings/sound/ti,tas67524.yaml b/Documentation/devicetree/bindings/sound/ti,tas67524.yaml
new file mode 100644
index 000000000000..b8da1360e698
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,tas67524.yaml
@@ -0,0 +1,277 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,tas67524.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TAS67524 Audio Amplifier
+
+maintainers:
+ - Sen Wang <sen@ti.com>
+
+description:
+ The TAS67524 is a four-channel, digital-input, automotive
+ Class-D audio amplifier with load diagnostics and an integrated
+ DSP for audio processing.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - 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.
+
+ powerdown-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the 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.
+
+ standby-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the 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: [powerdown-gpios]
+ - required: [standby-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";
+
+ standby-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
* Re: [PATCH v11 0/4] crypto: spacc - Add SPAcc Crypto Driver
From: Tony He @ 2026-04-03 4:10 UTC (permalink / raw)
To: Pavitrakumar Managutte
Cc: linux-crypto, linux-kernel, devicetree, herbert, robh, conor+dt,
Ruud.Derwig, manjunath.hadli, adityak, navami.telsang, bhoomikak
In-Reply-To: <20260318071808.817074-1-pavitrakumarm@vayavyalabs.com>
Hi,
I have a question regarding the capabilities of the Synopsys
SPAcc (Security Protocol Accelerator) in this driver series.
From the documentation, SPAcc appears to be more than a generic
crypto engine and is described as supporting protocol-level
acceleration (e.g., IPsec, TLS). I would like to clarify the
exact scope of its offload capabilities, specifically for IPsec
ESP processing.
Is SPAcc capable of handling full ESP packet processing (i.e.,
beyond basic encryption/decryption and authentication)?
For example:
Can it parse ESP packets and perform decapsulation
(removing ESP headers/trailers and returning the inner
payload)?
Does it handle sequence number processing and/or
anti-replay checks?
Or is it limited to crypto operations where the
driver/software stack (e.g., Linux XFRM) must handle all
protocol-level processing such as SA lookup, replay
protection, and packet decapsulation?
In other words, should SPAcc be treated as:
a protocol-aware data-path accelerator for ESP
(with partial packet processing), or
a pure crypto offload engine where all IPsec
semantics remain in software?
Clarifying this would help understand how the driver is
expected to integrate with the Linux IPsec/XFRM framework.
Thanks.
Tony
On Wed, Mar 18, 2026 at 3:22 PM Pavitrakumar Managutte
<pavitrakumarm@vayavyalabs.com> wrote:
>
> Add the driver for SPAcc(Security Protocol Accelerator), which is a
> crypto acceleration IP from Synopsys. The SPAcc supports multiple ciphers,
> hashes and AEAD algorithms with various modes. The driver currently supports
> below
>
> hash:
> - cmac(aes)
> - xcbc(aes)
> - cmac(sm4)
> - xcbc(sm4)
> - hmac(md5)
> - md5
> - hmac(sha1)
> - sha1
> - sha224
> - sha256
> - sha384
> - sha512
> - hmac(sha224)
> - hmac(sha256)
> - hmac(sha384)
> - hmac(sha512)
> - sha3-224
> - sha3-256
> - sha3-384
> - sha3-512
> - hmac(sm3)
> - sm3
> - michael_mic
>
> changelog:
> v1->v2 changes:
> - Added local_bh_disable() and local_bh_enable() for the below calls.
> a. for ciphers skcipher_request_complete()
> b. for aead aead_request_complete()
> c. for hash ahash_request_complete()
> - dt-bindings updates
> a. removed snps,vspacc-priority and made it into config option
> b. renamed snps,spacc-wdtimer to snps,spacc-internal-counter
> c. Added description to all properties
> - Updated corresponding dt-binding changes to code
>
> v2->v3 changes:
> - cra_init and cra_exit replaced with init_tfm and exit_tfm for hashes.
> - removed mutex_lock/unlock for spacc_skcipher_fallback call
> - dt-bindings updates
> a. updated SOC related information
> b. renamed compatible string as per SOC
> - Updated corresponding dt-binding changes to code
>
> v3->v4 changes:
> - removed snps,vspacc-id from the dt-bindings
> - removed mutex_lock from ciphers
> - replaced magic numbers with macros
> - removed sw_fb variable from struct mode_tab and associated code from the
> hashes
> - polling code is replaced by wait_event_interruptible
>
> v4->v5 changes:
> - Updated to register with the crypto-engine
> - Used semaphore to manage SPAcc device hardware context pool
> - This patchset supports Hashes only
> - Dropping the support for Ciphers and AEADs in this patchset
> - Added Reviewed-by tag on the Device tree patch since it was reviewed on
> v4 patch by Krzysztof Kozlowski and Rob Herring (Arm)
>
> v5->v6 changes:
> - Removed CRYPTO_DEV_SPACC_CIPHER and CRYPTO_DEV_SPACC_AEAD Kconfig options,
> since the cipher and aead support is not part of this patchset
> - Dropped spacc_skcipher.o and spacc_aead.o from Makefile to fix build errors
> reported by kernel test robot
> - Added Reported-by and Closes tags as suggested
>
> v6->v7 changes:
> - Fixed build error reported by Kernel test robot
> - Added Reported-by and Closes tags as suggested
>
> v7->v8 changes:
> - Fixed misleading comment: Clarified that only HMAC key pre-processing
> is done in software, while the actual HMAC operation is performed by
> hardware
> - Simplified do_shash() function signature by removing unused parameters
> - Updated all do_shash() call sites to use new simplified signature
> - Fixed commit message formatting by adding "crypto: spacc - <subject>" to
> all patches
> - used __free() for scope based resource management
>
> v8->v9 changes:
> - Updated the software fallback implementation to use HASH_FBREQ_ON_STACK
> - Corrected dynamic allocation of statesize and reqsize in init_tfm
> - Fixed synchronization issues in the digest request
>
> v9->v10 changes:
> - Fixed unused variable warning
>
> v10->v11 changes:
> - Removed the redundant crypto_alloc_ahash in the init_tfm function
> - Removed the redundant crypto_free_ahash in exit_tfm function
> - Removed the redundant crypto_ahash_setkey call in setkey function
>
> Pavitrakumar Managutte (4):
> dt-bindings: crypto: Document support for SPAcc
> crypto: spacc - Add SPAcc ahash support
> Add SPAcc AUTODETECT Support
> crypto: spacc - Add SPAcc Kconfig and Makefile
>
> .../bindings/crypto/snps,dwc-spacc.yaml | 50 +
> drivers/crypto/Kconfig | 1 +
> drivers/crypto/Makefile | 1 +
> drivers/crypto/dwc-spacc/Kconfig | 88 +
> drivers/crypto/dwc-spacc/Makefile | 8 +
> drivers/crypto/dwc-spacc/spacc_ahash.c | 886 ++++++
> drivers/crypto/dwc-spacc/spacc_core.c | 2413 +++++++++++++++++
> drivers/crypto/dwc-spacc/spacc_core.h | 827 ++++++
> drivers/crypto/dwc-spacc/spacc_device.c | 276 ++
> drivers/crypto/dwc-spacc/spacc_device.h | 236 ++
> drivers/crypto/dwc-spacc/spacc_hal.c | 374 +++
> drivers/crypto/dwc-spacc/spacc_hal.h | 114 +
> drivers/crypto/dwc-spacc/spacc_interrupt.c | 328 +++
> drivers/crypto/dwc-spacc/spacc_manager.c | 610 +++++
> 14 files changed, 6212 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/crypto/snps,dwc-spacc.yaml
> create mode 100644 drivers/crypto/dwc-spacc/Kconfig
> create mode 100644 drivers/crypto/dwc-spacc/Makefile
> create mode 100644 drivers/crypto/dwc-spacc/spacc_ahash.c
> create mode 100644 drivers/crypto/dwc-spacc/spacc_core.c
> create mode 100644 drivers/crypto/dwc-spacc/spacc_core.h
> create mode 100644 drivers/crypto/dwc-spacc/spacc_device.c
> create mode 100644 drivers/crypto/dwc-spacc/spacc_device.h
> create mode 100644 drivers/crypto/dwc-spacc/spacc_hal.c
> create mode 100644 drivers/crypto/dwc-spacc/spacc_hal.h
> create mode 100644 drivers/crypto/dwc-spacc/spacc_interrupt.c
> create mode 100644 drivers/crypto/dwc-spacc/spacc_manager.c
>
>
> base-commit: c708d3fad4217f23421b8496e231b0c5cee617a0
> --
> 2.25.1
>
>
^ permalink raw reply
* [PATCH v4] dt-bindings: input: touchscreen: ti,tsc2005: Add wakeup-source
From: phucduc.bui @ 2026-04-03 4:07 UTC (permalink / raw)
To: robh
Cc: conor+dt, conor, devicetree, dmitry.torokhov, krzk+dt, krzk,
linux-input, linux-kernel, marex, mingo, phucduc.bui, tglx
From: bui duc phuc <phucduc.bui@gmail.com>
Document the "wakeup-source" property for the ti,tsc2005 touchscreen
controllers to allow the device to wake the system from suspend.
Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
changes:
v4: Drop redundant "type: boolean" for wakeup-source to use the core
definition from dt-schema (as suggested by Rob Herring).
v3: Remove blank lines (suggested by Conor).
v2: Revise the commit content and remove patch1 related to I2C and SPI
wakeup handling
.../devicetree/bindings/input/touchscreen/ti,tsc2005.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/ti,tsc2005.yaml b/Documentation/devicetree/bindings/input/touchscreen/ti,tsc2005.yaml
index 7187c390b2f5..c06a85b74533 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/ti,tsc2005.yaml
+++ b/Documentation/devicetree/bindings/input/touchscreen/ti,tsc2005.yaml
@@ -55,6 +55,8 @@ properties:
touchscreen-size-x: true
touchscreen-size-y: true
+ wakeup-source: true
+
allOf:
- $ref: touchscreen.yaml#
- if:
@@ -97,6 +99,7 @@ examples:
ti,x-plate-ohms = <280>;
ti,esd-recovery-timeout-ms = <8000>;
+ wakeup-source;
};
};
- |
@@ -124,5 +127,6 @@ examples:
ti,x-plate-ohms = <280>;
ti,esd-recovery-timeout-ms = <8000>;
+ wakeup-source;
};
};
--
2.43.0
^ permalink raw reply related
* [PATCH] drivers/of: fdt: validate stdout-path properties before parsing them
From: Pengpeng Hou @ 2026-04-03 3:55 UTC (permalink / raw)
To: Rob Herring, Saravana Kannan; +Cc: devicetree, linux-kernel, pengpeng
early_init_dt_scan_chosen_stdout() fetches stdout-path and
linux,stdout-path directly from the flat DT and immediately passes the
result to strchrnul(). Flat DT properties are raw firmware-supplied
byte sequences, and this path does not prove that either property is
NUL-terminated within its declared bounds.
Use fdt_stringlist_get() so malformed unterminated stdout-path
properties are rejected before the local parser walks them as C
strings.
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
drivers/of/fdt.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 331646d667b9..3dcd20c2fa73 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -954,9 +954,9 @@ int __init early_init_dt_scan_chosen_stdout(void)
if (offset < 0)
return -ENOENT;
- p = fdt_getprop(fdt, offset, "stdout-path", &l);
+ p = fdt_stringlist_get(fdt, offset, "stdout-path", 0, &l);
if (!p)
- p = fdt_getprop(fdt, offset, "linux,stdout-path", &l);
+ p = fdt_stringlist_get(fdt, offset, "linux,stdout-path", 0, &l);
if (!p || !l)
return -ENOENT;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related
* Re: [PATCH] arm64: dts: imx{91,93}-phyboard-segin: Add peb-av-18 overlay
From: Frank Li @ 2026-04-03 4:00 UTC (permalink / raw)
To: Florijan Plohl
Cc: Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, imx, linux-arm-kernel,
devicetree, linux-kernel, upstream
In-Reply-To: <20260402070826.970012-1-florijan.plohl@norik.com>
On Thu, Apr 02, 2026 at 09:08:26AM +0200, Florijan Plohl wrote:
> Add overlay for the PEB-AV-18 adapter on phyBOARD-Segin-i.MX91/93.
> The supported LCD is Powertip PH800480T032-ZHC19 panel (AC220).
>
> Signed-off-by: Florijan Plohl <florijan.plohl@norik.com>
> ---
> arch/arm64/boot/dts/freescale/Makefile | 4 +
> .../imx91-phyboard-segin-peb-av-18.dtso | 142 ++++++++++++++++++
> .../imx93-phyboard-segin-peb-av-18.dtso | 142 ++++++++++++++++++
> 3 files changed, 288 insertions(+)
> create mode 100644 arch/arm64/boot/dts/freescale/imx91-phyboard-segin-peb-av-18.dtso
> create mode 100644 arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-av-18.dtso
>
> diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile
> index bae24b53bce6..8f5b3996b678 100644
> --- a/arch/arm64/boot/dts/freescale/Makefile
> +++ b/arch/arm64/boot/dts/freescale/Makefile
> @@ -437,17 +437,21 @@ dtb-$(CONFIG_ARCH_MXC) += imx93-kontron-bl-osm-s.dtb
> dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-nash.dtb
> dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-segin.dtb
>
> +imx91-phyboard-segin-peb-av-18-dtbs += imx91-phyboard-segin.dtb imx91-phyboard-segin-peb-av-18.dtbo
> imx93-phyboard-nash-jtag-dtbs += imx93-phyboard-nash.dtb imx93-phyboard-nash-jtag.dtbo
> imx93-phyboard-nash-peb-wlbt-07-dtbs += imx93-phyboard-nash.dtb imx93-phyboard-nash-peb-wlbt-07.dtbo
> imx93-phyboard-nash-pwm-fan-dtbs += imx93-phyboard-nash.dtb imx93-phyboard-nash-pwm-fan.dtbo
> imx93-phyboard-segin-peb-av-02-dtbs += imx93-phyboard-segin.dtb imx93-phyboard-segin-peb-av-02.dtbo
> +imx93-phyboard-segin-peb-av-18-dtbs += imx93-phyboard-segin.dtb imx93-phyboard-segin-peb-av-18.dtbo
> imx93-phyboard-segin-peb-eval-01-dtbs += imx93-phyboard-segin.dtb imx93-phyboard-segin-peb-eval-01.dtbo
> imx93-phyboard-segin-peb-wlbt-05-dtbs += imx93-phyboard-segin.dtb imx93-phyboard-segin-peb-wlbt-05.dtbo
> imx93-phycore-rpmsg-dtbs += imx93-phyboard-nash.dtb imx93-phyboard-segin.dtb imx93-phycore-rpmsg.dtbo
> +dtb-$(CONFIG_ARCH_MXC) += imx91-phyboard-segin-peb-av-18.dtb
https://sashiko.dev/#/patchset/20260402070826.970012-1-florijan.plohl%40norik.com
"should this be kept with the other imx91 dtb definition"
Frank Li
> dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-nash-jtag.dtb
> dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-nash-peb-wlbt-07.dtb
> dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-nash-pwm-fan.dtb
> dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-segin-peb-av-02.dtb
> +dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-segin-peb-av-18.dtb
> dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-segin-peb-eval-01.dtb
> dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-segin-peb-wlbt-05.dtb
> dtb-$(CONFIG_ARCH_MXC) += imx93-phycore-rpmsg.dtb
> diff --git a/arch/arm64/boot/dts/freescale/imx91-phyboard-segin-peb-av-18.dtso b/arch/arm64/boot/dts/freescale/imx91-phyboard-segin-peb-av-18.dtso
> new file mode 100644
> index 000000000000..ec6ef2e5a11a
> --- /dev/null
> +++ b/arch/arm64/boot/dts/freescale/imx91-phyboard-segin-peb-av-18.dtso
> @@ -0,0 +1,142 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (C) 2026 PHYTEC Messtechnik GmbH
> + *
> + * Author: Florijan Plohl <florijan.plohl@norik.com>
> + */
> +
> +#include <dt-bindings/clock/imx93-clock.h>
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/interrupt-controller/arm-gic.h>
> +#include "imx91-pinfunc.h"
> +
> +/dts-v1/;
> +/plugin/;
> +
> +&{/} {
> + backlight: backlight {
> + compatible = "pwm-backlight";
> + brightness-levels = <0 4 8 16 32 64 128 255>;
> + default-brightness-level = <5>;
> + power-supply = <®_vcc_3v3_con>;
> + pwms = <&pwm7 0 5000000 0>;
> + };
> +
> + panel {
> + compatible = "powertip,ph800480t032-zhc19";
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_panel>;
> +
> + backlight = <&backlight>;
> + enable-gpios = <&gpio4 29 GPIO_ACTIVE_HIGH>;
> + power-supply = <®_vcc_3v3_con>;
> +
> + port {
> + panel_in: endpoint {
> + remote-endpoint = <&dpi_to_panel>;
> + };
> + };
> + };
> +
> + pwm7: pwm-7 {
> + compatible = "pwm-gpio";
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_pwm7>;
> + gpios = <&gpio4 28 GPIO_ACTIVE_HIGH>;
> + #pwm-cells = <3>;
> + };
> +
> + reg_vcc_3v3_con: regulator-vcc-3v3-con {
> + compatible = "regulator-fixed";
> + regulator-name = "VCC3V3_CON";
> + regulator-max-microvolt = <3300000>;
> + regulator-min-microvolt = <3300000>;
> + };
> +};
> +
> +&dpi_bridge {
> + status = "okay";
> +};
> +
> +&dpi_to_panel {
> + remote-endpoint = <&panel_in>;
> + bus-width = <18>;
> +};
> +
> +&lcdif {
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_lcdif>;
> + assigned-clocks = <&clk IMX93_CLK_VIDEO_PLL>;
> + assigned-clock-rates = <27272728>;
> + status = "okay";
> +};
> +
> +&lpi2c2 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + touchscreen@41 {
> + compatible = "ilitek,ili2130";
> + reg = <0x41>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_touchscreen>;
> + interrupt-parent = <&gpio4>;
> + interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
> + reset-gpios = <&gpio4 1 GPIO_ACTIVE_LOW>;
> + touchscreen-size-x = <800>;
> + touchscreen-size-y = <480>;
> + wakeup-source;
> + };
> +};
> +
> +&media_blk_ctrl {
> + status = "okay";
> +};
> +
> +&iomuxc {
> + pinctrl_lcdif: lcdifgrp {
> + fsl,pins = <
> + MX91_PAD_GPIO_IO00__MEDIAMIX_DISP_CLK 0x50e
> + MX91_PAD_GPIO_IO01__MEDIAMIX_DISP_DE 0x50e
> + MX91_PAD_GPIO_IO02__MEDIAMIX_DISP_VSYNC 0x50e
> + MX91_PAD_GPIO_IO03__MEDIAMIX_DISP_HSYNC 0x50e
> + MX91_PAD_GPIO_IO04__MEDIAMIX_DISP_DATA0 0x50e
> + MX91_PAD_GPIO_IO05__MEDIAMIX_DISP_DATA1 0x50e
> + MX91_PAD_GPIO_IO06__MEDIAMIX_DISP_DATA2 0x50e
> + MX91_PAD_GPIO_IO07__MEDIAMIX_DISP_DATA3 0x50e
> + MX91_PAD_GPIO_IO08__MEDIAMIX_DISP_DATA4 0x50e
> + MX91_PAD_GPIO_IO09__MEDIAMIX_DISP_DATA5 0x51e
> + MX91_PAD_GPIO_IO10__MEDIAMIX_DISP_DATA6 0x50e
> + MX91_PAD_GPIO_IO11__MEDIAMIX_DISP_DATA7 0x50e
> + MX91_PAD_GPIO_IO12__MEDIAMIX_DISP_DATA8 0x50e
> + MX91_PAD_GPIO_IO13__MEDIAMIX_DISP_DATA9 0x50e
> + MX91_PAD_GPIO_IO14__MEDIAMIX_DISP_DATA10 0x50e
> + MX91_PAD_GPIO_IO15__MEDIAMIX_DISP_DATA11 0x50e
> + MX91_PAD_GPIO_IO16__MEDIAMIX_DISP_DATA12 0x506
> + MX91_PAD_GPIO_IO17__MEDIAMIX_DISP_DATA13 0x506
> + MX91_PAD_GPIO_IO18__MEDIAMIX_DISP_DATA14 0x506
> + MX91_PAD_GPIO_IO19__MEDIAMIX_DISP_DATA15 0x506
> + MX91_PAD_GPIO_IO20__MEDIAMIX_DISP_DATA16 0x506
> + MX91_PAD_GPIO_IO21__MEDIAMIX_DISP_DATA17 0x506
> + >;
> + };
> +
> + pinctrl_panel: panelgrp {
> + fsl,pins = <
> + MX91_PAD_CCM_CLKO4__GPIO4_IO29 0x1133e
> + >;
> + };
> +
> + pinctrl_pwm7: pwm7grp {
> + fsl,pins = <
> + MX91_PAD_CCM_CLKO3__GPIO4_IO28 0x1133e
> + >;
> + };
> +
> + pinctrl_touchscreen: touchscreengrp {
> + fsl,pins = <
> + MX91_PAD_ENET1_MDIO__GPIO4_IO1 0x11e
> + MX91_PAD_ENET1_RD2__GPIO4_IO12 0x1133e
> + >;
> + };
> +};
> diff --git a/arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-av-18.dtso b/arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-av-18.dtso
> new file mode 100644
> index 000000000000..189b0f0472d2
> --- /dev/null
> +++ b/arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-av-18.dtso
> @@ -0,0 +1,142 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (C) 2026 PHYTEC Messtechnik GmbH
> + *
> + * Author: Florijan Plohl <florijan.plohl@norik.com>
> + */
> +
> +#include <dt-bindings/clock/imx93-clock.h>
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/interrupt-controller/arm-gic.h>
> +#include "imx93-pinfunc.h"
> +
> +/dts-v1/;
> +/plugin/;
> +
> +&{/} {
> + backlight: backlight {
> + compatible = "pwm-backlight";
> + brightness-levels = <0 4 8 16 32 64 128 255>;
> + default-brightness-level = <5>;
> + power-supply = <®_vcc_3v3_con>;
> + pwms = <&pwm7 0 5000000 0>;
> + };
> +
> + panel {
> + compatible = "powertip,ph800480t032-zhc19";
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_panel>;
> +
> + backlight = <&backlight>;
> + enable-gpios = <&gpio4 29 GPIO_ACTIVE_HIGH>;
> + power-supply = <®_vcc_3v3_con>;
> +
> + port {
> + panel_in: endpoint {
> + remote-endpoint = <&dpi_to_panel>;
> + };
> + };
> + };
> +
> + pwm7: pwm-7 {
> + compatible = "pwm-gpio";
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_pwm7>;
> + gpios = <&gpio4 28 GPIO_ACTIVE_HIGH>;
> + #pwm-cells = <3>;
> + };
> +
> + reg_vcc_3v3_con: regulator-vcc-3v3-con {
> + compatible = "regulator-fixed";
> + regulator-name = "VCC3V3_CON";
> + regulator-max-microvolt = <3300000>;
> + regulator-min-microvolt = <3300000>;
> + };
> +};
> +
> +&dpi_bridge {
> + status = "okay";
> +};
> +
> +&dpi_to_panel {
> + remote-endpoint = <&panel_in>;
> + bus-width = <18>;
> +};
> +
> +&lcdif {
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_lcdif>;
> + assigned-clocks = <&clk IMX93_CLK_VIDEO_PLL>;
> + assigned-clock-rates = <27272728>;
> + status = "okay";
> +};
> +
> +&lpi2c2 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + touchscreen@41 {
> + compatible = "ilitek,ili2130";
> + reg = <0x41>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_touchscreen>;
> + interrupt-parent = <&gpio4>;
> + interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
> + reset-gpios = <&gpio4 1 GPIO_ACTIVE_LOW>;
> + touchscreen-size-x = <800>;
> + touchscreen-size-y = <480>;
> + wakeup-source;
> + };
> +};
> +
> +&media_blk_ctrl {
> + status = "okay";
> +};
> +
> +&iomuxc {
> + pinctrl_lcdif: lcdifgrp {
> + fsl,pins = <
> + MX93_PAD_GPIO_IO00__MEDIAMIX_DISP_CLK 0x50e
> + MX93_PAD_GPIO_IO01__MEDIAMIX_DISP_DE 0x50e
> + MX93_PAD_GPIO_IO02__MEDIAMIX_DISP_VSYNC 0x50e
> + MX93_PAD_GPIO_IO03__MEDIAMIX_DISP_HSYNC 0x50e
> + MX93_PAD_GPIO_IO04__MEDIAMIX_DISP_DATA00 0x50e
> + MX93_PAD_GPIO_IO05__MEDIAMIX_DISP_DATA01 0x50e
> + MX93_PAD_GPIO_IO06__MEDIAMIX_DISP_DATA02 0x50e
> + MX93_PAD_GPIO_IO07__MEDIAMIX_DISP_DATA03 0x50e
> + MX93_PAD_GPIO_IO08__MEDIAMIX_DISP_DATA04 0x50e
> + MX93_PAD_GPIO_IO09__MEDIAMIX_DISP_DATA05 0x51e
> + MX93_PAD_GPIO_IO10__MEDIAMIX_DISP_DATA06 0x50e
> + MX93_PAD_GPIO_IO11__MEDIAMIX_DISP_DATA07 0x50e
> + MX93_PAD_GPIO_IO12__MEDIAMIX_DISP_DATA08 0x50e
> + MX93_PAD_GPIO_IO13__MEDIAMIX_DISP_DATA09 0x50e
> + MX93_PAD_GPIO_IO14__MEDIAMIX_DISP_DATA10 0x50e
> + MX93_PAD_GPIO_IO15__MEDIAMIX_DISP_DATA11 0x50e
> + MX93_PAD_GPIO_IO16__MEDIAMIX_DISP_DATA12 0x506
> + MX93_PAD_GPIO_IO17__MEDIAMIX_DISP_DATA13 0x506
> + MX93_PAD_GPIO_IO18__MEDIAMIX_DISP_DATA14 0x506
> + MX93_PAD_GPIO_IO19__MEDIAMIX_DISP_DATA15 0x506
> + MX93_PAD_GPIO_IO20__MEDIAMIX_DISP_DATA16 0x506
> + MX93_PAD_GPIO_IO21__MEDIAMIX_DISP_DATA17 0x506
> + >;
> + };
> +
> + pinctrl_panel: panelgrp {
> + fsl,pins = <
> + MX93_PAD_CCM_CLKO4__GPIO4_IO29 0x1133e
> + >;
> + };
> +
> + pinctrl_pwm7: pwm7grp {
> + fsl,pins = <
> + MX93_PAD_CCM_CLKO3__GPIO4_IO28 0x1133e
> + >;
> + };
> +
> + pinctrl_touchscreen: touchscreengrp {
> + fsl,pins = <
> + MX93_PAD_ENET1_MDIO__GPIO4_IO01 0x11e
> + MX93_PAD_ENET1_RD2__GPIO4_IO12 0x1133e
> + >;
> + };
> +};
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v1 22/22] riscv: dts: starfive: jhb100: Add clocks and resets nodes
From: Changhuang Liang @ 2026-04-03 1:07 UTC (permalink / raw)
To: Conor Dooley
Cc: Michael Turquette, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Stephen Boyd, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Philipp Zabel, Emil Renner Berthing, Kees Cook,
Gustavo A . R . Silva, Richard Cochran, linux-clk@vger.kernel.org,
linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
linux-riscv@lists.infradead.org, linux-hardening@vger.kernel.org,
netdev@vger.kernel.org, JeeHeng Sia, Hal Feng, Leyfoon Tan
In-Reply-To: <20260402-fox-overhand-9a45ec670bce@spud>
Hi, Conor
> On Thu, Apr 02, 2026 at 03:55:23AM -0700, Changhuang Liang wrote:
> > Add clocks and resets nodes for JHB100 RISC-V BMC SoC. They contain
> > sys0crg/sys1crg/sys2crg/per0crg/per1crg/per2crg/per3crg.
> >
> > Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
> > ---
> > arch/riscv/boot/dts/starfive/jhb100.dtsi | 198
> > ++++++++++++++++++++++-
> > 1 file changed, 195 insertions(+), 3 deletions(-)
> >
> > diff --git a/arch/riscv/boot/dts/starfive/jhb100.dtsi
> > b/arch/riscv/boot/dts/starfive/jhb100.dtsi
> > index 4d03470f78ab..700d00f800bc 100644
> > --- a/arch/riscv/boot/dts/starfive/jhb100.dtsi
> > +++ b/arch/riscv/boot/dts/starfive/jhb100.dtsi
> > @@ -4,6 +4,8 @@
> > */
> >
> > /dts-v1/;
> > +#include <dt-bindings/clock/starfive,jhb100-crg.h>
> > +#include <dt-bindings/reset/starfive,jhb100-crg.h>
> >
> > / {
> > compatible = "starfive,jhb100";
> > @@ -268,12 +270,96 @@ pmu {
> > <0x00 0x22 0xFFFFFFFF 0xFFFFFF22 0x00007FF8>; /* Event
> ID 34 */
> > };
> >
> > - clk_uart: clk-uart {
> > - compatible = "fixed-clock"; /* Initial clock handler for UART */
> > + osc: osc {
> > + compatible = "fixed-clock";
> > #clock-cells = <0>;
> > clock-frequency = <25000000>;
> > };
>
> Is this really on the SoC?
This is not on the SoC.
>
> >
> > + pll0: pll0 {
> > + compatible = "fixed-clock";
> > + #clock-cells = <0>;
> > + clock-frequency = <2400000000>;
> > + };
>
> What's providing all of these PLLs? Are they all fixed-frequency on-chip PLLs
> without an off-chip reference? I find that somewhat unlikely.
>
> Since devicetrees are now being imported into U-Boot, it's important to make
> sure that I'm not merging fixed-clocks that later get replaced by dedicated
> drivers that U-Boot won't have.
>
> To that end, I won't apply the series this depends on without this patch being
> applied at the same time.
I am preparing a PLL driver series, but PLL0 and PLL1 will still retain fixed frequencies.
The reference clock for each PLL comes from the osc. Perhaps I can use "fixed-factor-clock"
to indicate the relationship of the reference clock.
Best Regards,
Changhuang
^ permalink raw reply
* Re: [PATCH v3] dt-bindings: input: touchscreen: ti,tsc2005: Add wakeup-source
From: Bui Duc Phuc @ 2026-04-03 3:35 UTC (permalink / raw)
To: Rob Herring
Cc: conor+dt, krzk+dt, conor, devicetree, dmitry.torokhov, krzk,
linux-input, linux-kernel, marex, mingo, tglx
In-Reply-To: <20260326141551.GA2304345-robh@kernel.org>
Hi Rob, Krzysztof, Conor,
Thanks for the discussion.
The core schema already defines "wakeup-source" as a common property
(using oneOf for boolean or phandle-array), so defining the type here is
redundant.
https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/wakeup-source.yaml
I'll update it to "wakeup-source: true" in v4.
Best regards,
Phuc
On Thu, Mar 26, 2026 at 9:15 PM Rob Herring <robh@kernel.org> wrote:
>
> On Wed, Mar 18, 2026 at 03:31:24PM +0700, phucduc.bui@gmail.com wrote:
> > From: bui duc phuc <phucduc.bui@gmail.com>
> >
> > Document the "wakeup-source" property for the ti,tsc2005 touchscreen
> > controllers to allow the device to wake the system from suspend.
> >
> > Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
> > ---
> >
> > changes:
> > v3: Remove blank lines
> > v2: Revise the commit content and remove patch1 related to I2C and SPI
> > wakeup handling
> >
> > .../devicetree/bindings/input/touchscreen/ti,tsc2005.yaml | 5 +++++
> > 1 file changed, 5 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/input/touchscreen/ti,tsc2005.yaml b/Documentation/devicetree/bindings/input/touchscreen/ti,tsc2005.yaml
> > index 7187c390b2f5..a9842509c1fe 100644
> > --- a/Documentation/devicetree/bindings/input/touchscreen/ti,tsc2005.yaml
> > +++ b/Documentation/devicetree/bindings/input/touchscreen/ti,tsc2005.yaml
> > @@ -55,6 +55,9 @@ properties:
> > touchscreen-size-x: true
> > touchscreen-size-y: true
> >
> > + wakeup-source:
> > + type: boolean
>
> wakeup-source already has a defined type.
>
> wakeup-source: true
>
> > +
> > allOf:
> > - $ref: touchscreen.yaml#
> > - if:
> > @@ -97,6 +100,7 @@ examples:
> >
> > ti,x-plate-ohms = <280>;
> > ti,esd-recovery-timeout-ms = <8000>;
> > + wakeup-source;
> > };
> > };
> > - |
> > @@ -124,5 +128,6 @@ examples:
> >
> > ti,x-plate-ohms = <280>;
> > ti,esd-recovery-timeout-ms = <8000>;
> > + wakeup-source;
> > };
> > };
> > --
> > 2.43.0
> >
^ permalink raw reply
* Re: [PATCH v1 5/5] riscv: dts: starfive: jhb100: Add JHB100 base DT
From: Changhuang Liang @ 2026-04-03 3:06 UTC (permalink / raw)
To: Conor Dooley
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Paul Walmsley, Samuel Holland, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Daniel Lezcano, Emil Renner Berthing, Yixun Lan,
Joel Stanley, Drew Fustini, Darshan Prajapati, Guodong Xu,
Michal Simek, Junhui Liu, Heinrich Schuchardt, E Shattow,
Icenowy Zheng, Anup Patel, linux-kernel@vger.kernel.org,
devicetree@vger.kernel.org, linux-riscv@lists.infradead.org,
JiSheng Teoh, Hal Feng, Leyfoon Tan, Michael Zhu
In-Reply-To: <20260402-shakable-hefty-0164a042b748@spud>
Hi, Conor
> On Thu, Apr 02, 2026 at 01:40:19AM -0700, Changhuang Liang wrote:
> > From: Ley Foon Tan <leyfoon.tan@starfivetech.com>
> >
> > Add JHB100 base dtsi and dts. Consist of 4 Dubhe-70 cores, CLINT,
> > PLIC, PMU, UART and 1GB DDR.
> >
> > Signed-off-by: Ley Foon Tan <leyfoon.tan@starfivetech.com>
> > Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
> > ---
> > MAINTAINERS | 6 +
> > arch/riscv/boot/dts/starfive/Makefile | 2 +
> > .../boot/dts/starfive/jhb100-evb1-eth.dts | 6 +
> > arch/riscv/boot/dts/starfive/jhb100-evb1.dtsi | 32 ++
> > arch/riscv/boot/dts/starfive/jhb100.dtsi | 326
> ++++++++++++++++++
> > 5 files changed, 372 insertions(+)
> > create mode 100644 arch/riscv/boot/dts/starfive/jhb100-evb1-eth.dts
> > create mode 100644 arch/riscv/boot/dts/starfive/jhb100-evb1.dtsi
> > create mode 100644 arch/riscv/boot/dts/starfive/jhb100.dtsi
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS index
> > 7d10988cbc62..b1892a480c31 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -25306,6 +25306,12 @@ S: Supported
> > F:
> Documentation/devicetree/bindings/interrupt-controller/starfive,jh8100
> -intc.yaml
> > F: drivers/irqchip/irq-starfive-jh8100-intc.c
> >
> > +STARFIVE JHB100 DEVICETREES
> > +M: Changhuang Liang <changhuang.liang@starfivetech.com>
> > +L: linux-riscv@lists.infradead.org
> > +S: Maintained
>
> Supported, no?
>
> > +F: arch/riscv/boot/dts/starfive/jhb100*
> > +
> > STATIC BRANCH/CALL
> > M: Peter Zijlstra <peterz@infradead.org>
> > M: Josh Poimboeuf <jpoimboe@kernel.org>
> > diff --git a/arch/riscv/boot/dts/starfive/Makefile
> > b/arch/riscv/boot/dts/starfive/Makefile
> > index 3dd1f05283f7..7cdb75788053 100644
> > --- a/arch/riscv/boot/dts/starfive/Makefile
> > +++ b/arch/riscv/boot/dts/starfive/Makefile
> > @@ -18,3 +18,5 @@ dtb-$(CONFIG_ARCH_STARFIVE) +=
> > jh7110-starfive-visionfive-2-lite.dtb
> > dtb-$(CONFIG_ARCH_STARFIVE) +=
> > jh7110-starfive-visionfive-2-lite-emmc.dtb
> > dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
> > dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb
> > +
> > +dtb-$(CONFIG_ARCH_STARFIVE) += jhb100-evb1-eth.dtb
> > diff --git a/arch/riscv/boot/dts/starfive/jhb100-evb1-eth.dts
> > b/arch/riscv/boot/dts/starfive/jhb100-evb1-eth.dts
> > new file mode 100644
> > index 000000000000..62cd046e1224
> > --- /dev/null
> > +++ b/arch/riscv/boot/dts/starfive/jhb100-evb1-eth.dts
> > @@ -0,0 +1,6 @@
> > +// SPDX-License-Identifier: GPL-2.0 OR MIT
> > +/*
> > + * Copyright (c) 2024-2026 StarFive Technology Co., Ltd.
> > + */
> > +
> > +#include "jhb100-evb1.dtsi"
>
> What is the point of this file? Is this the base-board?
> Shouldn't it have a specific compatible?
>
> Can the SoM be used without a base board? I've got no info about this board
> appearing on google, do you even have pictures of it or any documentation?
> I see this
> https://www.starfivetech.com/en/index.php?s=hardware&c=show&id=22
> and
> https://www.starfivetech.com/en/index.php?s=hardware&c=show&id=23
> but the former doesn't look like it needs a base-board and the latter is called
> "evb3", so is not what's here?
The former is the base board of the EVB1. Currently, we are only carrying out
upstream work based on the EVB1. The EVB1 base board has reserved slots
that can accommodate expansion boards to verify more advanced features.
At present, the jhb100-evb1.dtsi file corresponds to the configuration of the
EVB1 base board. In the future, we will add dtsi files for the expansion boards.
The jhb100-evb1-eth.dts file will then be used to combine these dtsi files to
generate the final version of the device tree source.
Best Regards,
Changhuang
^ permalink raw reply
* Re: [PATCh v3 13/14] ASoC: rsnd: Export rsnd_ssiu_mod_get() for PM support
From: Kuninori Morimoto @ 2026-04-03 1:59 UTC (permalink / raw)
To: John Madieu
Cc: Mark Brown, Liam Girdwood, Geert Uytterhoeven, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai,
Magnus Damm, Philipp Zabel, Claudiu Beznea, Biju Das, john.madieu,
linux-sound, linux-renesas-soc, devicetree, linux-kernel
In-Reply-To: <20260402162436.12059-14-john.madieu.xa@bp.renesas.com>
Hi John
> Remove the static qualifier from rsnd_ssiu_mod_get() and export it
> via rsnd.h.
>
> This is preparation for system suspend/resume support, where the PM
> callbacks need to access SSIU modules to manage their clock and reset
> state.
>
> Signed-off-by: John Madieu <john.madieu.xa@bp.renesas.com>
> ---
Is rsnd_ssiu_mod_get() really used ?
I can't find it on [14/14]
Thank you for your help !!
Best regards
---
Kuninori Morimoto
^ permalink raw reply
* Re: [PATCh v3 14/14] ASoC: rsnd: Add system suspend/resume support
From: Kuninori Morimoto @ 2026-04-03 1:53 UTC (permalink / raw)
To: John Madieu
Cc: Mark Brown, Liam Girdwood, Geert Uytterhoeven, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai,
Magnus Damm, Philipp Zabel, Claudiu Beznea, Biju Das, john.madieu,
linux-sound, linux-renesas-soc, devicetree, linux-kernel
In-Reply-To: <20260402162436.12059-15-john.madieu.xa@bp.renesas.com>
Hi John
> Add per-module suspend/resume functions following the existing driver
> architecture where each module manages its own resources in its own
> file. core.c provides common clock/reset helpers and orchestrates the
> calls in the correct order (reverse probe for suspend, probe order
> for resume).
>
> Infrastructure clocks (ADG, audmacpp, SCU) are managed globally
> using optional APIs to remain transparent to platforms that don't
> specify these clocks/resets.
>
> Signed-off-by: John Madieu <john.madieu.xa@bp.renesas.com>
> ---
(snip)
> +/* Per-module suspend/resume */
> +void rsnd_ssi_suspend(struct rsnd_priv *priv);
> +void rsnd_ssi_resume(struct rsnd_priv *priv);
> +void rsnd_ssiu_suspend(struct rsnd_priv *priv);
> +void rsnd_ssiu_resume(struct rsnd_priv *priv);
> +void rsnd_src_suspend(struct rsnd_priv *priv);
> +void rsnd_src_resume(struct rsnd_priv *priv);
> +void rsnd_ctu_suspend(struct rsnd_priv *priv);
> +void rsnd_ctu_resume(struct rsnd_priv *priv);
> +void rsnd_mix_suspend(struct rsnd_priv *priv);
> +void rsnd_mix_resume(struct rsnd_priv *priv);
> +void rsnd_dvc_suspend(struct rsnd_priv *priv);
> +void rsnd_dvc_resume(struct rsnd_priv *priv);
> +void rsnd_adg_suspend(struct rsnd_priv *priv);
> +void rsnd_adg_resume(struct rsnd_priv *priv);
> +void rsnd_dma_suspend(struct rsnd_priv *priv);
> +void rsnd_dma_resume(struct rsnd_priv *priv);
rsnd.h is categorizing definition for each IPs.
Please merge above into existing area.
Thank you for your help !!
Best regards
---
Kuninori Morimoto
^ permalink raw reply
* Re: [PATCh v3 12/14] ASoC: rsnd: Add rsnd_adg_mod_get() for PM support
From: Kuninori Morimoto @ 2026-04-03 1:50 UTC (permalink / raw)
To: John Madieu
Cc: Mark Brown, Liam Girdwood, Geert Uytterhoeven, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai,
Magnus Damm, Philipp Zabel, Claudiu Beznea, Biju Das, john.madieu,
linux-sound, linux-renesas-soc, devicetree, linux-kernel
In-Reply-To: <20260402162436.12059-13-john.madieu.xa@bp.renesas.com>
Hi John
> Add rsnd_adg_mod_get() to retrieve the ADG module handle.
>
> This is preparation for system suspend/resume support, where the PM
> callbacks need to access the ADG module to manage its clock and reset
> state. Other modules (SSI, SRC, CTU, MIX, DVC) already have their
> getters exported.
>
> Signed-off-by: John Madieu <john.madieu.xa@bp.renesas.com>
> ---
This is just a comment.
The reason why other modules has getting function is it is needed
on rsnd_parse_connect_xxx() which is called in __rsnd_dai_probe().
> @@ -618,6 +618,7 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
> #define rsnd_adg_clk_disable(priv) rsnd_adg_clk_control(priv, 0)
> int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable);
> void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m);
> +struct rsnd_mod *rsnd_adg_mod_get(struct rsnd_priv *priv);
The user of rsnd_adg_mod_get() is only ADG, no need to add it on rsnd.h ?
Thank you for your help !!
Best regards
---
Kuninori Morimoto
^ permalink raw reply
* Re: [PATCh v3 11/14] ASoC: rsnd: src: Add SRC reset and clock support for RZ/G3E
From: Kuninori Morimoto @ 2026-04-03 1:38 UTC (permalink / raw)
To: John Madieu
Cc: Mark Brown, Liam Girdwood, Geert Uytterhoeven, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai,
Magnus Damm, Philipp Zabel, Claudiu Beznea, Biju Das, john.madieu,
linux-sound, linux-renesas-soc, devicetree, linux-kernel
In-Reply-To: <20260402162436.12059-12-john.madieu.xa@bp.renesas.com>
Hi John
> The RZ/G3E SoC requires explicit SCU (Sampling Rate Converter Unit)
> reset and clock management unlike previous R-Car generations:
>
> - scu_clk: SCU module clock
> - scu_clkx2: SCU double-rate clock
> - scu_supply_clk: SCU supply clock
>
> Without these clocks enabled, the SRC module cannot operate on RZ/G3E.
> Add support for the shared SCU reset controller used by the SRC modules
> on the Renesas RZ/G3E SoC. All SRC instances are gated by the same "scu"
> reset line.
>
> Signed-off-by: John Madieu <john.madieu.xa@bp.renesas.com>
> ---
(snip)
> diff --git a/sound/soc/renesas/rcar/rsnd.h b/sound/soc/renesas/rcar/rsnd.h
> index 2c5738926093..8700b39b535e 100644
> --- a/sound/soc/renesas/rcar/rsnd.h
> +++ b/sound/soc/renesas/rcar/rsnd.h
> @@ -632,6 +632,13 @@ struct rsnd_priv {
> struct clk *audmapp_clk;
> struct reset_control *audmapp_rstc;
>
> + /*
> + * Below values will be filled in rsnd_src_probe()
> + */
> + struct clk *clk_scu;
> + struct clk *clk_scu_x2;
> + struct clk *clk_scu_supply;
It is SRC specific.
Please move it to rsnd_src instead of rsnd_priv.
> @@ -711,8 +720,9 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
>
> int rsnd_src_probe(struct rsnd_priv *priv)
> {
> - struct device_node *node;
> struct device *dev = rsnd_priv_to_dev(priv);
> + struct reset_control *rstc;
> + struct device_node *node;
Very nitpick. No need to move *node :)
Thank you for your help !!
Best regards
---
Kuninori Morimoto
^ permalink raw reply
* Re: [PATCh v3 10/14] ASoC: rsnd: adg: Add per-SSI ADG and SSIF supply clock management
From: Kuninori Morimoto @ 2026-04-03 1:33 UTC (permalink / raw)
To: John Madieu
Cc: Mark Brown, Liam Girdwood, Geert Uytterhoeven, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai,
Magnus Damm, Philipp Zabel, Claudiu Beznea, Biju Das, john.madieu,
linux-sound, linux-renesas-soc, devicetree, linux-kernel
In-Reply-To: <20260402162436.12059-11-john.madieu.xa@bp.renesas.com>
Hi John
> RZ/G3E's ADG module requires explicit clock management for SSI audio
> interfaces that differs from R-Car Gen2/Gen3/Gen4:
>
> - Per-SSI ADG clocks (adg.ssi.N) for each SSI module
> - A shared SSIF supply clock for the SSI subsystem
>
> These clocks are acquired using optional APIs, making them transparent
> to platforms that do not require them.
>
> Additionally, since rsnd_adg_ssi_clk_try_start() is called from the
> trigger path (atomic context), clk_prepare_enable() cannot be used
> directly as clk_prepare() may sleep. Split clock handling into:
>
> - hw_params: clk_prepare() - sleepable context
> - trigger (start): clk_enable() - atomic safe
> - trigger (stop): clk_disable() - atomic safe
> - hw_free: clk_unprepare() - sleepable context
>
> Signed-off-by: John Madieu <john.madieu.xa@bp.renesas.com>
> ---
(snip)
> +int rsnd_adg_ssi_clk_prepare(struct rsnd_mod *ssi_mod)
> +{
> + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
> + struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
> + struct device *dev = rsnd_priv_to_dev(priv);
> + int id = rsnd_mod_id(ssi_mod);
> + int ret;
> +
> + ret = clk_prepare(adg->clk_adg_ssi[id]);
> + if (ret) {
> + dev_err(dev, "Cannot prepare adg.ssi.%d ADG clock\n", id);
> + return ret;
> + }
> +
> + ret = clk_prepare(adg->clk_ssif_supply);
> + if (ret) {
> + dev_err(dev, "Cannot prepare SSIF supply clock\n");
> + clk_unprepare(adg->clk_adg_ssi[id]);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * RZ/G3E: Unprepare SSI clocks - call from hw_free (can sleep)
> + */
> +void rsnd_adg_ssi_clk_unprepare(struct rsnd_mod *ssi_mod)
> +{
> + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
> + struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
> + int id = rsnd_mod_id(ssi_mod);
> +
> + clk_unprepare(adg->clk_adg_ssi[id]);
> + clk_unprepare(adg->clk_ssif_supply);
> +}
Can't we done clk_{un}prepare() at rsnd_adg_clk_control() ?
It is the function that ADG is calling clk_{un}prepare().
Performing similar processes in multiple locations makes maintenance
difficult.
Thank you for your help !!
Best regards
---
Kuninori Morimoto
^ permalink raw reply
* [PATCH] drivers/of: fdt: validate flat DT string properties before string use
From: Pengpeng Hou @ 2026-04-03 0:47 UTC (permalink / raw)
To: Rob Herring, Saravana Kannan; +Cc: devicetree, linux-kernel, pengpeng
Firmware-supplied flat DT properties are raw byte sequences. Several
early FDT helpers fetch properties such as status, model, compatible,
and device_type and then use them as C strings with strcmp(), strlen(),
or pr_info() without first proving that the property is NUL-terminated
within its declared length.
Use fdt_stringlist_get() for these string properties instead. That
preserves the existing behavior for valid DTBs while rejecting malformed
unterminated properties before they are passed to C string helpers.
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
drivers/of/fdt.c | 25 +++++++++++--------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 331646d667b9..311a258fc225 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -68,7 +68,7 @@ void __init of_fdt_limit_memory(int limit)
bool of_fdt_device_is_available(const void *blob, unsigned long node)
{
- const char *status = fdt_getprop(blob, node, "status", NULL);
+ const char *status = fdt_stringlist_get(blob, node, "status", 0, NULL);
if (!status)
return true;
@@ -741,9 +741,10 @@ const char * __init of_flat_dt_get_machine_name(void)
const char *name;
unsigned long dt_root = of_get_flat_dt_root();
- name = of_get_flat_dt_prop(dt_root, "model", NULL);
+ name = fdt_stringlist_get(initial_boot_params, dt_root, "model", 0, NULL);
if (!name)
- name = of_get_flat_dt_prop(dt_root, "compatible", NULL);
+ name = fdt_stringlist_get(initial_boot_params, dt_root,
+ "compatible", 0, NULL);
return name;
}
@@ -775,19 +776,14 @@ const void * __init of_flat_dt_match_machine(const void *default_match,
}
if (!best_data) {
const char *prop;
- int size;
+ int idx = 0, size;
pr_err("\n unrecognized device tree list:\n[ ");
- prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
- if (prop) {
- while (size > 0) {
- printk("'%s' ", prop);
- size -= strlen(prop) + 1;
- prop += strlen(prop) + 1;
- }
- }
- printk("]\n\n");
+ while ((prop = fdt_stringlist_get(initial_boot_params, dt_root,
+ "compatible", idx++, &size)))
+ pr_err("'%s' ", prop);
+ pr_err("]\n\n");
return NULL;
}
@@ -1032,7 +1028,8 @@ int __init early_init_dt_scan_memory(void)
const void *fdt = initial_boot_params;
fdt_for_each_subnode(node, fdt, 0) {
- const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+ const char *type = fdt_stringlist_get(fdt, node,
+ "device_type", 0, NULL);
const __be32 *reg;
int i, l;
bool hotpluggable;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related
* Re: [PATCH v1 05/22] dt-bindings: clock: Add StarFive JHB100 System-0 clock and reset generator
From: Changhuang Liang @ 2026-04-03 0:53 UTC (permalink / raw)
To: Philipp Zabel, Michael Turquette, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Stephen Boyd, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Emil Renner Berthing,
Kees Cook, Gustavo A . R . Silva, Richard Cochran
Cc: linux-clk@vger.kernel.org, linux-kernel@vger.kernel.org,
devicetree@vger.kernel.org, linux-riscv@lists.infradead.org,
linux-hardening@vger.kernel.org, netdev@vger.kernel.org,
JeeHeng Sia, Hal Feng, Leyfoon Tan
In-Reply-To: <2d3ef7359f63fb364cb7bc13b721132894428874.camel@pengutronix.de>
> On Do, 2026-04-02 at 03:55 -0700, Changhuang Liang wrote:
> > Add bindings for the System-0 clocks and reset generator (SYS0CRG) on
> > JHB100 SoC.
> >
> > Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
> > ---
> > .../clock/starfive,jhb100-sys0crg.yaml | 63
> +++++++++++++++++++
> > .../dt-bindings/clock/starfive,jhb100-crg.h | 56 +++++++++++++++++
> > .../dt-bindings/reset/starfive,jhb100-crg.h | 30 +++++++++
> > 3 files changed, 149 insertions(+)
> > create mode 100644
> > Documentation/devicetree/bindings/clock/starfive,jhb100-sys0crg.yaml
> > create mode 100644 include/dt-bindings/clock/starfive,jhb100-crg.h
> > create mode 100644 include/dt-bindings/reset/starfive,jhb100-crg.h
> >
> [...]
> > diff --git a/include/dt-bindings/reset/starfive,jhb100-crg.h
> > b/include/dt-bindings/reset/starfive,jhb100-crg.h
> > new file mode 100644
> > index 000000000000..71affdcdf733
> > --- /dev/null
> > +++ b/include/dt-bindings/reset/starfive,jhb100-crg.h
> > @@ -0,0 +1,30 @@
> > +/* SPDX-License-Identifier: GPL-2.0 OR MIT */
> > +/*
> > + * Copyright (C) 2024 StarFive Technology Co., Ltd.
> > + * Author: Changhuang Liang <changhuang.liang@starfivetech.com>
> > + *
> > + */
> > +
> > +#ifndef __DT_BINDINGS_RESET_STARFIVE_JHB100_CRG_H__
> > +#define __DT_BINDINGS_RESET_STARFIVE_JHB100_CRG_H__
> > +
> > +/* SYS0CRG resets */
> > +#define JHB100_SYS0RST_RESOURCE_ARB 0
>
> Where are resets 1 and 2, ...
>
> > +#define JHB100_SYS0RST_SYS0_IOMUX_PRESETN 3
> > +#define JHB100_SYS0RST_SYS0H_IOMUX_PRESETN 4
> > +#define JHB100_SYS0RST_RST_ADAPTOR_TIMEOUT_RSTN
> 5
>
> ... where are 6-13?
>
> > +
> > +#define JHB100_SYS0RST_BMCPCIERP_RSTN_BUS 14
> [...]
>
> If there are non-reset bits in these registers, please enumerate reset controls
> in a contiguous range for this binding and add a mapping table in the driver.
>
I will do this.
Best Regards,
Changhuang
^ permalink raw reply
* Re: [PATCh v3 07/14] ASoC: rsnd: ssui: Add RZ/G3E SSIU BUSIF support
From: Kuninori Morimoto @ 2026-04-03 1:18 UTC (permalink / raw)
To: Mark Brown
Cc: John Madieu, Liam Girdwood, Geert Uytterhoeven, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai,
Magnus Damm, Philipp Zabel, Claudiu Beznea, Biju Das, john.madieu,
linux-sound, linux-renesas-soc, devicetree, linux-kernel
In-Reply-To: <a457c427-95bf-43be-84db-02144fcc2bd6@sirena.org.uk>
Hi Mark
Thank you for your review
> > @@ -160,7 +161,8 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
> > /*
> > * SSI_MODE0
> > */
> > - rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id);
> > + if (!rsnd_is_rzg3e(priv))
> > + rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id);
>
> Perhaps should be a capability flag like the changelog said?
Have or not have registers are depending on the each SoC, and (probably)
there's no regularity to it.
And SoC special handling is needed not only for the register settings.
There is no clear rule on this driver for now.
Handling it by flag is one the idea, but rsnd_is_xxx() is enough for now.
Because it is easy to find SoC specific handling part by "grep rsnd_is_"
Thank you for your help !!
Best regards
---
Kuninori Morimoto
^ permalink raw reply
* Re: [PATCH v4 net-next 13/14] net: dsa: netc: initialize buffer bool table and implement flow-control
From: Jakub Kicinski @ 2026-04-03 1:17 UTC (permalink / raw)
To: wei.fang
Cc: Jakub Kicinski, claudiu.manoil, vladimir.oltean, xiaoning.wang,
andrew+netdev, davem, edumazet, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew, netdev,
linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel, imx
In-Reply-To: <20260331113025.1566878-14-wei.fang@nxp.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: dsa: netc: initialize buffer bool table and implement flow-control
This commit adds buffer pool initialization and flow control support for the
NXP netc switch. It maps frame priorities to specific buffer pools and
configures transmission pause thresholds.
> diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c
> index 3609d83ac363..688dceb486c2 100644
> --- a/drivers/net/dsa/netc/netc_main.c
> +++ b/drivers/net/dsa/netc/netc_main.c
[ ... ]
> @@ -386,6 +388,15 @@ static void netc_port_fixed_config(struct netc_port *np)
> /* Enable L2 and L3 DOS */
> netc_port_rmw(np, NETC_PCR, PCR_L2DOSE | PCR_L3DOSE,
> PCR_L2DOSE | PCR_L3DOSE);
> +
> + /* Set the quanta value of TX PAUSE frame */
> + netc_mac_port_wr(np, NETC_PM_PAUSE_QUANTA(0), pqnt);
> +
> + /* When a quanta timer counts down and reaches this value,
> + * the MAC sends a refresh PAUSE frame with the programmed
> + * full quanta value if a pause condition still exists.
> + */
> + netc_mac_port_wr(np, NETC_PM_PAUSE_TRHESH(0), qth);
This isn't a bug, but there's a typo in the macro name (TRHESH instead of
THRESH).
[ ... ]
> +static u32 netc_get_buffer_pool_num(struct netc_switch *priv)
> +{
> + /* The BPCAPR register is a read only register, the hardware
> + * guarantees that the BPCAPR_NUM_BP field will not be 0.
> + */
> + return netc_base_rd(&priv->regs, NETC_BPCAPR) & BPCAPR_NUM_BP;
> +}
[ ... ]
> +static void netc_ipv_to_buffer_pool_mapping(struct netc_switch *priv)
> +{
> + int bp_per_port = priv->num_bp / priv->info->num_ports;
> + int q = NETC_IPV_NUM / bp_per_port;
> + int r = NETC_IPV_NUM % bp_per_port;
If the hardware register read in netc_get_buffer_pool_num() returns a value
smaller than priv->info->num_ports (for instance, during a bus error or
reset state), bp_per_port will evaluate to 0.
Can this lead to a division by zero panic here?
Should priv->num_bp be validated against the number of ports before
performing this calculation to ensure the driver fails gracefully if the
hardware returns an unexpected value?
[ ... ]
> diff --git a/drivers/net/dsa/netc/netc_switch_hw.h b/drivers/net/dsa/netc/netc_switch_hw.h
> index c6a0c0a8ff8a..1e1c0d279a21 100644
> --- a/drivers/net/dsa/netc/netc_switch_hw.h
> +++ b/drivers/net/dsa/netc/netc_switch_hw.h
[ ... ]
> @@ -145,6 +152,12 @@ enum netc_stg_stage {
> #define NETC_PM_IEVENT(a) (0x1040 + (a) * 0x400)
> #define PM_IEVENT_RX_EMPTY BIT(6)
>
> +#define NETC_PM_PAUSE_QUANTA(a) (0x1054 + (a) * 0x400)
> +#define PAUSE_QUANTA_PQNT GENMASK(15, 0)
> +
> +#define NETC_PM_PAUSE_TRHESH(a) (0x1064 + (a) * 0x400)
> +#define PAUSE_TRHESH_QTH GENMASK(15, 0)
This isn't a bug, but there's a typo in the macro names here as well
(TRHESH instead of THRESH).
^ permalink raw reply
* Re: [PATCH v4 net-next 12/14] net: dsa: netc: add more basic functions support
From: Jakub Kicinski @ 2026-04-03 1:17 UTC (permalink / raw)
To: wei.fang
Cc: Jakub Kicinski, claudiu.manoil, vladimir.oltean, xiaoning.wang,
andrew+netdev, davem, edumazet, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew, netdev,
linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel, imx
In-Reply-To: <20260331113025.1566878-13-wei.fang@nxp.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: dsa: netc: add more basic functions support
This patch expands the NETC switch driver with several foundational
features, including FDB and MDB management, STP state handling, MTU
configuration, port setup/teardown, and host flooding support.
At this stage, the driver operates only in standalone port mode. Each
port uses VLAN 0 as its PVID, meaning ingress frames are internally
assigned VID 0 regardless of whether they arrive tagged or untagged.
Note that this does not inject a VLAN 0 header into the frame, the VID
is used purely for subsequent VLAN processing within the switch.
> diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c
> --- a/drivers/net/dsa/netc/netc_main.c
> +++ b/drivers/net/dsa/netc/netc_main.c
[ ... ]
> +static int netc_add_standalone_vlan_entry(struct netc_switch *priv)
> +{
> + u32 bitmap_stg = VFT_STG_ID(0) | netc_available_port_bitmap(priv);
Does putting all standalone ports into a single shared VLAN 0 break the DSA
isolation requirements?
If a user manually adds a static FDB entry to a standalone port, it is
programmed into VLAN 0. Because all standalone ports reside in this VLAN, a
frame arriving on one standalone port with a matching destination MAC might
be hardware-switched directly to another standalone port, bypassing the CPU.
[ ... ]
> +static int netc_port_set_fdb_entry(struct netc_port *np,
> + const unsigned char *addr, u16 vid)
> +{
[ ... ]
> + /* If the entry already exists, but not on this port, we need to
> + * update the port bitmap. In general, it should only be valid
> + * for multicast or broadcast address.
> + */
> + port_bitmap ^= BIT(port);
> + entry->cfge.port_bitmap = cpu_to_le32(port_bitmap);
Can this XOR operation cause multi-port floods during unicast MAC migrations?
When a unicast MAC address moves to a new port, the bridging layer issues an
ADD for the new port. The XOR operation leaves the old port bit set alongside
the new port bit. Does the driver need to differentiate between unicast and
multicast addresses and explicitly overwrite the bitmap for unicast entries?
[ ... ]
> +static int netc_port_del_fdb_entry(struct netc_port *np,
> + const unsigned char *addr, u16 vid)
> +{
> + struct netc_switch *priv = np->switch_priv;
> + struct ntmp_user *ntmp = &priv->ntmp;
> + struct netc_fdb_entry *entry;
> + int port = np->dp->index;
> + u32 port_bitmap;
> + int err = 0;
> +
> + mutex_lock(&priv->fdbt_lock);
> +
> + entry = netc_lookup_fdb_entry(priv, addr, vid);
> + if (unlikely(!entry))
> + goto unlock_fdbt;
Will this prevent dynamically learned FDB entries from being deleted?
Since the CPU port is configured with MLO_HW, the switch hardware dynamically
learns MAC addresses, but they are not added to the software priv->fdb_list.
If a user attempts to delete a dynamic entry, the software lookup will fail
here and return 0 (success), leaving the stale dynamically learned entry
active in hardware.
[ ... ]
> +static int netc_port_change_mtu(struct dsa_switch *ds,
> + int port, int mtu)
> +{
> + u32 max_frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
> + struct netc_port *np = NETC_PORT(ds, port);
> +
> + if (dsa_is_cpu_port(ds, port))
> + max_frame_size += NETC_TAG_MAX_LEN;
Does this double-count the DSA tag overhead for the CPU port?
The DSA core computes the CPU port's MTU by adding the tagger overhead before
calling the switch's .port_change_mtu() function. If the maximum MTU of 9582
bytes is requested, the CPU port receives an MTU of 9596. Adding
NETC_TAG_MAX_LEN again here results in 9628 bytes, which exceeds the
hardware limit of 9600 (NETC_MAX_FRAME_LEN) and might lead to truncation.
[ ... ]
> +static int netc_port_fdb_add(struct dsa_switch *ds, int port,
> + const unsigned char *addr, u16 vid,
> + struct dsa_db db)
> +{
> + struct netc_port *np = NETC_PORT(ds, port);
> +
> + /* Currently, we only support standalone port mode, so all VLANs
> + * should be converted to NETC_STANDALONE_PVID.
> + */
> + return netc_port_set_fdb_entry(np, addr, NETC_STANDALONE_PVID);
> +}
Is it safe to silently override user-requested VLAN IDs to VLAN 0?
If a user explicitly attempts to configure a MAC address for a specific VLAN,
the hardware is programmed for VLAN 0 and returns success. Should this
instead check if the requested vid is not 0 and not NETC_STANDALONE_PVID,
and return -EOPNOTSUPP to inform the networking stack?
[ ... ]
> +static void netc_port_set_host_flood(struct dsa_switch *ds, int port,
> + bool uc, bool mc)
> +{
> + struct netc_port *np = NETC_PORT(ds, port);
> +
> + if (np->uc == uc && np->mc == mc)
> + return;
> +
> + /* IPFT does not support in-place updates to the KEYE element,
> + * so we need to delete the old IPFT entry and then add a new
> + * one.
> + */
> + if (np->host_flood)
> + netc_port_remove_host_flood(np);
> +
> + if (netc_port_add_host_flood_rule(np, uc, mc))
> + dev_err(ds->dev, "Failed to add host flood rule on port %d\n",
> + port);
> +}
Could this permanently lose the host flood configuration if the new rule
addition fails?
If the call to netc_port_add_host_flood_rule() fails (for example, due to
-ENOMEM), the function logs an error and returns, leaving the port entirely
without a host flood rule. Should the driver revert to the previous
configuration on failure to avoid blackholing traffic meant for the CPU?
^ permalink raw reply
* Re: [PATCH v4 net-next 11/14] net: dsa: netc: add phylink MAC operations
From: Jakub Kicinski @ 2026-04-03 1:17 UTC (permalink / raw)
To: wei.fang
Cc: Jakub Kicinski, claudiu.manoil, vladimir.oltean, xiaoning.wang,
andrew+netdev, davem, edumazet, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew, netdev,
linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel, imx
In-Reply-To: <20260331113025.1566878-12-wei.fang@nxp.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: dsa: netc: add phylink MAC operations
Different versions of NETC switches have different numbers of ports and
MAC capabilities, so add .phylink_get_caps() to struct netc_switch_info.
In addition, related interfaces of struct phylink_mac_ops are added.
> diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c
> index 5828fd3e342e..f11f5d0f6a6d 100644
> --- a/drivers/net/dsa/netc/netc_main.c
> +++ b/drivers/net/dsa/netc/netc_main.c
[ ... ]
> +static void netc_port_set_mac_mode(struct netc_port *np,
> + unsigned int mode,
> + phy_interface_t phy_mode)
> +{
> + u32 mask = PM_IF_MODE_IFMODE | PM_IF_MODE_REVMII;
> + u32 val = 0;
> +
> + switch (phy_mode) {
> + case PHY_INTERFACE_MODE_RGMII:
[ ... ]
> + case PHY_INTERFACE_MODE_SGMII:
> + case PHY_INTERFACE_MODE_2500BASEX:
> + val |= IFMODE_SGMII;
> + break;
> + default:
> + break;
> + }
> +
> + netc_mac_port_rmw(np, NETC_PM_IF_MODE(0), mask, val);
> +}
In imx94_switch_phylink_get_caps(), PHY_INTERFACE_MODE_1000BASEX is
advertised as supported for ports 0 and 1. Does it need to be handled
in this switch statement? As written, it appears it will fall through
to the default case and clear the IFMODE bits, leaving the MAC in an
undefined mode.
[ ... ]
> +static void netc_port_mac_rx_enable(struct netc_port *np)
> +{
> + netc_port_rmw(np, NETC_POR, PCR_RXDIS, 0);
> + netc_mac_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_RX_EN,
> + PM_CMD_CFG_RX_EN);
> +}
Pseudo ports (like the CPU port) bypass PMAC register accesses during
graceful stop via is_netc_pseudo_port(). Should there be a similar check
here, and in netc_port_set_mac_mode(), to prevent accessing physical MAC
registers on pseudo ports?
Additionally, when np->caps.pmac is true, netc_port_mac_rx_graceful_stop()
disables the preemption MAC via NETC_PM_CMD_CFG(1). Should this function
re-enable it when bringing the link back up?
> +static void netc_port_wait_rx_empty(struct netc_port *np, int mac)
> +{
> + u32 val;
> +
> + if (read_poll_timeout(netc_port_rd, val, val & PM_IEVENT_RX_EMPTY,
> + 100, 10000, false, np, NETC_PM_IEVENT(mac)))
> + dev_warn(np->switch_priv->dev,
> + "MAC %d of swp%d RX is not empty\n", mac,
> + np->dp->index);
> +}
Is NETC_PM_IEVENT a sticky write-1-to-clear register? If the RX FIFO had
previously emptied during normal operation, could this bit already be set,
causing read_poll_timeout() to return immediately without waiting?
> +static void netc_port_mac_rx_graceful_stop(struct netc_port *np)
> +{
> + u32 val;
> +
> + if (is_netc_pseudo_port(np))
> + goto check_rx_busy;
> +
> + if (np->caps.pmac) {
> + netc_port_rmw(np, NETC_PM_CMD_CFG(1), PM_CMD_CFG_RX_EN, 0);
> + netc_port_wait_rx_empty(np, 1);
> + }
> +
> + netc_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_RX_EN, 0);
> + netc_port_wait_rx_empty(np, 0);
> +
> +check_rx_busy:
> + if (read_poll_timeout(netc_port_rd, val, !(val & PSR_RX_BUSY),
> + 100, 10000, false, np, NETC_PSR))
> + dev_warn(np->switch_priv->dev, "swp%d RX is busy\n",
> + np->dp->index);
> +
> + netc_port_rmw(np, NETC_POR, PCR_RXDIS, PCR_RXDIS);
> +}
For pseudo ports, the code jumps directly to check_rx_busy and polls for
PSR_RX_BUSY to clear. Since PCR_RXDIS is only set after the polling
completes, will the RX pipeline continue accepting traffic and cause this
poll to always timeout on an active link?
> +static void netc_mac_link_up(struct phylink_config *config,
> + struct phy_device *phy, unsigned int mode,
> + phy_interface_t interface, int speed,
> + int duplex, bool tx_pause, bool rx_pause)
> +{
> + struct dsa_port *dp = dsa_phylink_to_port(config);
> + struct netc_port *np;
> +
> + np = NETC_PORT(dp->ds, dp->index);
> + netc_port_set_speed(np, speed);
> +
> + if (phy_interface_mode_is_rgmii(interface))
> + netc_port_set_rgmii_mac(np, speed, duplex);
> +
> + if (interface == PHY_INTERFACE_MODE_RMII ||
> + interface == PHY_INTERFACE_MODE_REVMII ||
> + interface == PHY_INTERFACE_MODE_MII)
> + netc_port_set_rmii_mii_mac(np, speed, duplex);
> +
> + netc_port_mac_rx_enable(np);
> +}
The callback imx94_switch_phylink_get_caps() advertises MAC_ASYM_PAUSE and
MAC_SYM_PAUSE capabilities. Is it expected that tx_pause and rx_pause are
ignored here? If flow control is implemented in a subsequent patch, should
the advertisement be deferred until then?
> +static void netc_mac_link_down(struct phylink_config *config,
> + unsigned int mode,
> + phy_interface_t interface)
> +{
> + struct dsa_port *dp = dsa_phylink_to_port(config);
> +
> + netc_port_mac_rx_graceful_stop(NETC_PORT(dp->ds, dp->index));
> +}
This correctly stops the RX pipeline, but should the TX path
(PM_CMD_CFG_TX_EN or PCR_TXDIS) also be disabled when the link goes down?
^ 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