ASoC: fsl: fsl_qmc_audio: Reduce amount of
Merge series from Christophe Leroy <christophe.leroy@csgroup.eu>:
This is a RESEND of v3 sent one month ago, see:
https://lore.kernel.org/all/cover.1754993232.git.christophe.leroy@csgroup.eu/
This series reduces significantly the amount of interrupts on
fsl_qmc_audio device.
Patches 1 and 2 are preparatory patches.
Patch 3 is the main change
Patch 4 is a cleanup which is enabled by previous patch
diff --git a/Documentation/devicetree/bindings/mfd/twl4030-audio.txt b/Documentation/devicetree/bindings/mfd/twl4030-audio.txt
deleted file mode 100644
index 414d2ae..0000000
--- a/Documentation/devicetree/bindings/mfd/twl4030-audio.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-Texas Instruments TWL family (twl4030) audio module
-
-The audio module inside the TWL family consist of an audio codec and a vibra
-driver.
-
-Required properties:
-- compatible : must be "ti,twl4030-audio"
-
-Optional properties, nodes:
-
-Audio functionality:
-- codec { }: Need to be present if the audio functionality is used. Within this
- section the following options can be used:
-- ti,digimic_delay: Delay need after enabling the digimic to reduce artifacts
- from the start of the recorded sample (in ms)
--ti,ramp_delay_value: HS ramp delay configuration to reduce pop noise
--ti,hs_extmute: Use external mute for HS pop reduction
--ti,hs_extmute_gpio: Use external GPIO to control the external mute
--ti,offset_cncl_path: Offset cancellation path selection, refer to TRM for the
- valid values.
-
-Vibra functionality
-- ti,enable-vibra: Need to be set to <1> if the vibra functionality is used. if
- missing or it is 0, the vibra functionality is disabled.
-
-Example:
-&i2c1 {
- clock-frequency = <2600000>;
-
- twl: twl@48 {
- reg = <0x48>;
- interrupts = <7>; /* SYS_NIRQ cascaded to intc */
- interrupt-parent = <&intc>;
-
- twl_audio: audio {
- compatible = "ti,twl4030-audio";
-
- ti,enable-vibra = <1>;
-
- codec {
- ti,ramp_delay_value = <3>;
- };
-
- };
- };
-};
diff --git a/Documentation/devicetree/bindings/sound/alc5623.txt b/Documentation/devicetree/bindings/sound/alc5623.txt
deleted file mode 100644
index 26c86c9..0000000
--- a/Documentation/devicetree/bindings/sound/alc5623.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-ALC5621/ALC5622/ALC5623 audio Codec
-
-Required properties:
-
- - compatible: "realtek,alc5623"
- - reg: the I2C address of the device.
-
-Optional properties:
-
- - add-ctrl: Default register value for Reg-40h, Additional Control
- Register. If absent or has the value of 0, the
- register is untouched.
-
- - jack-det-ctrl: Default register value for Reg-5Ah, Jack Detect
- Control Register. If absent or has value 0, the
- register is untouched.
-
-Example:
-
- alc5621: alc5621@1a {
- compatible = "alc5621";
- reg = <0x1a>;
- add-ctrl = <0x3700>;
- jack-det-ctrl = <0x4810>;
- };
diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4458.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4458.yaml
index 4477f84..1fdbeec 100644
--- a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4458.yaml
+++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4458.yaml
@@ -15,6 +15,9 @@
- asahi-kasei,ak4458
- asahi-kasei,ak4497
+ "#sound-dai-cells":
+ const: 0
+
reg:
maxItems: 1
@@ -46,6 +49,7 @@
- reg
allOf:
+ - $ref: dai-common.yaml#
- if:
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.txt b/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.txt
deleted file mode 100644
index 7bb0362..0000000
--- a/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-* Broadcom BCM2835 SoC I2S/PCM module
-
-Required properties:
-- compatible: "brcm,bcm2835-i2s"
-- reg: Should contain PCM registers location and length.
-- clocks: the (PCM) clock to use
-- dmas: List of DMA controller phandle and DMA request line ordered pairs.
-- dma-names: Identifier string for each DMA request line in the dmas property.
- These strings correspond 1:1 with the ordered pairs in dmas.
-
- One of the DMA channels will be responsible for transmission (should be
- named "tx") and one for reception (should be named "rx").
-
-Example:
-
-bcm2835_i2s: i2s@7e203000 {
- compatible = "brcm,bcm2835-i2s";
- reg = <0x7e203000 0x24>;
- clocks = <&clocks BCM2835_CLOCK_PCM>;
-
- dmas = <&dma 2>,
- <&dma 3>;
- dma-names = "tx", "rx";
-};
diff --git a/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.yaml b/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.yaml
new file mode 100644
index 0000000..f3cfe92
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.yaml
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/brcm,bcm2835-i2s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Broadcom BCM2835 SoC I2S/PCM module
+
+maintainers:
+ - Florian Fainelli <florian.fainelli@broadcom.com>
+
+properties:
+ compatible:
+ const: brcm,bcm2835-i2s
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ dmas:
+ items:
+ - description: Transmission DMA controller phandle and request line.
+ - description: Reception DMA controller phandle and request line.
+
+ dma-names:
+ items:
+ - const: tx
+ - const: rx
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - dmas
+ - dma-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/bcm2835.h>
+
+ i2s@7e203000 {
+ compatible = "brcm,bcm2835-i2s";
+ reg = <0x7e203000 0x24>;
+ clocks = <&clocks BCM2835_CLOCK_PCM>;
+ dmas = <&dma 2>, <&dma 3>;
+ dma-names = "tx", "rx";
+ };
diff --git a/Documentation/devicetree/bindings/sound/everest,es8316.yaml b/Documentation/devicetree/bindings/sound/everest,es8316.yaml
index e4b2eb5..81a0215 100644
--- a/Documentation/devicetree/bindings/sound/everest,es8316.yaml
+++ b/Documentation/devicetree/bindings/sound/everest,es8316.yaml
@@ -12,6 +12,22 @@
- Matteo Martelli <matteomartelli3@gmail.com>
- Binbin Zhou <zhoubinbin@loongson.cn>
+description: |
+ Everest ES8311, ES8316 and ES8323 audio CODECs
+
+ Pins on the device (for linking into audio routes):
+
+ Outputs:
+ * LOUT: Left Analog Output
+ * ROUT: Right Analog Output
+ * MICBIAS: Microphone Bias
+
+ Inputs:
+ * MIC1P: Microphone 1 Positive Analog Input
+ * MIC1N: Microphone 1 Negative Analog Input
+ * MIC2P: Microphone 2 Positive Analog Input
+ * MIC2N: Microphone 2 Negative Analog Input
+
allOf:
- $ref: dai-common.yaml#
diff --git a/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml b/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml
new file mode 100644
index 0000000..4da7353
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/foursemi,fs2105s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: FourSemi FS2104/5S Digital Audio Amplifier
+
+maintainers:
+ - Nick Li <nick.li@foursemi.com>
+
+description:
+ The FS2104 is a 15W Inductor-Less, Stereo, Closed-Loop,
+ Digital Input Class-D Power Amplifier with Enhanced Signal Processing.
+ The FS2105S is a 30W Inductor-Less, Stereo, Closed-Loop,
+ Digital Input Class-D Power Amplifier with Enhanced Signal Processing.
+
+properties:
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ - foursemi,fs2104
+ - const: foursemi,fs2105s
+ - enum:
+ - foursemi,fs2105s
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: The clock of I2S BCLK
+
+ clock-names:
+ items:
+ - const: bclk
+
+ interrupts:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+ pvdd-supply:
+ description:
+ Regulator for power supply(PVDD in datasheet).
+
+ dvdd-supply:
+ description:
+ Regulator for digital supply(DVDD in datasheet).
+
+ reset-gpios:
+ maxItems: 1
+ description:
+ It's the SDZ pin in datasheet, the pin is active low,
+ it will power down and reset the chip to shut down state.
+
+ firmware-name:
+ maxItems: 1
+ description: |
+ The firmware(*.bin) contains:
+ a. Register initialization settings
+ b. DSP effect parameters
+ c. Multi-scene sound effect configurations(optional)
+ It's gernerated by FourSemi's tuning tool.
+
+required:
+ - compatible
+ - reg
+ - '#sound-dai-cells'
+ - pvdd-supply
+ - dvdd-supply
+ - reset-gpios
+ - firmware-name
+
+allOf:
+ - $ref: dai-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ audio-codec@68 {
+ compatible = "foursemi,fs2105s";
+ reg = <0x68>;
+ clocks = <&clocks 18>;
+ clock-names = "bclk";
+ #sound-dai-cells = <0>;
+ pvdd-supply = <&pvdd_supply>;
+ dvdd-supply = <&dvdd_supply>;
+ reset-gpios = <&gpio 18 GPIO_ACTIVE_LOW>;
+ firmware-name = "fs2105s-btl-2p0-0s.bin";
+ pinctrl-names = "default";
+ pinctrl-0 = <&fs210x_pins_default>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
index 8f1108e..d5727f8 100644
--- a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
@@ -104,6 +104,6 @@
"ctx2_rx", "ctx2_tx",
"ctx3_rx", "ctx3_tx";
firmware-name = "imx/easrc/easrc-imx8mn.bin";
- fsl,asrc-rate = <8000>;
+ fsl,asrc-rate = <8000>;
fsl,asrc-format = <2>;
};
diff --git a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
index 85799f8..c9152ba 100644
--- a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
@@ -176,7 +176,7 @@
<&sdma 20 23 1>, <&sdma 21 23 1>, <&sdma 22 23 1>;
dma-names = "rxa", "rxb", "rxc",
"txa", "txb", "txc";
- fsl,asrc-rate = <48000>;
+ fsl,asrc-rate = <48000>;
fsl,asrc-width = <16>;
port {
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt
deleted file mode 100644
index 2f89db8..0000000
--- a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-Freescale i.MX audio complex with SGTL5000 codec
-
-Required properties:
-
- - compatible : "fsl,imx-audio-sgtl5000"
-
- - model : The user-visible name of this sound complex
-
- - ssi-controller : The phandle of the i.MX SSI controller
-
- - audio-codec : The phandle of the SGTL5000 audio codec
-
- - audio-routing : A list of the connections between audio components.
- Each entry is a pair of strings, the first being the
- connection's sink, the second being the connection's
- source. Valid names could be power supplies, SGTL5000
- pins, and the jacks on the board:
-
- Power supplies:
- * Mic Bias
-
- SGTL5000 pins:
- * MIC_IN
- * LINE_IN
- * HP_OUT
- * LINE_OUT
-
- Board connectors:
- * Mic Jack
- * Line In Jack
- * Headphone Jack
- * Line Out Jack
- * Ext Spk
-
- - mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
-
- - mux-ext-port : The external port of the i.MX audio muxer
-
-Note: The AUDMUX port numbering should start at 1, which is consistent with
-hardware manual.
-
-Example:
-
-sound {
- compatible = "fsl,imx51-babbage-sgtl5000",
- "fsl,imx-audio-sgtl5000";
- model = "imx51-babbage-sgtl5000";
- ssi-controller = <&ssi1>;
- audio-codec = <&sgtl5000>;
- audio-routing =
- "MIC_IN", "Mic Jack",
- "Mic Jack", "Mic Bias",
- "Headphone Jack", "HP_OUT";
- mux-int-port = <1>;
- mux-ext-port = <3>;
-};
diff --git a/Documentation/devicetree/bindings/sound/linux,spdif.yaml b/Documentation/devicetree/bindings/sound/linux,spdif.yaml
index 0f4893e..aea6230 100644
--- a/Documentation/devicetree/bindings/sound/linux,spdif.yaml
+++ b/Documentation/devicetree/bindings/sound/linux,spdif.yaml
@@ -23,6 +23,9 @@
sound-name-prefix: true
+ port:
+ $ref: /schemas/graph.yaml#/properties/port
+
required:
- "#sound-dai-cells"
- compatible
diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8825.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8825.yaml
index a54f194..4ebbcb4 100644
--- a/Documentation/devicetree/bindings/sound/nuvoton,nau8825.yaml
+++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8825.yaml
@@ -9,6 +9,20 @@
maintainers:
- John Hsu <KCHSU0@nuvoton.com>
+description: |
+ NAU8825 audio CODEC
+
+ Pins on the device (for linking into audio routes):
+
+ Outputs:
+ * HPOL : Headphone Left Output
+ * HPOR : Headphone Right Output
+ * MICBIAS : Microphone Bias Output
+
+ Inputs:
+ * MICP : Analog Microphone Positive Input
+ * MICN : Analog Microphone Negative Input
+
allOf:
- $ref: dai-common.yaml#
diff --git a/Documentation/devicetree/bindings/sound/omap-twl4030.txt b/Documentation/devicetree/bindings/sound/omap-twl4030.txt
deleted file mode 100644
index f6a715e..0000000
--- a/Documentation/devicetree/bindings/sound/omap-twl4030.txt
+++ /dev/null
@@ -1,62 +0,0 @@
-* Texas Instruments SoC with twl4030 based audio setups
-
-Required properties:
-- compatible: "ti,omap-twl4030"
-- ti,model: Name of the sound card (for example "omap3beagle")
-- ti,mcbsp: phandle for the McBSP node
-
-Optional properties:
-- ti,codec: phandle for the twl4030 audio node
-- ti,mcbsp-voice: phandle for the McBSP node connected to the voice port of twl
-- ti, jack-det-gpio: Jack detect GPIO
-- ti,audio-routing: List of connections between audio components.
- Each entry is a pair of strings, the first being the connection's sink,
- the second being the connection's source.
- If the routing is not provided all possible connection will be available
-
-Available audio endpoints for the audio-routing table:
-
-Board connectors:
- * Headset Stereophone
- * Earpiece Spk
- * Handsfree Spk
- * Ext Spk
- * Main Mic
- * Sub Mic
- * Headset Mic
- * Carkit Mic
- * Digital0 Mic
- * Digital1 Mic
- * Line In
-
-twl4030 pins:
- * HSOL
- * HSOR
- * EARPIECE
- * HFL
- * HFR
- * PREDRIVEL
- * PREDRIVER
- * CARKITL
- * CARKITR
- * MAINMIC
- * SUBMIC
- * HSMIC
- * DIGIMIC0
- * DIGIMIC1
- * CARKITMIC
- * AUXL
- * AUXR
-
- * Headset Mic Bias
- * Mic Bias 1 /* Used for Main Mic or Digimic0 */
- * Mic Bias 2 /* Used for Sub Mic or Digimic1 */
-
-Example:
-
-sound {
- compatible = "ti,omap-twl4030";
- ti,model = "omap3beagle";
-
- ti,mcbsp = <&mcbsp2>;
-};
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
index dd549db..1c0d78a 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
@@ -20,6 +20,7 @@
- qcom,sc8280xp-lpass-va-macro
- items:
- enum:
+ - qcom,glymur-lpass-va-macro
- qcom,sm8650-lpass-va-macro
- qcom,sm8750-lpass-va-macro
- qcom,x1e80100-lpass-va-macro
@@ -79,12 +80,25 @@
compatible:
contains:
const: qcom,sc7280-lpass-va-macro
+
then:
- properties:
- clocks:
- maxItems: 1
- clock-names:
- maxItems: 1
+ if:
+ required:
+ - power-domains
+ then:
+ properties:
+ clocks:
+ maxItems: 1
+ clock-names:
+ maxItems: 1
+ else:
+ properties:
+ clocks:
+ minItems: 3
+ maxItems: 3
+ clock-names:
+ minItems: 3
+ maxItems: 3
- if:
properties:
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
index 9082e36..b6f5ba5 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
@@ -20,6 +20,7 @@
- qcom,sc8280xp-lpass-wsa-macro
- items:
- enum:
+ - qcom,glymur-lpass-wsa-macro
- qcom,sm8650-lpass-wsa-macro
- qcom,sm8750-lpass-wsa-macro
- qcom,x1e80100-lpass-wsa-macro
diff --git a/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.yaml b/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.yaml
new file mode 100644
index 0000000..6e2f103
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.yaml
@@ -0,0 +1,134 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,pm4125-codec.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm PM4125 Audio Codec
+
+maintainers:
+ - Alexey Klimov <alexey.klimov@linaro.org>
+
+description:
+ The audio codec IC found on Qualcomm PM4125/PM2250 PMIC.
+ It has RX and TX Soundwire slave devices.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: qcom,pm4125-codec
+
+ reg:
+ description:
+ Specifies the SPMI base address for the audio codec peripherals. The
+ address space contains reset register needed to power-on the codec.
+ maxItems: 1
+
+ reg-names:
+ maxItems: 1
+
+ vdd-io-supply:
+ description: A reference to the 1.8V I/O supply
+
+ vdd-cp-supply:
+ description: A reference to the charge pump I/O supply
+
+ vdd-mic-bias-supply:
+ description: A reference to the 3.3V mic bias supply
+
+ vdd-pa-vpos-supply:
+ description: A reference to the PA VPOS supply
+
+ qcom,tx-device:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description: A reference to Soundwire tx device phandle
+
+ qcom,rx-device:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description: A reference to Soundwire rx device phandle
+
+ qcom,micbias1-microvolt:
+ description: micbias1 voltage
+ minimum: 1800000
+ maximum: 2850000
+
+ qcom,micbias2-microvolt:
+ description: micbias2 voltage
+ minimum: 1800000
+ maximum: 2850000
+
+ qcom,micbias3-microvolt:
+ description: micbias3 voltage
+ minimum: 1800000
+ maximum: 2850000
+
+ qcom,mbhc-buttons-vthreshold-microvolt:
+ description:
+ Array of 8 Voltage threshold values corresponding to headset
+ button0 - button7
+ minItems: 8
+ maxItems: 8
+
+ '#sound-dai-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - vdd-io-supply
+ - vdd-cp-supply
+ - vdd-mic-bias-supply
+ - vdd-pa-vpos-supply
+ - qcom,tx-device
+ - qcom,rx-device
+ - qcom,micbias1-microvolt
+ - qcom,micbias2-microvolt
+ - qcom,micbias3-microvolt
+ - '#sound-dai-cells'
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/spmi/spmi.h>
+
+ spmi {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pmic {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec@f000 {
+ compatible = "qcom,pm4125-codec";
+ reg = <0xf000>;
+ vdd-io-supply = <&pm4125_l15>;
+ vdd-cp-supply = <&pm4125_s4>;
+ vdd-pa-vpos-supply = <&pm4125_s4>;
+ vdd-mic-bias-supply = <&pm4125_l22>;
+ qcom,micbias1-microvolt = <1800000>;
+ qcom,micbias2-microvolt = <1800000>;
+ qcom,micbias3-microvolt = <1800000>;
+ qcom,rx-device = <&pm4125_rx>;
+ qcom,tx-device = <&pm4125_tx>;
+ #sound-dai-cells = <1>;
+ };
+ };
+ };
+
+ /* ... */
+
+ soundwire@a610000 {
+ reg = <0x0a610000 0x2000>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ pm4125_rx: audio-codec@0,4 {
+ compatible = "sdw20217010c00";
+ reg = <0 4>;
+ qcom,rx-port-mapping = <1 3>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml b/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml
new file mode 100644
index 0000000..23624f3
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,pm4125-sdw.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm SoundWire Slave devices on PM4125/PM2250 PMIC audio codec.
+
+maintainers:
+ - Alexey Klimov <alexey.klimov@linaro.org>
+
+description:
+ The audio codec IC found on Qualcomm PM4125/PM2250 PMICs.
+ It has RX and TX Soundwire slave devices.
+
+properties:
+ compatible:
+ const: sdw20217010c00
+
+ reg:
+ maxItems: 1
+
+ qcom,tx-port-mapping:
+ description: |
+ Specifies static port mapping between device and host tx ports.
+ In the order of the device port index which are adc1_port, adc23_port,
+ dmic03_mbhc_port, dmic46_port.
+ Supports maximum 2 tx soundwire ports.
+
+ PM4125 TX Port 1 (ADC1,2 & DMIC0 & MBHC) <=> SWR0 Port 1
+ PM4125 TX Port 2 (ADC1 & DMIC0,1,2 & MBHC) <=> SWR0 Port 2
+
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 2
+ maxItems: 2
+ items:
+ enum: [1, 2, 3, 4]
+
+ qcom,rx-port-mapping:
+ description: |
+ Specifies static port mapping between device and host rx ports.
+ In the order of device port index which are hph_port, clsh_port,
+ comp_port, lo_port, dsd port.
+ Supports maximum 2 rx soundwire ports.
+
+ PM4125 RX Port 1 (HPH_L/R) <==> SWR1 Port 1 (HPH_L/R)
+ PM4125 RX Port 2 (COMP_L/R) <==> SWR1 Port 3 (COMP_L/R)
+
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 2
+ maxItems: 2
+ items:
+ enum: [1, 2, 3, 4, 5]
+
+required:
+ - compatible
+ - reg
+
+oneOf:
+ - required:
+ - qcom,tx-port-mapping
+ - required:
+ - qcom,rx-port-mapping
+
+additionalProperties: false
+
+examples:
+ - |
+ soundwire@a610000 {
+ reg = <0x0a610000 0x2000>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ pm4125_rx: codec@0,1 {
+ compatible = "sdw20217010c00";
+ reg = <0 1>;
+ qcom,rx-port-mapping = <1 3>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
index 5d3dbb6..8ac9162 100644
--- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
@@ -31,6 +31,7 @@
- fairphone,fp4-sndcard
- fairphone,fp5-sndcard
- qcom,apq8096-sndcard
+ - qcom,glymur-sndcard
- qcom,qcm6490-idp-sndcard
- qcom,qcs6490-rb3gen2-sndcard
- qcom,qcs8275-sndcard
diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml
index 14d312f..098f1df 100644
--- a/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml
@@ -29,6 +29,10 @@
description: GPIO spec for Powerdown/Shutdown line to use (pin SD_N)
maxItems: 1
+ reset-gpios:
+ description: Powerdown/Shutdown line to use (pin SD_N)
+ maxItems: 1
+
vdd-supply:
description: VDD Supply for the Codec
@@ -50,10 +54,15 @@
- compatible
- reg
- vdd-supply
- - powerdown-gpios
- "#thermal-sensor-cells"
- "#sound-dai-cells"
+oneOf:
+ - required:
+ - powerdown-gpios
+ - required:
+ - reset-gpios
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/sound/realtek,alc5623.yaml b/Documentation/devicetree/bindings/sound/realtek,alc5623.yaml
new file mode 100644
index 0000000..683c58c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/realtek,alc5623.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/realtek,alc5623.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ALC5621/ALC5623 Audio Codec
+
+maintainers:
+ - Mahdi Khosravi <mmk1776@gmail.com>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - realtek,alc5621
+ - realtek,alc5623
+
+ reg:
+ maxItems: 1
+
+ add-ctrl:
+ description:
+ Default register value for Reg-40h, Additional Control Register.
+ If absent or zero, the register is left untouched.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ jack-det-ctrl:
+ description:
+ Default register value for Reg-5Ah, Jack Detect Control Register.
+ If absent or zero, the register is left untouched.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ codec@1a {
+ compatible = "realtek,alc5623";
+ reg = <0x1a>;
+ add-ctrl = <0x3700>;
+ jack-det-ctrl = <0x4810>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/ti,omap-twl4030.yaml b/Documentation/devicetree/bindings/sound/ti,omap-twl4030.yaml
new file mode 100644
index 0000000..27c7019
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,omap-twl4030.yaml
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,omap-twl4030.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments SoC with twl4030 based audio setups
+
+maintainers:
+ - Peter Ujfalusi <peter.ujfalusi@gmail.com>
+
+description:
+ Audio setups on TI OMAP SoCs using TWL4030-family
+ audio codec connected via a McBSP port.
+
+properties:
+ compatible:
+ const: ti,omap-twl4030
+
+ ti,model:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: Name of the sound card (for example "omap3beagle").
+
+ ti,mcbsp:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: phandle for the McBSP node.
+
+ ti,codec:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: phandle for the twl4030 audio node.
+
+ ti,mcbsp-voice:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: phandle to the McBSP node connected to the voice port.
+
+ ti,jack-det-gpio:
+ description: GPIO specifier for jack detection.
+ maxItems: 1
+
+ ti,audio-routing:
+ description: |
+ A list of audio routing connections. Each entry is a pair of strings,
+ with the first being the connection's sink and the second being the
+ source. If not provided, all possible connections are available.
+
+ $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+ items:
+ enum:
+ # Board Connectors
+ - Headset Stereophone
+ - Earpiece Spk
+ - Handsfree Spk
+ - Ext Spk
+ - Main Mic
+ - Sub Mic
+ - Headset Mic
+ - Carkit Mic
+ - Digital0 Mic
+ - Digital1 Mic
+ - Line In
+
+ # CODEC Pins
+ - HSOL
+ - HSOR
+ - EARPIECE
+ - HFL
+ - HFR
+ - PREDRIVEL
+ - PREDRIVER
+ - CARKITL
+ - CARKITR
+ - MAINMIC
+ - SUBMIC
+ - HSMIC
+ - DIGIMIC0
+ - DIGIMIC1
+ - CARKITMIC
+ - AUXL
+ - AUXR
+
+ # Headset Mic Bias
+ - Mic Bias 1 # Used for Main Mic or Digimic0
+ - Mic Bias 2 # Used for Sub Mic or Digimic1
+
+required:
+ - compatible
+ - ti,model
+ - ti,mcbsp
+
+additionalProperties: false
+
+examples:
+ - |
+ sound {
+ compatible = "ti,omap-twl4030";
+ ti,model = "omap3beagle";
+ ti,mcbsp = <&mcbsp2>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/ti,pcm1754.yaml b/Documentation/devicetree/bindings/sound/ti,pcm1754.yaml
new file mode 100644
index 0000000..a757f73
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,pcm1754.yaml
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,pcm1754.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments PCM1754 Stereo DAC
+
+description:
+ The PCM1754 is a simple stereo DAC that is controlled via hardware gpios.
+
+maintainers:
+ - Stefan Kerkmann <s.kerkmann@pengutronix.de>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - ti,pcm1754
+
+ vcc-supply: true
+
+ '#sound-dai-cells':
+ const: 0
+
+ format-gpios:
+ maxItems: 1
+ description:
+ GPIO used to select the PCM format
+
+ mute-gpios:
+ maxItems: 1
+ description:
+ GPIO used to mute all outputs
+
+required:
+ - compatible
+ - '#sound-dai-cells'
+ - vcc-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ codec {
+ compatible = "ti,pcm1754";
+ #sound-dai-cells = <0>;
+
+ vcc-supply = <&vcc_reg>;
+ mute-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
+ format-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/ti,twl4030-audio.yaml b/Documentation/devicetree/bindings/sound/ti,twl4030-audio.yaml
new file mode 100644
index 0000000..c9c3f75
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,twl4030-audio.yaml
@@ -0,0 +1,90 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,twl4030-audio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TWL4030-family Audio Module
+
+maintainers:
+ - Peter Ujfalusi <peter.ujfalusi@gmail.com>
+
+description:
+ The audio module within the TWL4030-family of companion chips consists
+ of an audio codec and a vibra driver. This binding describes the parent
+ node for these functions.
+
+properties:
+ compatible:
+ const: ti,twl4030-audio
+
+ codec:
+ type: object
+ description: Node containing properties for the audio codec functionality.
+
+ properties:
+ ti,digimic_delay:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Delay in milliseconds after enabling digital microphones to reduce
+ artifacts.
+
+ ti,ramp_delay_value:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Headset ramp delay configuration to reduce pop noise.
+
+ ti,hs_extmute:
+ type: boolean
+ description:
+ Enable the use of an external mute for headset pop reduction.
+
+ ti,hs_extmute_gpio:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description:
+ The GPIO specifier for the external mute control.
+ maxItems: 1
+
+ ti,offset_cncl_path:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Offset cancellation path selection. Refer to the Technical
+ Reference Manual for valid values.
+
+ # The 'codec' node itself is optional, but if it exists, it can be empty.
+ # We don't require any of its sub-properties.
+
+ ti,enable-vibra:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ description:
+ Enable or disable the vibra functionality.
+
+additionalProperties: false
+
+required:
+ - compatible
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ twl: twl@48 {
+ reg = <0x48>;
+ interrupts = <7>; /* SYS_NIRQ cascaded to intc */
+ interrupt-parent = <&intc>;
+
+ twl_audio: audio {
+ compatible = "ti,twl4030-audio";
+
+ ti,enable-vibra = <1>;
+
+ codec {
+ ti,ramp_delay_value = <3>;
+ };
+
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml
index 3c2b979..c8c786c 100644
--- a/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml
+++ b/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml
@@ -9,6 +9,28 @@
maintainers:
- patches@opensource.cirrus.com
+description: |
+ Wolfson WM8960 audio codec
+
+ Pins on the device (for linking into audio routes):
+
+ Outputs:
+ * HP_L : Left Headphone/Line Output
+ * HP_R : Right Headphone/Line Output
+ * SPK_LP : Left Speaker Output (Positive)
+ * SPK_LN : Left Speaker Output (Negative)
+ * SPK_RP : Right Speaker Output (Positive)
+ * SPK_RN : Right Speaker Output (Negative)
+ * OUT3 : Mono, Left, Right or buffered midrail output for capless mode
+
+ Inputs:
+ * LINPUT1 : Left single-ended or negative differential microphone input
+ * RINPUT1 : Right single-ended or negative differential microphone input
+ * LINPUT2 : Left line input or positive differential microphone input
+ * RINPUT2 : Right line input or positive differential microphone input
+ * LINPUT3 : Left line input, positive differential microphone, or Jack Detect 2
+ * RINPUT3 : Right line input, positive differential microphone, or Jack Detect 3
+
properties:
compatible:
const: wlf,wm8960
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 9ec8947..dc534d7 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -554,6 +554,8 @@
description: FocalTech Systems Co.,Ltd
"^forlinx,.*":
description: Baoding Forlinx Embedded Technology Co., Ltd.
+ "^foursemi,.*":
+ description: Shanghai FourSemi Semiconductor Co.,Ltd.
"^freebox,.*":
description: Freebox SAS
"^freecom,.*":
diff --git a/Documentation/sound/soc/codec.rst b/Documentation/sound/soc/codec.rst
index af973c4..b9d87a4 100644
--- a/Documentation/sound/soc/codec.rst
+++ b/Documentation/sound/soc/codec.rst
@@ -131,8 +131,8 @@
int (*prepare)(struct snd_pcm_substream *);
};
-Please refer to the ALSA driver PCM documentation for details.
-https://www.kernel.org/doc/html/latest/sound/kernel-api/writing-an-alsa-driver.html
+Please refer to the :doc:`ALSA driver PCM documentation
+<../kernel-api/writing-an-alsa-driver>` for details.
DAPM description
diff --git a/Documentation/sound/soc/platform.rst b/Documentation/sound/soc/platform.rst
index 7036630..bd21d0a 100644
--- a/Documentation/sound/soc/platform.rst
+++ b/Documentation/sound/soc/platform.rst
@@ -45,8 +45,8 @@
...
};
-Please refer to the ALSA driver documentation for details of audio DMA.
-https://www.kernel.org/doc/html/latest/sound/kernel-api/writing-an-alsa-driver.html
+Please refer to the :doc:`ALSA driver documentation
+<../kernel-api/writing-an-alsa-driver>` for details of audio DMA.
An example DMA driver is soc/pxa/pxa2xx-pcm.c
diff --git a/MAINTAINERS b/MAINTAINERS
index f620696..2356c46 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9592,6 +9592,14 @@
K: \bunsafe_memcpy\b
K: \b__NO_FORTIFY\b
+FOURSEMI AUDIO AMPLIFIER DRIVER
+M: Nick Li <nick.li@foursemi.com>
+L: linux-sound@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml
+F: sound/soc/codecs/fs-amp-lib.*
+F: sound/soc/codecs/fs210x.*
+
FPGA DFL DRIVERS
M: Xu Yilun <yilun.xu@intel.com>
R: Tom Rix <trix@redhat.com>
@@ -20470,6 +20478,8 @@
F: sound/soc/codecs/lpass-*.*
F: sound/soc/codecs/msm8916-wcd-analog.c
F: sound/soc/codecs/msm8916-wcd-digital.c
+F: sound/soc/codecs/pm4125-sdw.c
+F: sound/soc/codecs/pm4125.*
F: sound/soc/codecs/wcd-clsh-v2.*
F: sound/soc/codecs/wcd-mbhc-v2.*
F: sound/soc/codecs/wcd93*.*
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index 4fd5cac..55c1db8 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -1360,6 +1360,18 @@ int sdw_slave_get_scale_index(struct sdw_slave *slave, u8 *base)
}
EXPORT_SYMBOL(sdw_slave_get_scale_index);
+int sdw_slave_get_current_bank(struct sdw_slave *slave)
+{
+ int tmp;
+
+ tmp = sdw_read(slave, SDW_SCP_CTRL);
+ if (tmp < 0)
+ return tmp;
+
+ return FIELD_GET(SDW_SCP_STAT_CURR_BANK, tmp);
+}
+EXPORT_SYMBOL_GPL(sdw_slave_get_current_bank);
+
static int sdw_slave_set_frequency(struct sdw_slave *slave)
{
int scale_index;
diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c
index d2d9955..3d4d001 100644
--- a/drivers/soundwire/slave.c
+++ b/drivers/soundwire/slave.c
@@ -273,4 +273,10 @@ int sdw_of_find_slaves(struct sdw_bus *bus)
return 0;
}
+struct device *of_sdw_find_device_by_node(struct device_node *np)
+{
+ return bus_find_device_by_of_node(&sdw_bus_type, np);
+}
+EXPORT_SYMBOL_GPL(of_sdw_find_device_by_node);
+
MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index 0832776..e6a3476b 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -19,6 +19,7 @@
struct dentry;
struct fwnode_handle;
+struct device_node;
struct sdw_bus;
struct sdw_slave;
@@ -1086,6 +1087,10 @@ int sdw_stream_add_slave(struct sdw_slave *slave,
int sdw_stream_remove_slave(struct sdw_slave *slave,
struct sdw_stream_runtime *stream);
+struct device *of_sdw_find_device_by_node(struct device_node *np);
+
+int sdw_slave_get_current_bank(struct sdw_slave *sdev);
+
int sdw_slave_get_scale_index(struct sdw_slave *slave, u8 *base);
/* messaging and data APIs */
@@ -1119,6 +1124,18 @@ static inline int sdw_stream_remove_slave(struct sdw_slave *slave,
return -EINVAL;
}
+static inline struct device *of_sdw_find_device_by_node(struct device_node *np)
+{
+ WARN_ONCE(1, "SoundWire API is disabled");
+ return NULL;
+}
+
+static inline int sdw_slave_get_current_bank(struct sdw_slave *sdev)
+{
+ WARN_ONCE(1, "SoundWire API is disabled");
+ return -EINVAL;
+}
+
/* messaging and data APIs */
static inline int sdw_read(struct sdw_slave *slave, u32 addr)
{
diff --git a/include/sound/cs-amp-lib.h b/include/sound/cs-amp-lib.h
index 5459c22..43a87a3 100644
--- a/include/sound/cs-amp-lib.h
+++ b/include/sound/cs-amp-lib.h
@@ -49,6 +49,7 @@ int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
const struct cirrus_amp_cal_data *data);
int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
struct cirrus_amp_cal_data *out_data);
+int cs_amp_get_vendor_spkid(struct device *dev);
struct cs_amp_test_hooks {
efi_status_t (*get_efi_variable)(efi_char16_t *name,
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h
index 1ef13bc..9472f0a 100644
--- a/include/sound/dmaengine_pcm.h
+++ b/include/sound/dmaengine_pcm.h
@@ -69,6 +69,10 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
* @peripheral_config: peripheral configuration for programming peripheral
* for dmaengine transfer
* @peripheral_size: peripheral configuration buffer size
+ * @port_window_size: The length of the register area in words the data need
+ * to be accessed on the device side. It is only used for devices which is using
+ * an area instead of a single register to send/receive the data. Typically the
+ * DMA loops in this area in order to transfer the data.
*/
struct snd_dmaengine_dai_dma_data {
dma_addr_t addr;
@@ -80,6 +84,7 @@ struct snd_dmaengine_dai_dma_data {
unsigned int flags;
void *peripheral_config;
size_t peripheral_size;
+ u32 port_window_size;
};
void snd_dmaengine_pcm_set_config_from_dai_data(
diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h
index 2caa807..b954f34 100644
--- a/include/sound/soc-component.h
+++ b/include/sound/soc-component.h
@@ -261,89 +261,18 @@ struct snd_soc_component {
list_for_each_entry_safe(dai, _dai, &(component)->dai_list, list)
/**
- * snd_soc_dapm_to_component() - Casts a DAPM context to the component it is
- * embedded in
- * @dapm: The DAPM context to cast to the component
- *
- * This function must only be used on DAPM contexts that are known to be part of
- * a component (e.g. in a component driver). Otherwise the behavior is
- * undefined.
- */
-static inline struct snd_soc_component *snd_soc_dapm_to_component(
- struct snd_soc_dapm_context *dapm)
-{
- return container_of(dapm, struct snd_soc_component, dapm);
-}
-
-/**
- * snd_soc_component_get_dapm() - Returns the DAPM context associated with a
+ * snd_soc_component_to_dapm() - Returns the DAPM context associated with a
* component
* @component: The component for which to get the DAPM context
*/
-static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm(
+static inline struct snd_soc_dapm_context *snd_soc_component_to_dapm(
struct snd_soc_component *component)
{
return &component->dapm;
}
-/**
- * snd_soc_component_init_bias_level() - Initialize COMPONENT DAPM bias level
- * @component: The COMPONENT for which to initialize the DAPM bias level
- * @level: The DAPM level to initialize to
- *
- * Initializes the COMPONENT DAPM bias level. See snd_soc_dapm_init_bias_level()
- */
-static inline void
-snd_soc_component_init_bias_level(struct snd_soc_component *component,
- enum snd_soc_bias_level level)
-{
- snd_soc_dapm_init_bias_level(
- snd_soc_component_get_dapm(component), level);
-}
-
-/**
- * snd_soc_component_get_bias_level() - Get current COMPONENT DAPM bias level
- * @component: The COMPONENT for which to get the DAPM bias level
- *
- * Returns: The current DAPM bias level of the COMPONENT.
- */
-static inline enum snd_soc_bias_level
-snd_soc_component_get_bias_level(struct snd_soc_component *component)
-{
- return snd_soc_dapm_get_bias_level(
- snd_soc_component_get_dapm(component));
-}
-
-/**
- * snd_soc_component_force_bias_level() - Set the COMPONENT DAPM bias level
- * @component: The COMPONENT for which to set the level
- * @level: The level to set to
- *
- * Forces the COMPONENT bias level to a specific state. See
- * snd_soc_dapm_force_bias_level().
- */
-static inline int
-snd_soc_component_force_bias_level(struct snd_soc_component *component,
- enum snd_soc_bias_level level)
-{
- return snd_soc_dapm_force_bias_level(
- snd_soc_component_get_dapm(component),
- level);
-}
-
-/**
- * snd_soc_dapm_kcontrol_component() - Returns the component associated to a
- * kcontrol
- * @kcontrol: The kcontrol
- *
- * This function must only be used on DAPM contexts that are known to be part of
- * a COMPONENT (e.g. in a COMPONENT driver). Otherwise the behavior is undefined
- */
-static inline struct snd_soc_component *snd_soc_dapm_kcontrol_component(
- struct snd_kcontrol *kcontrol)
-{
- return snd_soc_dapm_to_component(snd_soc_dapm_kcontrol_dapm(kcontrol));
-}
+// FIXME
+#define snd_soc_component_get_dapm snd_soc_component_to_dapm
/**
* snd_soc_component_cache_sync() - Sync the register cache with the hardware
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 0b5c7e6..7594132 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -583,11 +583,9 @@ struct snd_soc_dapm_update {
struct snd_soc_dapm_context {
enum snd_soc_bias_level bias_level;
- /* bit field */
- unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
- unsigned int suspend_bias_off:1; /* Use BIAS_OFF in suspend if the DAPM is idle */
+ bool idle_bias; /* Use BIAS_OFF instead of STANDBY when false */
- struct device *dev; /* from parent - for debug */
+ struct device *dev; /* from parent - for debug */ /* REMOVE ME */
struct snd_soc_component *component; /* parent component */
struct snd_soc_card *card; /* parent card */
@@ -660,6 +658,12 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card);
int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai);
int snd_soc_dapm_widget_name_cmp(struct snd_soc_dapm_widget *widget, const char *s);
+struct device *snd_soc_dapm_to_dev(struct snd_soc_dapm_context *dapm);
+struct snd_soc_card *snd_soc_dapm_to_card(struct snd_soc_dapm_context *dapm);
+struct snd_soc_component *snd_soc_dapm_to_component(struct snd_soc_dapm_context *dapm);
+
+bool snd_soc_dapm_get_idle_bias(struct snd_soc_dapm_context *dapm);
+void snd_soc_dapm_set_idle_bias(struct snd_soc_dapm_context *dapm, bool on);
/* dapm path setup */
int snd_soc_dapm_new_widgets(struct snd_soc_card *card);
@@ -699,7 +703,6 @@ int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm);
int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin);
int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin);
int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin);
-unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol);
void snd_soc_dapm_mark_endpoints_dirty(struct snd_soc_card *card);
/*
@@ -718,10 +721,23 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, enum snd_soc_dapm_direction));
void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list);
-struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(struct snd_kcontrol *kcontrol);
-struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(struct snd_kcontrol *kcontrol);
+struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_to_dapm(struct snd_kcontrol *kcontrol);
+struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_to_widget(struct snd_kcontrol *kcontrol);
+struct snd_soc_component *snd_soc_dapm_kcontrol_to_component(struct snd_kcontrol *kcontrol);
+unsigned int snd_soc_dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol);
int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level);
+enum snd_soc_bias_level snd_soc_dapm_get_bias_level(struct snd_soc_dapm_context *dapm);
+void snd_soc_dapm_init_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level);
+
+// REMOVE ME !!
+#define snd_soc_component_force_bias_level(c, l) snd_soc_dapm_force_bias_level(&(c)->dapm, l)
+#define snd_soc_component_get_bias_level(c) snd_soc_dapm_get_bias_level(&(c)->dapm)
+#define snd_soc_component_init_bias_level(c, l) snd_soc_dapm_init_bias_level(&(c)->dapm, l)
+#define snd_soc_dapm_kcontrol_widget snd_soc_dapm_kcontrol_to_widget
+#define snd_soc_dapm_kcontrol_dapm snd_soc_dapm_kcontrol_to_dapm
+#define dapm_kcontrol_get_value snd_soc_dapm_kcontrol_get_value
+#define snd_soc_dapm_kcontrol_component snd_soc_dapm_kcontrol_to_component
#define for_each_dapm_widgets(list, i, widget) \
for ((i) = 0; \
@@ -729,37 +745,6 @@ int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_so
(i)++)
/**
- * snd_soc_dapm_init_bias_level() - Initialize DAPM bias level
- * @dapm: The DAPM context to initialize
- * @level: The DAPM level to initialize to
- *
- * This function only sets the driver internal state of the DAPM level and will
- * not modify the state of the device. Hence it should not be used during normal
- * operation, but only to synchronize the internal state to the device state.
- * E.g. during driver probe to set the DAPM level to the one corresponding with
- * the power-on reset state of the device.
- *
- * To change the DAPM state of the device use snd_soc_dapm_set_bias_level().
- */
-static inline void snd_soc_dapm_init_bias_level(
- struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
-{
- dapm->bias_level = level;
-}
-
-/**
- * snd_soc_dapm_get_bias_level() - Get current DAPM bias level
- * @dapm: The context for which to get the bias level
- *
- * Returns: The current bias level of the passed DAPM context.
- */
-static inline enum snd_soc_bias_level snd_soc_dapm_get_bias_level(
- struct snd_soc_dapm_context *dapm)
-{
- return dapm->bias_level;
-}
-
-/**
* snd_soc_dapm_widget_for_each_path - Iterates over all paths in the
* specified direction of a widget
* @w: The widget
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 1fffef3..ddc508f 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1120,6 +1120,11 @@ static inline int snd_soc_card_is_instantiated(struct snd_soc_card *card)
return card && card->instantiated;
}
+static inline struct snd_soc_dapm_context *snd_soc_card_to_dapm(struct snd_soc_card *card)
+{
+ return &card->dapm;
+}
+
/* SoC machine DAI configuration, glues a codec and cpu DAI together */
struct snd_soc_pcm_runtime {
struct device *dev;
diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
index e85c7afd..15fac53 100644
--- a/include/sound/sof/ipc4/header.h
+++ b/include/sound/sof/ipc4/header.h
@@ -326,10 +326,14 @@ struct sof_ipc4_base_module_cfg {
#define SOF_IPC4_MOD_INSTANCE_SHIFT 16
#define SOF_IPC4_MOD_INSTANCE_MASK GENMASK(23, 16)
#define SOF_IPC4_MOD_INSTANCE(x) ((x) << SOF_IPC4_MOD_INSTANCE_SHIFT)
+#define SOF_IPC4_MOD_INSTANCE_GET(x) (((x) & SOF_IPC4_MOD_INSTANCE_MASK) \
+ >> SOF_IPC4_MOD_INSTANCE_SHIFT)
#define SOF_IPC4_MOD_ID_SHIFT 0
#define SOF_IPC4_MOD_ID_MASK GENMASK(15, 0)
#define SOF_IPC4_MOD_ID(x) ((x) << SOF_IPC4_MOD_ID_SHIFT)
+#define SOF_IPC4_MOD_ID_GET(x) (((x) & SOF_IPC4_MOD_ID_MASK) \
+ >> SOF_IPC4_MOD_ID_SHIFT)
/* init module ipc msg */
#define SOF_IPC4_MOD_EXT_PARAM_SIZE_SHIFT 0
diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h
index c3a9efa..a21f34c 100644
--- a/include/sound/tas2781-dsp.h
+++ b/include/sound/tas2781-dsp.h
@@ -198,6 +198,14 @@ struct tasdevice_rca {
int ncfgs;
struct tasdevice_config_info **cfg_info;
int profile_cfg_id;
+ /*
+ * Since version 0x105, the keyword 'init' was introduced into the
+ * profile, which is used for chip initialization, particularly to
+ * store common settings for other non-initialization profiles.
+ * if (init_profile_id < 0)
+ * No init profile inside the RCA firmware.
+ */
+ int init_profile_id;
};
void tasdevice_select_cfg_blk(void *context, int conf_no,
diff --git a/include/sound/tlv320dac33-plat.h b/include/sound/tlv320dac33-plat.h
deleted file mode 100644
index 7a7249a..0000000
--- a/include/sound/tlv320dac33-plat.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Platform header for Texas Instruments TLV320DAC33 codec driver
- *
- * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
- *
- * Copyright: (C) 2009 Nokia Corporation
- */
-
-#ifndef __TLV320DAC33_PLAT_H
-#define __TLV320DAC33_PLAT_H
-
-struct tlv320dac33_platform_data {
- int power_gpio;
- int mode1_latency; /* latency caused by the i2c writes in us */
- int auto_fifo_config; /* FIFO config based on the period size */
- int keep_bclk; /* Keep the BCLK running in FIFO modes */
- u8 burst_bclkdiv;
-};
-
-#endif /* __TLV320DAC33_PLAT_H */
diff --git a/include/uapi/sound/intel/avs/tokens.h b/include/uapi/sound/intel/avs/tokens.h
index c9f845b..f3ff6aa 100644
--- a/include/uapi/sound/intel/avs/tokens.h
+++ b/include/uapi/sound/intel/avs/tokens.h
@@ -133,6 +133,21 @@ enum avs_tplg_token {
AVS_TKN_PATH_FE_FMT_ID_U32 = 1902,
AVS_TKN_PATH_BE_FMT_ID_U32 = 1903,
+ /* struct avs_tplg_path_template (conditional) */
+ AVS_TKN_CONDPATH_TMPL_ID_U32 = 1801,
+ AVS_TKN_CONDPATH_TMPL_SOURCE_TPLG_NAME_STRING = 2002,
+ AVS_TKN_CONDPATH_TMPL_SOURCE_PATH_TMPL_ID_U32 = 2003,
+ AVS_TKN_CONDPATH_TMPL_SINK_TPLG_NAME_STRING = 2004,
+ AVS_TKN_CONDPATH_TMPL_SINK_PATH_TMPL_ID_U32 = 2005,
+ AVS_TKN_CONDPATH_TMPL_COND_TYPE_U32 = 2006,
+ AVS_TKN_CONDPATH_TMPL_OVERRIDABLE_BOOL = 2007,
+ AVS_TKN_CONDPATH_TMPL_PRIORITY_U8 = 2008,
+
+ /* struct avs_tplg_path (conditional) */
+ AVS_TKN_CONDPATH_ID_U32 = 1901,
+ AVS_TKN_CONDPATH_SOURCE_PATH_ID_U32 = 2102,
+ AVS_TKN_CONDPATH_SINK_PATH_ID_U32 = 2103,
+
/* struct avs_tplg_pin_format */
AVS_TKN_PIN_FMT_INDEX_U32 = 2201,
AVS_TKN_PIN_FMT_IOBS_U32 = 2202,
diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h
index b9b9093..6b8102e 100644
--- a/include/uapi/sound/snd_ar_tokens.h
+++ b/include/uapi/sound/snd_ar_tokens.h
@@ -3,6 +3,8 @@
#ifndef __SND_AR_TOKENS_H__
#define __SND_AR_TOKENS_H__
+#include <linux/types.h>
+
#define APM_SUB_GRAPH_PERF_MODE_LOW_POWER 0x1
#define APM_SUB_GRAPH_PERF_MODE_LOW_LATENCY 0x2
@@ -118,6 +120,12 @@ enum ar_event_types {
* LPAIF_WSA = 2,
* LPAIF_VA = 3,
* LPAIF_AXI = 4
+ * Possible values for MI2S
+ * I2S_INTF_TYPE_PRIMARY = 0,
+ * I2S_INTF_TYPE_SECONDARY = 1,
+ * I2S_INTF_TYPE_TERTIARY = 2,
+ * I2S_INTF_TYPE_QUATERNARY = 3,
+ * I2S_INTF_TYPE_QUINARY = 4,
*
* %AR_TKN_U32_MODULE_FMT_INTERLEAVE: PCM Interleaving
* PCM_INTERLEAVED = 1,
@@ -184,8 +192,8 @@ enum ar_event_types {
#define AR_TKN_U32_MODULE_INSTANCE_ID 201
#define AR_TKN_U32_MODULE_MAX_IP_PORTS 202
#define AR_TKN_U32_MODULE_MAX_OP_PORTS 203
-#define AR_TKN_U32_MODULE_IN_PORTS 204
-#define AR_TKN_U32_MODULE_OUT_PORTS 205
+#define AR_TKN_U32_MODULE_IN_PORTS 204 /* deprecated */
+#define AR_TKN_U32_MODULE_OUT_PORTS 205 /* deprecated */
#define AR_TKN_U32_MODULE_SRC_OP_PORT_ID 206
#define AR_TKN_U32_MODULE_DST_IN_PORT_ID 207
#define AR_TKN_U32_MODULE_SRC_INSTANCE_ID 208
@@ -232,4 +240,12 @@ enum ar_event_types {
#define AR_TKN_U32_MODULE_LOG_TAP_POINT_ID 260
#define AR_TKN_U32_MODULE_LOG_MODE 261
+#define SND_SOC_AR_TPLG_MODULE_CFG_TYPE 0x01001006
+struct audioreach_module_priv_data {
+ __le32 size; /* size in bytes of the array, including all elements */
+ __le32 type; /* SND_SOC_AR_TPLG_MODULE_CFG_TYPE */
+ __le32 priv[2]; /* Private data for future expansion */
+ __le32 data[0]; /* config data */
+};
+
#endif /* __SND_AR_TOKENS_H__ */
diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h
index c28c766..9ce72fb 100644
--- a/include/uapi/sound/sof/tokens.h
+++ b/include/uapi/sound/sof/tokens.h
@@ -106,6 +106,8 @@
*/
#define SOF_TKN_COMP_NO_WNAME_IN_KCONTROL_NAME 417
+#define SOF_TKN_COMP_SCHED_DOMAIN 418
+
/* SSP */
#define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500
#define SOF_TKN_INTEL_SSP_MCLK_ID 501
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
index 7204096..f0c1750 100644
--- a/sound/core/pcm_dmaengine.c
+++ b/sound/core/pcm_dmaengine.c
@@ -111,6 +111,7 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
slave_config->dst_addr = dma_data->addr;
slave_config->dst_maxburst = dma_data->maxburst;
+ slave_config->dst_port_window_size = dma_data->port_window_size;
if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
slave_config->dst_addr_width =
DMA_SLAVE_BUSWIDTH_UNDEFINED;
@@ -119,6 +120,7 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
} else {
slave_config->src_addr = dma_data->addr;
slave_config->src_maxburst = dma_data->maxburst;
+ slave_config->src_port_window_size = dma_data->port_window_size;
if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
slave_config->src_addr_width =
DMA_SLAVE_BUSWIDTH_UNDEFINED;
diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c
index aeffd24..7e9c074 100644
--- a/sound/soc/amd/acp/acp-rembrandt.c
+++ b/sound/soc/amd/acp/acp-rembrandt.c
@@ -147,7 +147,7 @@ static int rembrandt_audio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct acp_chip_info *chip;
- u32 ret;
+ int ret;
chip = dev_get_platdata(&pdev->dev);
if (!chip || !chip->base) {
diff --git a/sound/soc/amd/acp/amd-sdw-acpi.c b/sound/soc/amd/acp/amd-sdw-acpi.c
index 238b584..0160b0d 100644
--- a/sound/soc/amd/acp/amd-sdw-acpi.c
+++ b/sound/soc/amd/acp/amd-sdw-acpi.c
@@ -17,8 +17,8 @@
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/export.h>
-#include <linux/fwnode.h>
#include <linux/module.h>
+#include <linux/property.h>
#include <linux/soundwire/sdw_amd.h>
#include <linux/string.h>
diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c
index e7f2a05..352485d 100644
--- a/sound/soc/amd/raven/acp3x-i2s.c
+++ b/sound/soc/amd/raven/acp3x-i2s.c
@@ -149,8 +149,9 @@ static int acp3x_i2s_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
struct i2s_stream_instance *rtd;
- u32 ret, val, period_bytes, reg_val, ier_val, water_val;
+ u32 val, period_bytes, reg_val, ier_val, water_val;
u32 buf_size, buf_reg;
+ int ret;
rtd = substream->runtime->private_data;
period_bytes = frames_to_bytes(substream->runtime,
diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c
index 7dbe33f..bf719f6 100644
--- a/sound/soc/amd/vangogh/acp5x-i2s.c
+++ b/sound/soc/amd/vangogh/acp5x-i2s.c
@@ -234,8 +234,9 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,
{
struct i2s_stream_instance *rtd;
struct i2s_dev_data *adata;
- u32 ret, val, period_bytes, reg_val, ier_val, water_val;
+ u32 val, period_bytes, reg_val, ier_val, water_val;
u32 buf_size, buf_reg;
+ int ret;
adata = snd_soc_dai_get_drvdata(dai);
rtd = substream->runtime->private_data;
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 6d7e472..59616f5 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -125,6 +125,7 @@
imply SND_SOC_ES7134
imply SND_SOC_ES7241
imply SND_SOC_FRAMER
+ imply SND_SOC_FS210X
imply SND_SOC_GTM601
imply SND_SOC_HDAC_HDMI
imply SND_SOC_HDAC_HDA
@@ -177,6 +178,7 @@
imply SND_SOC_NAU8825
imply SND_SOC_HDMI_CODEC
imply SND_SOC_PCM1681
+ imply SND_SOC_PCM1754
imply SND_SOC_PCM1789_I2C
imply SND_SOC_PCM179X_I2C
imply SND_SOC_PCM179X_SPI
@@ -192,6 +194,7 @@
imply SND_SOC_PCM512x_SPI
imply SND_SOC_PCM6240
imply SND_SOC_PEB2466
+ imply SND_SOC_PM4125_SDW
imply SND_SOC_RK3308
imply SND_SOC_RK3328
imply SND_SOC_RK817
@@ -300,7 +303,6 @@
imply SND_SOC_LPASS_MACRO_COMMON
imply SND_SOC_LPASS_RX_MACRO
imply SND_SOC_LPASS_TX_MACRO
- imply SND_SOC_WL1273
imply SND_SOC_WM0010
imply SND_SOC_WM1250_EV1
imply SND_SOC_WM2000
@@ -622,6 +624,7 @@
config SND_SOC_AK4641
tristate
depends on I2C
+ depends on GPIOLIB_LEGACY
config SND_SOC_AK4642
tristate "AKM AK4642 CODEC"
@@ -1232,6 +1235,21 @@
To compile this driver as a module, choose M here: the module
will be called snd-soc-framer.
+config SND_SOC_FS_AMP_LIB
+ select CRC16
+ tristate
+
+config SND_SOC_FS210X
+ tristate 'FourSemi FS2104/5S digital audio amplifier'
+ depends on I2C
+ select GPIOLIB
+ select REGMAP_I2C
+ select SND_SOC_FS_AMP_LIB
+ help
+ Enable support for FourSemi FS2104/5S digital audio amplifier.
+ The FS2104/5S are Inductor-Less, Stereo, Closed-Loop,
+ Digital Input Class-D Power Amplifiers with Enhanced Signal Processing.
+ The amplifiers support I2C and I2S/TDM.
config SND_SOC_GTM601
tristate 'GTM601 UMTS modem audio codec'
@@ -1426,6 +1444,10 @@
tristate "Texas Instruments PCM1681 CODEC"
depends on I2C
+config SND_SOC_PCM1754
+ tristate "Texas Instruments PCM1754 CODEC"
+ depends on GPIOLIB
+
config SND_SOC_PCM1789
tristate
@@ -1542,6 +1564,23 @@
To compile this driver as a module, choose M here: the module
will be called snd-soc-peb2466.
+config SND_SOC_PM4125
+ depends on SND_SOC_PM4125_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+
+config SND_SOC_PM4125_SDW
+ tristate "PM4125 audio codec - SDW"
+ select SND_SOC_PM4125
+ select SND_SOC_WCD_MBHC
+ select REGMAP_IRQ
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ The PMIC PM4125 has an in-built audio codec IC used with SoCs
+ like QCM2290, and it is connected via soundwire and SPMI.
+ To compile this codec driver say Y or m.
+
config SND_SOC_RK3308
tristate "Rockchip RK3308 audio CODEC"
depends on ARM64 || COMPILE_TEST
@@ -2175,6 +2214,7 @@
config SND_SOC_TLV320DAC33
tristate
depends on I2C
+ depends on GPIOLIB_LEGACY
config SND_SOC_TLV320ADCX140
tristate "Texas Instruments TLV320ADCX140 CODEC family"
@@ -2229,10 +2269,14 @@
config SND_SOC_UDA1380
tristate
depends on I2C
+ depends on GPIOLIB_LEGACY
config SND_SOC_WCD_CLASSH
tristate
+config SND_SOC_WCD_COMMON
+ tristate
+
config SND_SOC_WCD9335
tristate "WCD9335 Codec"
depends on SLIMBUS
@@ -2254,6 +2298,7 @@
select REGMAP_IRQ
select REGMAP_SLIMBUS
select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
select SND_SOC_WCD_MBHC
depends on MFD_WCD934X || COMPILE_TEST
help
@@ -2265,6 +2310,7 @@
tristate
depends on SOUNDWIRE || !SOUNDWIRE
select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
config SND_SOC_WCD937X_SDW
tristate "WCD9370/WCD9375 Codec - SDW"
@@ -2284,6 +2330,7 @@
tristate
depends on SOUNDWIRE || !SOUNDWIRE
select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
select MULTIPLEXER
config SND_SOC_WCD938X_SDW
@@ -2303,6 +2350,7 @@
depends on SOUNDWIRE || !SOUNDWIRE
depends on TYPEC || !TYPEC
select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
config SND_SOC_WCD939X_SDW
tristate "WCD9390/WCD9395 Codec - SDW"
@@ -2316,9 +2364,6 @@
The WCD9390/9395 is a audio codec IC Integrated in
Qualcomm SoCs like SM8650.
-config SND_SOC_WL1273
- tristate
-
config SND_SOC_WM0010
tristate
depends on SPI_MASTER
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index a68c3d1..438eabd 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -137,6 +137,8 @@
snd-soc-es8375-y := es8375.o
snd-soc-es8389-y := es8389.o
snd-soc-framer-y := framer-codec.o
+snd-soc-fs-amp-lib-y := fs-amp-lib.o
+snd-soc-fs210x-y := fs210x.o
snd-soc-gtm601-y := gtm601.o
snd-soc-hdac-hdmi-y := hdac_hdmi.o
snd-soc-hdac-hda-y := hdac_hda.o
@@ -201,6 +203,7 @@
snd-soc-ntpfw-y := ntpfw.o
snd-soc-hdmi-codec-y := hdmi-codec.o
snd-soc-pcm1681-y := pcm1681.o
+snd-soc-pcm1754-y := pcm1754.o
snd-soc-pcm1789-codec-y := pcm1789.o
snd-soc-pcm1789-i2c-y := pcm1789-i2c.o
snd-soc-pcm179x-codec-y := pcm179x.o
@@ -222,6 +225,8 @@
snd-soc-pcm512x-spi-y := pcm512x-spi.o
snd-soc-pcm6240-y := pcm6240.o
snd-soc-peb2466-y := peb2466.o
+snd-soc-pm4125-y := pm4125.o
+snd-soc-pm4125-sdw-y := pm4125-sdw.o
snd-soc-rk3308-y := rk3308_codec.o
snd-soc-rk3328-y := rk3328_codec.o
snd-soc-rk817-y := rk817_codec.o
@@ -339,6 +344,7 @@
snd-soc-uda1342-y := uda1342.o
snd-soc-uda1380-y := uda1380.o
snd-soc-wcd-classh-y := wcd-clsh-v2.o
+snd-soc-wcd-common-y := wcd-common.o
snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o
snd-soc-wcd9335-y := wcd9335.o
snd-soc-wcd934x-y := wcd934x.o
@@ -348,7 +354,6 @@
snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
snd-soc-wcd939x-y := wcd939x.o
snd-soc-wcd939x-sdw-y := wcd939x-sdw.o
-snd-soc-wl1273-y := wl1273.o
snd-soc-wm-adsp-y := wm_adsp.o
snd-soc-wm0010-y := wm0010.o
snd-soc-wm1250-ev1-y := wm1250-ev1.o
@@ -562,6 +567,8 @@
obj-$(CONFIG_SND_SOC_ES8375) += snd-soc-es8375.o
obj-$(CONFIG_SND_SOC_ES8389) += snd-soc-es8389.o
obj-$(CONFIG_SND_SOC_FRAMER) += snd-soc-framer.o
+obj-$(CONFIG_SND_SOC_FS_AMP_LIB)+= snd-soc-fs-amp-lib.o
+obj-$(CONFIG_SND_SOC_FS210X) += snd-soc-fs210x.o
obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o
@@ -621,6 +628,7 @@
obj-$(CONFIG_SND_SOC_NTPFW) += snd-soc-ntpfw.o
obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
+obj-$(CONFIG_SND_SOC_PCM1754) += snd-soc-pcm1754.o
obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
obj-$(CONFIG_SND_SOC_PCM1789_I2C) += snd-soc-pcm1789-i2c.o
obj-$(CONFIG_SND_SOC_PCM1789) += snd-soc-pcm1789-codec.o
@@ -642,6 +650,12 @@
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
obj-$(CONFIG_SND_SOC_PCM6240) += snd-soc-pcm6240.o
obj-$(CONFIG_SND_SOC_PEB2466) += snd-soc-peb2466.o
+obj-$(CONFIG_SND_SOC_PM4125_SDW) += snd-soc-pm4125-sdw.o
+obj-$(CONFIG_SND_SOC_PM4125) += snd-soc-pm4125.o
+ifdef CONFIG_SND_SOC_PM4125_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_PM4125) += snd-soc-pm4125-sdw.o
+endif
obj-$(CONFIG_SND_SOC_RK3308) += snd-soc-rk3308.o
obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o
obj-$(CONFIG_SND_SOC_RK817) += snd-soc-rk817.o
@@ -761,6 +775,7 @@
obj-$(CONFIG_SND_SOC_UDA1342) += snd-soc-uda1342.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WCD_CLASSH) += snd-soc-wcd-classh.o
+obj-$(CONFIG_SND_SOC_WCD_COMMON) += snd-soc-wcd-common.o
obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o
obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o
obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o
@@ -779,7 +794,6 @@
# avoid link failure by forcing sdw code built-in when needed
obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o
endif
-obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
index ae59efb..c193a9f 100644
--- a/sound/soc/codecs/adau1977.c
+++ b/sound/soc/codecs/adau1977.c
@@ -795,7 +795,7 @@ static int adau1977_set_sysclk(struct snd_soc_component *component,
struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
unsigned int mask = 0;
unsigned int clk_src;
- unsigned int ret;
+ int ret;
if (dir != SND_SOC_CLOCK_IN)
return -EINVAL;
diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c
index f536501..2fde843 100644
--- a/sound/soc/codecs/cs-amp-lib-test.c
+++ b/sound/soc/codecs/cs-amp-lib-test.c
@@ -19,6 +19,14 @@
#include <linux/random.h>
#include <sound/cs-amp-lib.h>
+#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker"
+#define LENOVO_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82)
+
+#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID"
+#define HP_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e)
+
KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
struct faux_device *)
@@ -196,8 +204,9 @@ static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name,
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size);
- KUNIT_EXPECT_MEMEQ(test, name, expected_name, sizeof(expected_name));
- KUNIT_EXPECT_MEMEQ(test, guid, &expected_guid, sizeof(expected_guid));
+ if (memcmp(name, expected_name, sizeof(expected_name)) ||
+ efi_guidcmp(*guid, expected_guid))
+ return -EFI_NOT_FOUND;
if (!buf) {
*size = priv->cal_blob->size;
@@ -211,6 +220,56 @@ static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name,
return EFI_SUCCESS;
}
+static efi_status_t cs_amp_lib_test_get_hp_cal_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ static const efi_char16_t expected_name[] = L"SmartAmpCalibrationData";
+ static const efi_guid_t expected_guid =
+ EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93);
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size);
+
+ if (memcmp(name, expected_name, sizeof(expected_name)) ||
+ efi_guidcmp(*guid, expected_guid))
+ return -EFI_NOT_FOUND;
+
+ if (!buf) {
+ *size = priv->cal_blob->size;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small");
+
+ memcpy(buf, priv->cal_blob, priv->cal_blob->size);
+
+ return EFI_SUCCESS;
+}
+
+/* Get cal data block from HP variable. */
+static void cs_amp_lib_test_get_hp_efi_cal(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 2);
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_hp_cal_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_MEMEQ(test, &result_data, &priv->cal_blob->data[0], sizeof(result_data));
+}
+
/* Get cal data block for a given amp, matched by target UID. */
static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test)
{
@@ -642,6 +701,185 @@ static void cs_amp_lib_test_write_cal_data_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, entry->value, data.calStatus);
}
+static void cs_amp_lib_test_spkid_lenovo_not_present(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_none);
+
+ KUNIT_EXPECT_EQ(test, -ENOENT, cs_amp_get_vendor_spkid(dev));
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_d0(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, LENOVO_SPEAKER_ID_EFI_NAME, sizeof(LENOVO_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0xd0;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_d1(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, LENOVO_SPEAKER_ID_EFI_NAME, sizeof(LENOVO_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0xd1;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_00(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ KUNIT_ASSERT_EQ(test, 0, efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID));
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0;
+
+ return EFI_SUCCESS;
+}
+
+static void cs_amp_lib_test_spkid_lenovo_d0(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_lenovo_d0);
+
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_lenovo_d1(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_lenovo_d1);
+
+ KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_lenovo_illegal(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_lenovo_00);
+
+ KUNIT_EXPECT_LT(test, cs_amp_get_vendor_spkid(dev), 0);
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_buf_too_small(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ return EFI_BUFFER_TOO_SMALL;
+}
+
+static void cs_amp_lib_test_spkid_lenovo_oversize(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_buf_too_small);
+
+ KUNIT_EXPECT_LT(test, cs_amp_get_vendor_spkid(dev), 0);
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_hp_30(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, HP_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, HP_SPEAKER_ID_EFI_NAME, sizeof(HP_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0x30;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_hp_31(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, HP_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, HP_SPEAKER_ID_EFI_NAME, sizeof(HP_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0x31;
+
+ return EFI_SUCCESS;
+}
+
+static void cs_amp_lib_test_spkid_hp_30(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_hp_30);
+
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_hp_31(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_hp_31);
+
+ KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
+}
+
static int cs_amp_lib_test_case_init(struct kunit *test)
{
struct cs_amp_lib_test_priv *priv;
@@ -722,6 +960,7 @@ static struct kunit_case cs_amp_lib_test_cases[] = {
KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test),
KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_no_index_test),
KUNIT_CASE(cs_amp_lib_test_get_efi_cal_zero_not_matched_test),
+ KUNIT_CASE(cs_amp_lib_test_get_hp_efi_cal),
KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_uid_test,
cs_amp_lib_test_get_cal_gen_params),
KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_unchecked_test,
@@ -737,6 +976,15 @@ static struct kunit_case cs_amp_lib_test_cases[] = {
/* Tests for writing calibration data */
KUNIT_CASE(cs_amp_lib_test_write_cal_data_test),
+ /* Test cases for speaker ID */
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_not_present),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_d0),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_d1),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_illegal),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_oversize),
+ KUNIT_CASE(cs_amp_lib_test_spkid_hp_30),
+ KUNIT_CASE(cs_amp_lib_test_spkid_hp_31),
+
{ } /* terminator */
};
diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c
index 808e67c..8434d51 100644
--- a/sound/soc/codecs/cs-amp-lib.c
+++ b/sound/soc/codecs/cs-amp-lib.c
@@ -16,10 +16,35 @@
#include <linux/types.h>
#include <sound/cs-amp-lib.h>
-#define CS_AMP_CAL_GUID \
+#define CIRRUS_LOGIC_CALIBRATION_EFI_NAME L"CirrusSmartAmpCalibrationData"
+#define CIRRUS_LOGIC_CALIBRATION_EFI_GUID \
EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3)
-#define CS_AMP_CAL_NAME L"CirrusSmartAmpCalibrationData"
+#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker"
+#define LENOVO_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82)
+
+#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID"
+#define HP_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e)
+
+#define HP_CALIBRATION_EFI_NAME L"SmartAmpCalibrationData"
+#define HP_CALIBRATION_EFI_GUID \
+ EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93)
+
+static const struct cs_amp_lib_cal_efivar {
+ efi_char16_t *name;
+ efi_guid_t *guid;
+} cs_amp_lib_cal_efivars[] = {
+ {
+ .name = HP_CALIBRATION_EFI_NAME,
+ .guid = &HP_CALIBRATION_EFI_GUID,
+ },
+ {
+ .name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME,
+ .guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID,
+ },
+};
static int cs_amp_write_cal_coeff(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
@@ -115,16 +140,41 @@ static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name,
return EFI_NOT_FOUND;
}
+static int cs_amp_convert_efi_status(efi_status_t status)
+{
+ switch (status) {
+ case EFI_SUCCESS:
+ return 0;
+ case EFI_NOT_FOUND:
+ return -ENOENT;
+ case EFI_BUFFER_TOO_SMALL:
+ return -EFBIG;
+ case EFI_UNSUPPORTED:
+ case EFI_ACCESS_DENIED:
+ case EFI_SECURITY_VIOLATION:
+ return -EACCES;
+ default:
+ return -EIO;
+ }
+}
+
static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev)
{
struct cirrus_amp_efi_data *efi_data;
unsigned long data_size = 0;
u8 *data;
efi_status_t status;
- int ret;
+ int i, ret;
- /* Get real size of UEFI variable */
- status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, NULL);
+ /* Find EFI variable and get size */
+ for (i = 0; i < ARRAY_SIZE(cs_amp_lib_cal_efivars); i++) {
+ status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
+ cs_amp_lib_cal_efivars[i].guid,
+ &data_size, NULL);
+ if (status == EFI_BUFFER_TOO_SMALL)
+ break;
+ }
+
if (status != EFI_BUFFER_TOO_SMALL)
return ERR_PTR(-ENOENT);
@@ -138,7 +188,9 @@ static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev)
if (!data)
return ERR_PTR(-ENOMEM);
- status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, data);
+ status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
+ cs_amp_lib_cal_efivars[i].guid,
+ &data_size, data);
if (status != EFI_SUCCESS) {
ret = -EINVAL;
goto err;
@@ -273,6 +325,81 @@ int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_
}
EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
+struct cs_amp_spkid_efi {
+ efi_char16_t *name;
+ efi_guid_t *guid;
+ u8 values[2];
+};
+
+static int cs_amp_get_efi_byte_spkid(struct device *dev, const struct cs_amp_spkid_efi *info)
+{
+ efi_status_t status;
+ unsigned long size;
+ u8 spkid;
+ int i, ret;
+
+ size = sizeof(spkid);
+ status = cs_amp_get_efi_variable(info->name, info->guid, &size, &spkid);
+ ret = cs_amp_convert_efi_status(status);
+ if (ret < 0)
+ return ret;
+
+ if (size == 0)
+ return -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(info->values); i++) {
+ if (info->values[i] == spkid)
+ return i;
+ }
+
+ dev_err(dev, "EFI speaker ID bad value %#x\n", spkid);
+
+ return -EINVAL;
+}
+
+static const struct cs_amp_spkid_efi cs_amp_spkid_byte_types[] = {
+ {
+ .name = LENOVO_SPEAKER_ID_EFI_NAME,
+ .guid = &LENOVO_SPEAKER_ID_EFI_GUID,
+ .values = { 0xd0, 0xd1 },
+ },
+ {
+ .name = HP_SPEAKER_ID_EFI_NAME,
+ .guid = &HP_SPEAKER_ID_EFI_GUID,
+ .values = { 0x30, 0x31 },
+ },
+};
+
+/**
+ * cs_amp_get_vendor_spkid - get a speaker ID from vendor-specific storage
+ * @dev: pointer to struct device
+ *
+ * Known vendor-specific methods of speaker ID are checked and if one is
+ * found its speaker ID value is returned.
+ *
+ * Return: >=0 is a valid speaker ID. -ENOENT if a vendor-specific method
+ * was not found. -EACCES if the vendor-specific storage could not
+ * be read. Other error values indicate that the data from the
+ * vendor-specific storage was found but could not be understood.
+ */
+int cs_amp_get_vendor_spkid(struct device *dev)
+{
+ int i, ret;
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
+ !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ return -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) {
+ ret = cs_amp_get_efi_byte_spkid(dev, &cs_amp_spkid_byte_types[i]);
+ if (ret != -ENOENT)
+ return ret;
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB");
+
static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
.get_efi_variable = cs_amp_get_efi_variable,
.write_cal_coeff = cs_amp_write_cal_coeff,
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
index 850fcf3..95d018e 100644
--- a/sound/soc/codecs/cs35l56-shared.c
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -1054,7 +1054,17 @@ int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base)
u32 speaker_id;
int i, ret;
- /* Attempt to read the speaker type from a device property first */
+ /* Check for vendor-specific speaker ID method */
+ ret = cs_amp_get_vendor_spkid(cs35l56_base->dev);
+ if (ret >= 0) {
+ dev_dbg(cs35l56_base->dev, "Vendor Speaker ID = %d\n", ret);
+ return ret;
+ } else if (ret != -ENOENT) {
+ dev_err(cs35l56_base->dev, "Error getting vendor Speaker ID: %d\n", ret);
+ return ret;
+ }
+
+ /* Attempt to read the speaker type from a device property */
ret = device_property_read_u32(cs35l56_base->dev, "cirrus,speaker-id", &speaker_id);
if (!ret) {
dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id);
diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c
index 2a0a498..867e23d 100644
--- a/sound/soc/codecs/cs42l43-jack.c
+++ b/sound/soc/codecs/cs42l43-jack.c
@@ -684,7 +684,7 @@ static int cs42l43_run_type_detect(struct cs42l43_codec *priv)
}
}
-static void cs42l43_clear_jack(struct cs42l43_codec *priv)
+void cs42l43_clear_jack(struct cs42l43_codec *priv)
{
struct cs42l43 *cs42l43 = priv->core;
@@ -703,8 +703,6 @@ static void cs42l43_clear_jack(struct cs42l43_codec *priv)
regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK,
0x2 << CS42L43_HSDET_MODE_SHIFT);
-
- snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
}
void cs42l43_tip_sense_work(struct work_struct *work)
@@ -753,6 +751,8 @@ void cs42l43_tip_sense_work(struct work_struct *work)
cs42l43_clear_jack(priv);
+ snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
+
if (cs42l43->sdw && priv->jack_present) {
pm_runtime_put(priv->dev);
priv->jack_present = false;
@@ -903,6 +903,8 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u
cs42l43_clear_jack(priv);
+ snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
+
if (!override) {
queue_delayed_work(system_long_wq, &priv->tip_sense_work, 0);
} else {
diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c
index b0c27d6..b61df09 100644
--- a/sound/soc/codecs/cs42l43.c
+++ b/sound/soc/codecs/cs42l43.c
@@ -2210,13 +2210,12 @@ static const struct cs42l43_irq cs42l43_irqs[] = {
};
static int cs42l43_request_irq(struct cs42l43_codec *priv,
- struct irq_domain *dom, const char * const name,
- unsigned int irq, irq_handler_t handler,
- unsigned long flags)
+ const char * const name, unsigned int irq,
+ irq_handler_t handler, unsigned long flags)
{
int ret;
- ret = irq_create_mapping(dom, irq);
+ ret = irq_create_mapping(priv->dom, irq);
if (ret < 0)
return dev_err_probe(priv->dev, ret, "Failed to map IRQ %s\n", name);
@@ -2230,13 +2229,29 @@ static int cs42l43_request_irq(struct cs42l43_codec *priv,
return 0;
}
-static int cs42l43_shutter_irq(struct cs42l43_codec *priv,
- struct irq_domain *dom, unsigned int shutter,
- const char * const open_name,
- const char * const close_name,
+static void cs42l43_disable_irq(struct cs42l43_codec *priv, unsigned int irq)
+{
+ int ret;
+
+ ret = irq_find_mapping(priv->dom, irq);
+ if (ret > 0)
+ disable_irq(ret);
+}
+
+static void cs42l43_enable_irq(struct cs42l43_codec *priv, unsigned int irq)
+{
+ int ret;
+
+ ret = irq_find_mapping(priv->dom, irq);
+ if (ret > 0)
+ enable_irq(ret);
+}
+
+static int cs42l43_shutter_irq(struct cs42l43_codec *priv, unsigned int shutter,
+ const char * const open_name, unsigned int *open_irq,
+ const char * const close_name, unsigned int *close_irq,
irq_handler_t handler)
{
- unsigned int open_irq, close_irq;
int ret;
switch (shutter) {
@@ -2244,40 +2259,35 @@ static int cs42l43_shutter_irq(struct cs42l43_codec *priv,
dev_warn(priv->dev, "Manual shutters, notifications not available\n");
return 0;
case 0x2:
- open_irq = CS42L43_GPIO1_RISE;
- close_irq = CS42L43_GPIO1_FALL;
+ *open_irq = CS42L43_GPIO1_RISE;
+ *close_irq = CS42L43_GPIO1_FALL;
break;
case 0x4:
- open_irq = CS42L43_GPIO2_RISE;
- close_irq = CS42L43_GPIO2_FALL;
+ *open_irq = CS42L43_GPIO2_RISE;
+ *close_irq = CS42L43_GPIO2_FALL;
break;
case 0x8:
- open_irq = CS42L43_GPIO3_RISE;
- close_irq = CS42L43_GPIO3_FALL;
+ *open_irq = CS42L43_GPIO3_RISE;
+ *close_irq = CS42L43_GPIO3_FALL;
break;
default:
return 0;
}
- ret = cs42l43_request_irq(priv, dom, close_name, close_irq, handler, IRQF_SHARED);
+ ret = cs42l43_request_irq(priv, close_name, *close_irq, handler, IRQF_SHARED);
if (ret)
return ret;
- return cs42l43_request_irq(priv, dom, open_name, open_irq, handler, IRQF_SHARED);
+ return cs42l43_request_irq(priv, open_name, *open_irq, handler, IRQF_SHARED);
}
static int cs42l43_codec_probe(struct platform_device *pdev)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);
struct cs42l43_codec *priv;
- struct irq_domain *dom;
unsigned int val;
int i, ret;
- dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY);
- if (!dom)
- return -EPROBE_DEFER;
-
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -2285,6 +2295,10 @@ static int cs42l43_codec_probe(struct platform_device *pdev)
priv->dev = &pdev->dev;
priv->core = cs42l43;
+ priv->dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY);
+ if (!priv->dom)
+ return -EPROBE_DEFER;
+
platform_set_drvdata(pdev, priv);
mutex_init(&priv->jack_lock);
@@ -2314,7 +2328,7 @@ static int cs42l43_codec_probe(struct platform_device *pdev)
goto err_pm;
for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) {
- ret = cs42l43_request_irq(priv, dom, cs42l43_irqs[i].name,
+ ret = cs42l43_request_irq(priv, cs42l43_irqs[i].name,
cs42l43_irqs[i].irq,
cs42l43_irqs[i].handler, 0);
if (ret)
@@ -2327,15 +2341,17 @@ static int cs42l43_codec_probe(struct platform_device *pdev)
goto err_pm;
}
- ret = cs42l43_shutter_irq(priv, dom, val & CS42L43_MIC_SHUTTER_CFG_MASK,
- "mic shutter open", "mic shutter close",
+ ret = cs42l43_shutter_irq(priv, val & CS42L43_MIC_SHUTTER_CFG_MASK,
+ "mic shutter open", &priv->shutter_irqs[0],
+ "mic shutter close", &priv->shutter_irqs[1],
cs42l43_mic_shutter);
if (ret)
goto err_pm;
- ret = cs42l43_shutter_irq(priv, dom, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >>
+ ret = cs42l43_shutter_irq(priv, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >>
CS42L43_SPK_SHUTTER_CFG_SHIFT,
- "spk shutter open", "spk shutter close",
+ "spk shutter open", &priv->shutter_irqs[2],
+ "spk shutter close", &priv->shutter_irqs[3],
cs42l43_spk_shutter);
if (ret)
goto err_pm;
@@ -2386,22 +2402,53 @@ static int cs42l43_codec_runtime_resume(struct device *dev)
return 0;
}
-static int cs42l43_codec_runtime_force_suspend(struct device *dev)
+static int cs42l43_codec_suspend(struct device *dev)
{
struct cs42l43_codec *priv = dev_get_drvdata(dev);
+ int i;
- dev_dbg(priv->dev, "Runtime suspend\n");
+ dev_dbg(priv->dev, "System suspend\n");
priv->suspend_jack_debounce = true;
- pm_runtime_force_suspend(dev);
+ for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++)
+ cs42l43_disable_irq(priv, cs42l43_irqs[i].irq);
+
+ for (i = 0; i < ARRAY_SIZE(priv->shutter_irqs); i++)
+ if (priv->shutter_irqs[i])
+ cs42l43_disable_irq(priv, priv->shutter_irqs[i]);
+
+ cancel_delayed_work_sync(&priv->bias_sense_timeout);
+ cancel_delayed_work_sync(&priv->tip_sense_work);
+ cancel_delayed_work_sync(&priv->hp_ilimit_clear_work);
+
+ cs42l43_clear_jack(priv);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int cs42l43_codec_resume(struct device *dev)
+{
+ struct cs42l43_codec *priv = dev_get_drvdata(dev);
+ int ret, i;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++)
+ cs42l43_enable_irq(priv, cs42l43_irqs[i].irq);
+
+ for (i = 0; i < ARRAY_SIZE(priv->shutter_irqs); i++)
+ if (priv->shutter_irqs[i])
+ cs42l43_enable_irq(priv, priv->shutter_irqs[i]);
return 0;
}
static const struct dev_pm_ops cs42l43_codec_pm_ops = {
RUNTIME_PM_OPS(NULL, cs42l43_codec_runtime_resume, NULL)
- SYSTEM_SLEEP_PM_OPS(cs42l43_codec_runtime_force_suspend, pm_runtime_force_resume)
+ SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend, cs42l43_codec_resume)
};
static const struct platform_device_id cs42l43_codec_id_table[] = {
diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h
index 3ea3636..b2fa2cd 100644
--- a/sound/soc/codecs/cs42l43.h
+++ b/sound/soc/codecs/cs42l43.h
@@ -44,6 +44,8 @@ struct cs42l43_codec {
struct device *dev;
struct cs42l43 *core;
struct snd_soc_component *component;
+ struct irq_domain *dom;
+ unsigned int shutter_irqs[4];
struct clk *mclk;
@@ -130,6 +132,7 @@ static inline int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream
int cs42l43_set_jack(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *d);
void cs42l43_bias_sense_timeout(struct work_struct *work);
+void cs42l43_clear_jack(struct cs42l43_codec *priv);
void cs42l43_tip_sense_work(struct work_struct *work);
irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data);
irqreturn_t cs42l43_button_press(int irq, void *data);
diff --git a/sound/soc/codecs/cs48l32-tables.c b/sound/soc/codecs/cs48l32-tables.c
index 59eaa9a..8ff3652 100644
--- a/sound/soc/codecs/cs48l32-tables.c
+++ b/sound/soc/codecs/cs48l32-tables.c
@@ -533,8 +533,6 @@ static const struct regmap_config cs48l32_regmap = {
int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32)
{
cs48l32->regmap = devm_regmap_init_spi(spi, &cs48l32_regmap);
- if (IS_ERR(cs48l32->regmap))
- return PTR_ERR(cs48l32->regmap);
- return 0;
+ return PTR_ERR_OR_ZERO(cs48l32->regmap);
}
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index a4496cc..ae89260c 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -2247,10 +2247,8 @@ static int da7213_runtime_resume(struct device *dev)
return regcache_sync(da7213->regmap);
}
-static const struct dev_pm_ops da7213_pm = {
- RUNTIME_PM_OPS(da7213_runtime_suspend, da7213_runtime_resume, NULL)
- SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
-};
+static DEFINE_RUNTIME_DEV_PM_OPS(da7213_pm, da7213_runtime_suspend,
+ da7213_runtime_resume, NULL);
static const struct i2c_device_id da7213_i2c_id[] = {
{ "da7213" },
diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c
index a982299..eb85b71 100644
--- a/sound/soc/codecs/es8323.c
+++ b/sound/soc/codecs/es8323.c
@@ -182,13 +182,13 @@ static const struct snd_kcontrol_new es8323_mono_adc_mux_controls =
/* Left Mixer */
static const struct snd_kcontrol_new es8323_left_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left Playback Switch", SND_SOC_NOPM, 7, 1, 1),
+ SOC_DAPM_SINGLE("Left Playback Switch", ES8323_DACCONTROL17, 7, 1, 0),
SOC_DAPM_SINGLE("Left Bypass Switch", ES8323_DACCONTROL17, 6, 1, 0),
};
/* Right Mixer */
static const struct snd_kcontrol_new es8323_right_mixer_controls[] = {
- SOC_DAPM_SINGLE("Right Playback Switch", SND_SOC_NOPM, 6, 1, 1),
+ SOC_DAPM_SINGLE("Right Playback Switch", ES8323_DACCONTROL20, 7, 1, 0),
SOC_DAPM_SINGLE("Right Bypass Switch", ES8323_DACCONTROL20, 6, 1, 0),
};
@@ -211,8 +211,8 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
SND_SOC_DAPM_ADC("Right ADC", "Right Capture", SND_SOC_NOPM, 4, 1),
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", SND_SOC_NOPM, 5, 1),
- SND_SOC_DAPM_DAC("Right DAC", "Right Playback", SND_SOC_NOPM, 6, 1),
- SND_SOC_DAPM_DAC("Left DAC", "Left Playback", SND_SOC_NOPM, 7, 1),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8323_DACPOWER, 6, 1),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8323_DACPOWER, 7, 1),
SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
&es8323_left_mixer_controls[0],
@@ -223,10 +223,10 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
SND_SOC_DAPM_PGA("Right ADC Power", SND_SOC_NOPM, 6, 1, NULL, 0),
SND_SOC_DAPM_PGA("Left ADC Power", SND_SOC_NOPM, 7, 1, NULL, 0),
- SND_SOC_DAPM_PGA("Right Out 2", SND_SOC_NOPM, 2, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Left Out 2", SND_SOC_NOPM, 3, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Right Out 1", SND_SOC_NOPM, 4, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Left Out 1", SND_SOC_NOPM, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, 0, 0, NULL, 0),
@@ -632,7 +632,6 @@ static int es8323_probe(struct snd_soc_component *component)
snd_soc_component_write(component, ES8323_CONTROL2, 0x60);
snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
- snd_soc_component_write(component, ES8323_DACCONTROL17, 0xB8);
return 0;
}
diff --git a/sound/soc/codecs/fs-amp-lib.c b/sound/soc/codecs/fs-amp-lib.c
new file mode 100644
index 0000000..c8f5661
--- /dev/null
+++ b/sound/soc/codecs/fs-amp-lib.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// fs-amp-lib.c --- Common library for FourSemi Audio Amplifiers
+//
+// Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+
+#include <linux/crc16.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "fs-amp-lib.h"
+
+static int fs_get_scene_count(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_table *table;
+ int count;
+
+ if (!amp_lib || !amp_lib->dev)
+ return -EINVAL;
+
+ table = amp_lib->table[FS_INDEX_SCENE];
+ if (!table)
+ return -EFAULT;
+
+ count = table->size / sizeof(struct fs_scene_index);
+ if (count < 1 || count > FS_SCENE_COUNT_MAX) {
+ dev_err(amp_lib->dev, "Invalid scene count: %d\n", count);
+ return -ERANGE;
+ }
+
+ return count;
+}
+
+static void fs_get_fwm_string(struct fs_amp_lib *amp_lib,
+ int offset, const char **pstr)
+{
+ const struct fs_fwm_table *table;
+
+ if (!amp_lib || !amp_lib->dev || !pstr)
+ return;
+
+ table = amp_lib->table[FS_INDEX_STRING];
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ *pstr = (char *)table + offset;
+ else
+ *pstr = NULL;
+}
+
+static void fs_get_scene_reg(struct fs_amp_lib *amp_lib,
+ int offset, struct fs_amp_scene *scene)
+{
+ const struct fs_fwm_table *table;
+
+ if (!amp_lib || !amp_lib->dev || !scene)
+ return;
+
+ table = amp_lib->table[FS_INDEX_REG];
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ scene->reg = (struct fs_reg_table *)((char *)table + offset);
+ else
+ scene->reg = NULL;
+}
+
+static void fs_get_scene_model(struct fs_amp_lib *amp_lib,
+ int offset, struct fs_amp_scene *scene)
+{
+ const struct fs_fwm_table *table;
+ const char *ptr;
+
+ if (!amp_lib || !amp_lib->dev || !scene)
+ return;
+
+ table = amp_lib->table[FS_INDEX_MODEL];
+ ptr = (char *)table;
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ scene->model = (struct fs_file_table *)(ptr + offset);
+ else
+ scene->model = NULL;
+}
+
+static void fs_get_scene_effect(struct fs_amp_lib *amp_lib,
+ int offset, struct fs_amp_scene *scene)
+{
+ const struct fs_fwm_table *table;
+ const char *ptr;
+
+ if (!amp_lib || !amp_lib->dev || !scene)
+ return;
+
+ table = amp_lib->table[FS_INDEX_EFFECT];
+ ptr = (char *)table;
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ scene->effect = (struct fs_file_table *)(ptr + offset);
+ else
+ scene->effect = NULL;
+}
+
+static int fs_parse_scene_tables(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_scene_index *scene_index;
+ const struct fs_fwm_table *table;
+ struct fs_amp_scene *scene;
+ int idx, count;
+
+ if (!amp_lib || !amp_lib->dev)
+ return -EINVAL;
+
+ count = fs_get_scene_count(amp_lib);
+ if (count <= 0)
+ return -EFAULT;
+
+ scene = devm_kcalloc(amp_lib->dev, count, sizeof(*scene), GFP_KERNEL);
+ if (!scene)
+ return -ENOMEM;
+
+ amp_lib->scene_count = count;
+ amp_lib->scene = scene;
+
+ table = amp_lib->table[FS_INDEX_SCENE];
+ scene_index = (struct fs_scene_index *)table->buf;
+
+ for (idx = 0; idx < count; idx++) {
+ fs_get_fwm_string(amp_lib, scene_index->name, &scene->name);
+ if (!scene->name)
+ scene->name = devm_kasprintf(amp_lib->dev,
+ GFP_KERNEL, "S%d", idx);
+ dev_dbg(amp_lib->dev, "scene.%d name: %s\n", idx, scene->name);
+ fs_get_scene_reg(amp_lib, scene_index->reg, scene);
+ fs_get_scene_model(amp_lib, scene_index->model, scene);
+ fs_get_scene_effect(amp_lib, scene_index->effect, scene);
+ scene++;
+ scene_index++;
+ }
+
+ return 0;
+}
+
+static int fs_parse_all_tables(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_table *table;
+ const struct fs_fwm_index *index;
+ const char *ptr;
+ int idx, count;
+ int ret;
+
+ if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+ return -EINVAL;
+
+ /* Parse all fwm tables */
+ table = (struct fs_fwm_table *)amp_lib->hdr->params;
+ index = (struct fs_fwm_index *)table->buf;
+ count = table->size / sizeof(*index);
+
+ for (idx = 0; idx < count; idx++, index++) {
+ if (index->type >= FS_INDEX_MAX)
+ return -ERANGE;
+ ptr = (char *)table + (int)index->offset;
+ amp_lib->table[index->type] = (struct fs_fwm_table *)ptr;
+ }
+
+ /* Parse all scene tables */
+ ret = fs_parse_scene_tables(amp_lib);
+ if (ret)
+ dev_err(amp_lib->dev, "Failed to parse scene: %d\n", ret);
+
+ return ret;
+}
+
+static int fs_verify_firmware(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_header *hdr;
+ int crcsum;
+
+ if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+ return -EINVAL;
+
+ hdr = amp_lib->hdr;
+
+ /* Verify the crcsum code */
+ crcsum = crc16(0x0000, (const char *)&hdr->crc_size, hdr->crc_size);
+ if (crcsum != hdr->crc16) {
+ dev_err(amp_lib->dev, "Failed to checksum: %x-%x\n",
+ crcsum, hdr->crc16);
+ return -EFAULT;
+ }
+
+ /* Verify the devid(chip_type) */
+ if (amp_lib->devid != LO_U16(hdr->chip_type)) {
+ dev_err(amp_lib->dev, "DEVID dismatch: %04X#%04X\n",
+ amp_lib->devid, hdr->chip_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void fs_print_firmware_info(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_header *hdr;
+ const char *pro_name = NULL;
+ const char *dev_name = NULL;
+
+ if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+ return;
+
+ hdr = amp_lib->hdr;
+
+ fs_get_fwm_string(amp_lib, hdr->project, &pro_name);
+ fs_get_fwm_string(amp_lib, hdr->device, &dev_name);
+
+ dev_info(amp_lib->dev, "Project: %s Device: %s\n",
+ pro_name ? pro_name : "null",
+ dev_name ? dev_name : "null");
+
+ dev_info(amp_lib->dev, "Date: %04d%02d%02d-%02d%02d\n",
+ hdr->date.year, hdr->date.month, hdr->date.day,
+ hdr->date.hour, hdr->date.minute);
+}
+
+int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name)
+{
+ const struct firmware *cont;
+ struct fs_fwm_header *hdr;
+ int ret;
+
+ if (!amp_lib || !amp_lib->dev || !name)
+ return -EINVAL;
+
+ ret = request_firmware(&cont, name, amp_lib->dev);
+ if (ret) {
+ dev_err(amp_lib->dev, "Failed to request %s: %d\n", name, ret);
+ return ret;
+ }
+
+ dev_info(amp_lib->dev, "Loading %s - size: %zu\n", name, cont->size);
+
+ hdr = devm_kmemdup(amp_lib->dev, cont->data, cont->size, GFP_KERNEL);
+ release_firmware(cont);
+ if (!hdr)
+ return -ENOMEM;
+
+ amp_lib->hdr = hdr;
+ ret = fs_verify_firmware(amp_lib);
+ if (ret) {
+ amp_lib->hdr = NULL;
+ return ret;
+ }
+
+ ret = fs_parse_all_tables(amp_lib);
+ if (ret) {
+ amp_lib->hdr = NULL;
+ return ret;
+ }
+
+ fs_print_firmware_info(amp_lib);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fs_amp_load_firmware);
+
+MODULE_AUTHOR("Nick Li <nick.li@foursemi.com>");
+MODULE_DESCRIPTION("FourSemi audio amplifier library");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs-amp-lib.h b/sound/soc/codecs/fs-amp-lib.h
new file mode 100644
index 0000000..4a77c7b
--- /dev/null
+++ b/sound/soc/codecs/fs-amp-lib.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * fs-amp-lib.h --- Common library for FourSemi Audio Amplifiers
+ *
+ * Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+ */
+
+#ifndef __FS_AMP_LIB_H__
+#define __FS_AMP_LIB_H__
+
+#define HI_U16(a) (((a) >> 8) & 0xFF)
+#define LO_U16(a) ((a) & 0xFF)
+#define FS_TABLE_NAME_LEN (4)
+#define FS_SCENE_COUNT_MAX (16)
+#define FS_CMD_DELAY_MS_MAX (100) /* 100ms */
+
+#define FS_CMD_DELAY (0xFF)
+#define FS_CMD_BURST (0xFE)
+#define FS_CMD_UPDATE (0xFD)
+
+#define FS_SOC_ENUM_EXT(xname, xhandler_info, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = xhandler_info, \
+ .get = xhandler_get, .put = xhandler_put \
+}
+
+enum fs_index_type {
+ FS_INDEX_INFO = 0,
+ FS_INDEX_STCOEF,
+ FS_INDEX_SCENE,
+ FS_INDEX_MODEL,
+ FS_INDEX_REG,
+ FS_INDEX_EFFECT,
+ FS_INDEX_STRING,
+ FS_INDEX_WOOFER,
+ FS_INDEX_MAX,
+};
+
+#pragma pack(push, 1)
+
+struct fs_reg_val {
+ u8 reg;
+ u16 val;
+};
+
+struct fs_reg_bits {
+ u8 cmd; /* FS_CMD_UPDATE */
+ u8 reg;
+ u16 val;
+ u16 mask;
+};
+
+struct fs_cmd_pkg {
+ union {
+ u8 cmd;
+ struct fs_reg_val regv;
+ struct fs_reg_bits regb;
+ };
+};
+
+struct fs_fwm_index {
+ /* Index type */
+ u16 type;
+ /* Offset address starting from the end of header */
+ u16 offset;
+};
+
+struct fs_fwm_table {
+ char name[FS_TABLE_NAME_LEN];
+ u16 size; /* size of buf */
+ u8 buf[];
+};
+
+struct fs_scene_index {
+ /* Offset address(scene name) in string table */
+ u16 name;
+ /* Offset address(scene reg) in register table */
+ u16 reg;
+ /* Offset address(scene model) in model table */
+ u16 model;
+ /* Offset address(scene effect) in effect table */
+ u16 effect;
+};
+
+struct fs_reg_table {
+ u16 size; /* size of buf */
+ u8 buf[];
+};
+
+struct fs_file_table {
+ u16 name;
+ u16 size; /* size of buf */
+ u8 buf[];
+};
+
+struct fs_fwm_date {
+ u32 year:12;
+ u32 month:4;
+ u32 day:5;
+ u32 hour:5;
+ u32 minute:6;
+};
+
+struct fs_fwm_header {
+ u16 version;
+ u16 project; /* Offset address(project name) in string table */
+ u16 device; /* Offset address(device name) in string table */
+ struct fs_fwm_date date;
+ u16 crc16;
+ u16 crc_size; /* Starting position for CRC checking */
+ u16 chip_type;
+ u16 addr; /* 7-bit i2c address */
+ u16 spkid;
+ u16 rsvd[6];
+ u8 params[];
+};
+
+#pragma pack(pop)
+
+struct fs_i2s_srate {
+ u32 srate; /* Sample rate */
+ u16 i2ssr; /* Value of Bit field[I2SSR] */
+};
+
+struct fs_pll_div {
+ unsigned int bclk; /* Rate of bit clock */
+ u16 pll1;
+ u16 pll2;
+ u16 pll3;
+};
+
+struct fs_amp_scene {
+ const char *name;
+ const struct fs_reg_table *reg;
+ const struct fs_file_table *model;
+ const struct fs_file_table *effect;
+};
+
+struct fs_amp_lib {
+ const struct fs_fwm_header *hdr;
+ const struct fs_fwm_table *table[FS_INDEX_MAX];
+ struct fs_amp_scene *scene;
+ struct device *dev;
+ int scene_count;
+ u16 devid;
+};
+
+int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name);
+
+#endif // __FS_AMP_LIB_H__
diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c
new file mode 100644
index 0000000..e2f8571
--- /dev/null
+++ b/sound/soc/codecs/fs210x.c
@@ -0,0 +1,1586 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// fs210x.c -- Driver for the FS2104/5S Audio Amplifier
+//
+// Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "fs210x.h"
+#include "fs-amp-lib.h"
+
+#define FS210X_DEFAULT_FWM_NAME "fs210x_fwm.bin"
+#define FS210X_DEFAULT_DAI_NAME "fs210x-aif"
+#define FS2105S_DEVICE_ID 0x20 /* FS2105S */
+#define FS210X_DEVICE_ID 0x45 /* FS2104 */
+#define FS210X_REG_MAX 0xF8
+#define FS210X_INIT_SCENE 0
+#define FS210X_DEFAULT_SCENE 1
+#define FS210X_START_DELAY_MS 5
+#define FS210X_FAULT_CHECK_INTERVAL_MS 2000
+#define FS2105S_RATES (SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+#define FS210X_RATES (SNDRV_PCM_RATE_16000 | FS2105S_RATES)
+#define FS210X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define FS210X_NUM_SUPPLIES ARRAY_SIZE(fs210x_supply_names)
+
+static const char *const fs210x_supply_names[] = {
+ "pvdd",
+ "dvdd",
+};
+
+struct fs210x_platform_data {
+ const char *fwm_name;
+};
+
+struct fs210x_priv {
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct regmap *regmap;
+ struct fs210x_platform_data pdata;
+ struct regulator_bulk_data supplies[FS210X_NUM_SUPPLIES];
+ struct gpio_desc *gpio_sdz;
+ struct delayed_work start_work;
+ struct delayed_work fault_check_work;
+ struct fs_amp_lib amp_lib;
+ const struct fs_amp_scene *cur_scene;
+ struct clk *clk_bclk;
+ /*
+ * @lock: Mutex ensuring exclusive access for critical device operations
+ *
+ * This lock serializes access between the following actions:
+ * - Device initialization procedures(probe)
+ * - Enable/disable device(DAPM event)
+ * - Suspend/resume device(PM)
+ * - Runtime scene switching(control)
+ * - Scheduling/execution of delayed works items(delayed works)
+ */
+ struct mutex lock;
+ unsigned int check_interval_ms;
+ unsigned int bclk;
+ unsigned int srate;
+ int scene_id;
+ u16 devid;
+ bool is_inited;
+ bool is_suspended;
+ bool is_bclk_on;
+ bool is_playing;
+};
+
+static const unsigned int fs2105s_rates[] = {
+ 32000, 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list fs2105s_constraints = {
+ .count = ARRAY_SIZE(fs2105s_rates),
+ .list = fs2105s_rates,
+};
+
+static const unsigned int fs210x_rates[] = {
+ 16000, 32000, 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list fs210x_constraints = {
+ .count = ARRAY_SIZE(fs210x_rates),
+ .list = fs210x_rates,
+};
+
+static const struct fs_pll_div fs210x_pll_div[] = {
+ /* bclk, pll1, pll2, pll3 */
+ { 512000, 0x006C, 0x0120, 0x0001 },
+ { 768000, 0x016C, 0x00C0, 0x0001 },
+ { 1024000, 0x016C, 0x0090, 0x0001 },
+ { 1536000, 0x016C, 0x0060, 0x0001 },
+ { 2048000, 0x016C, 0x0090, 0x0002 },
+ { 2304000, 0x016C, 0x0080, 0x0002 },
+ { 3072000, 0x016C, 0x0090, 0x0003 },
+ { 4096000, 0x016C, 0x0090, 0x0004 },
+ { 4608000, 0x016C, 0x0080, 0x0004 },
+ { 6144000, 0x016C, 0x0090, 0x0006 },
+ { 8192000, 0x016C, 0x0090, 0x0008 },
+ { 9216000, 0x016C, 0x0090, 0x0009 },
+ { 12288000, 0x016C, 0x0090, 0x000C },
+ { 16384000, 0x016C, 0x0090, 0x0010 },
+ { 18432000, 0x016C, 0x0090, 0x0012 },
+ { 24576000, 0x016C, 0x0090, 0x0018 },
+ { 1411200, 0x016C, 0x0060, 0x0001 },
+ { 2116800, 0x016C, 0x0080, 0x0002 },
+ { 2822400, 0x016C, 0x0090, 0x0003 },
+ { 4233600, 0x016C, 0x0080, 0x0004 },
+ { 5644800, 0x016C, 0x0090, 0x0006 },
+ { 8467200, 0x016C, 0x0090, 0x0009 },
+ { 11289600, 0x016C, 0x0090, 0x000C },
+ { 16934400, 0x016C, 0x0090, 0x0012 },
+ { 22579200, 0x016C, 0x0090, 0x0018 },
+ { 2000000, 0x017C, 0x0093, 0x0002 },
+};
+
+static int fs210x_bclk_set(struct fs210x_priv *fs210x, bool on)
+{
+ int ret = 0;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ if ((fs210x->is_bclk_on ^ on) == 0)
+ return 0;
+
+ if (on) {
+ clk_set_rate(fs210x->clk_bclk, fs210x->bclk);
+ ret = clk_prepare_enable(fs210x->clk_bclk);
+ fs210x->is_bclk_on = true;
+ fsleep(2000); /* >= 2ms */
+ } else {
+ clk_disable_unprepare(fs210x->clk_bclk);
+ fs210x->is_bclk_on = false;
+ }
+
+ return ret;
+}
+
+static int fs210x_reg_write(struct fs210x_priv *fs210x,
+ u8 reg, u16 val)
+{
+ int ret;
+
+ ret = regmap_write(fs210x->regmap, reg, val);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to write %02Xh: %d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_reg_read(struct fs210x_priv *fs210x,
+ u8 reg, u16 *pval)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(fs210x->regmap, reg, &val);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to read %02Xh: %d\n", reg, ret);
+ return ret;
+ }
+
+ *pval = (u16)val;
+
+ return 0;
+}
+
+static int fs210x_reg_update_bits(struct fs210x_priv *fs210x,
+ u8 reg, u16 mask, u16 val)
+{
+ int ret;
+
+ ret = regmap_update_bits(fs210x->regmap, reg, mask, val);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to update %02Xh: %d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_reg_bulk_write(struct fs210x_priv *fs210x,
+ u8 reg, const void *val, u32 size)
+{
+ int ret;
+
+ ret = regmap_bulk_write(fs210x->regmap, reg, val, size / 2);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to bulk write %02Xh: %d\n",
+ reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int fs210x_write_reg_val(struct fs210x_priv *fs210x,
+ const struct fs_reg_val *regv)
+{
+ return fs210x_reg_write(fs210x, regv->reg, regv->val);
+}
+
+static inline int fs210x_write_reg_bits(struct fs210x_priv *fs210x,
+ const struct fs_reg_bits *regu)
+{
+ return fs210x_reg_update_bits(fs210x,
+ regu->reg,
+ regu->mask,
+ regu->val);
+}
+
+static inline int fs210x_set_cmd_pkg(struct fs210x_priv *fs210x,
+ const struct fs_cmd_pkg *pkg,
+ unsigned int *offset)
+{
+ int delay_us;
+
+ if (pkg->cmd >= 0x00 && pkg->cmd <= FS210X_REG_MAX) {
+ *offset = sizeof(pkg->regv);
+ return fs210x_write_reg_val(fs210x, &pkg->regv);
+ } else if (pkg->cmd == FS_CMD_UPDATE) {
+ *offset = sizeof(pkg->regb);
+ return fs210x_write_reg_bits(fs210x, &pkg->regb);
+ } else if (pkg->cmd == FS_CMD_DELAY) {
+ if (pkg->regv.val > FS_CMD_DELAY_MS_MAX)
+ return -EOPNOTSUPP;
+ delay_us = pkg->regv.val * 1000; /* ms -> us */
+ fsleep(delay_us);
+ *offset = sizeof(pkg->regv);
+ return 0;
+ }
+
+ dev_err(fs210x->dev, "Invalid pkg cmd: %d\n", pkg->cmd);
+
+ return -EOPNOTSUPP;
+}
+
+static int fs210x_reg_write_table(struct fs210x_priv *fs210x,
+ const struct fs_reg_table *reg)
+{
+ const struct fs_cmd_pkg *pkg;
+ unsigned int index, offset;
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ if (!reg || reg->size == 0)
+ return -EFAULT;
+
+ for (index = 0; index < reg->size; index += offset) {
+ pkg = (struct fs_cmd_pkg *)(reg->buf + index);
+ ret = fs210x_set_cmd_pkg(fs210x, pkg, &offset);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set cmd pkg: %02X-%d\n",
+ pkg->cmd, ret);
+ return ret;
+ }
+ }
+
+ if (index != reg->size) {
+ dev_err(fs210x->dev, "Invalid reg table size: %d-%d\n",
+ index, reg->size);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int fs210x_dev_play(struct fs210x_priv *fs210x)
+{
+ int ret;
+
+ if (!fs210x->is_inited)
+ return -EFAULT;
+
+ if (fs210x->is_playing)
+ return 0;
+
+ ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PLAY);
+ if (!ret)
+ fs210x->is_playing = true;
+
+ fsleep(10000); /* >= 10ms */
+
+ return ret;
+}
+
+static int fs210x_dev_stop(struct fs210x_priv *fs210x)
+{
+ int ret;
+
+ if (!fs210x->is_inited)
+ return -EFAULT;
+
+ if (!fs210x->is_playing)
+ return 0;
+
+ ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PWDN);
+ fs210x->is_playing = false;
+
+ fsleep(30000); /* >= 30ms */
+
+ return ret;
+}
+
+static int fs210x_set_reg_table(struct fs210x_priv *fs210x,
+ const struct fs_amp_scene *scene)
+{
+ const struct fs_amp_scene *cur_scene;
+ const struct fs_reg_table *reg;
+
+ if (!fs210x || !fs210x->dev || !scene)
+ return -EINVAL;
+
+ cur_scene = fs210x->cur_scene;
+ if (!scene->reg || cur_scene == scene) {
+ dev_dbg(fs210x->dev, "Skip writing reg table\n");
+ return 0;
+ }
+
+ reg = scene->reg;
+ dev_dbg(fs210x->dev, "reg table size: %d\n", reg->size);
+
+ return fs210x_reg_write_table(fs210x, reg);
+}
+
+static int fs210x_set_woofer_table(struct fs210x_priv *fs210x)
+{
+ const struct fs_file_table *woofer;
+ const struct fs_fwm_table *table;
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ /* NOTE: fs2105s has woofer ram only */
+ if (fs210x->devid != FS2105S_DEVICE_ID)
+ return 0;
+
+ table = fs210x->amp_lib.table[FS_INDEX_WOOFER];
+ if (!table) {
+ dev_dbg(fs210x->dev, "Skip writing woofer table\n");
+ return 0;
+ }
+
+ woofer = (struct fs_file_table *)table->buf;
+ dev_dbg(fs210x->dev, "woofer table size: %d\n", woofer->size);
+ /* Unit of woofer data is u32(4 bytes) */
+ if (woofer->size == 0 || (woofer->size & 0x3)) {
+ dev_err(fs210x->dev, "Invalid woofer size: %d\n",
+ woofer->size);
+ return -EINVAL;
+ }
+
+ ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS2105S_46H_CAM_BURST_W);
+ ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+ woofer->buf, woofer->size);
+
+ return ret;
+}
+
+static int fs210x_set_effect_table(struct fs210x_priv *fs210x,
+ const struct fs_amp_scene *scene)
+{
+ const struct fs_amp_scene *cur_scene;
+ const struct fs_file_table *effect;
+ int half_size;
+ int ret;
+
+ if (!fs210x || !fs210x->dev || !scene)
+ return -EINVAL;
+
+ cur_scene = fs210x->cur_scene;
+ if (!scene->effect || cur_scene == scene) {
+ dev_dbg(fs210x->dev, "Skip writing effect table\n");
+ return 0;
+ }
+
+ effect = scene->effect;
+ dev_dbg(fs210x->dev, "effect table size: %d\n", effect->size);
+
+ /* Unit of effect data is u32(4 bytes), 2 channels */
+ if (effect->size == 0 || (effect->size & 0x7)) {
+ dev_err(fs210x->dev, "Invalid effect size: %d\n",
+ effect->size);
+ return -EINVAL;
+ }
+
+ half_size = effect->size / 2;
+
+ /* Left channel */
+ ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS210X_46H_CAM_BURST_L);
+ ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+ effect->buf, half_size);
+ if (ret)
+ return ret;
+
+ /* Right channel */
+ ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS210X_46H_CAM_BURST_R);
+ ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+ effect->buf + half_size, half_size);
+
+ return ret;
+}
+
+static int fs210x_access_dsp_ram(struct fs210x_priv *fs210x, bool enable)
+{
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ if (enable) {
+ ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_HIZ);
+ ret |= fs210x_reg_write(fs210x, FS210X_0BH_ACCKEY,
+ FS210X_0BH_ACCKEY_ON);
+ } else {
+ ret = fs210x_reg_write(fs210x, FS210X_0BH_ACCKEY,
+ FS210X_0BH_ACCKEY_OFF);
+ ret |= fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PWDN);
+ }
+
+ fsleep(10000); /* >= 10ms */
+
+ return ret;
+}
+
+static int fs210x_write_dsp_effect(struct fs210x_priv *fs210x,
+ const struct fs_amp_scene *scene,
+ int scene_id)
+{
+ int ret;
+
+ if (!fs210x || !scene)
+ return -EINVAL;
+
+ ret = fs210x_access_dsp_ram(fs210x, true);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to access dsp: %d\n", ret);
+ goto tag_exit;
+ }
+
+ ret = fs210x_set_effect_table(fs210x, scene);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set effect: %d\n", ret);
+ goto tag_exit;
+ }
+
+ if (scene_id == FS210X_INIT_SCENE)
+ ret = fs210x_set_woofer_table(fs210x);
+
+tag_exit:
+ fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS210X_46H_CAM_CLEAR);
+ fs210x_access_dsp_ram(fs210x, false);
+
+ return ret;
+}
+
+static int fs210x_check_scene(struct fs210x_priv *fs210x,
+ int scene_id, bool *skip_set)
+{
+ struct fs_amp_lib *amp_lib;
+
+ if (!fs210x || !skip_set)
+ return -EINVAL;
+
+ amp_lib = &fs210x->amp_lib;
+ if (amp_lib->scene_count == 0 || !amp_lib->scene) {
+ dev_err(fs210x->dev, "There's no scene data\n");
+ return -EINVAL;
+ }
+
+ if (scene_id < 0 || scene_id >= amp_lib->scene_count) {
+ dev_err(fs210x->dev, "Invalid scene_id: %d\n", scene_id);
+ return -EINVAL;
+ }
+
+ if (fs210x->scene_id == scene_id) {
+ dev_dbg(fs210x->dev, "Skip to set same scene\n");
+ return 0;
+ }
+
+ *skip_set = false;
+
+ return 0;
+}
+
+static int fs210x_set_scene(struct fs210x_priv *fs210x, int scene_id)
+{
+ const struct fs_amp_scene *scene;
+ bool skip_set = true;
+ bool is_playing;
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ ret = fs210x_check_scene(fs210x, scene_id, &skip_set);
+ if (ret || skip_set)
+ return ret;
+
+ scene = fs210x->amp_lib.scene + scene_id;
+ dev_info(fs210x->dev, "Switch scene.%d: %s\n",
+ scene_id, scene->name);
+
+ is_playing = fs210x->is_playing;
+ if (is_playing)
+ fs210x_dev_stop(fs210x);
+
+ ret = fs210x_set_reg_table(fs210x, scene);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = fs210x_write_dsp_effect(fs210x, scene, scene_id);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to write ram: %d\n", ret);
+ return ret;
+ }
+
+ fs210x->cur_scene = scene;
+ fs210x->scene_id = scene_id;
+
+ if (is_playing)
+ fs210x_dev_play(fs210x);
+
+ return 0;
+}
+
+static int fs210x_init_chip(struct fs210x_priv *fs210x)
+{
+ int scene_id;
+ int ret;
+
+ regcache_cache_bypass(fs210x->regmap, true);
+
+ if (!fs210x->gpio_sdz) {
+ /* Gpio is not found, i2c reset */
+ ret = fs210x_reg_write(fs210x, FS210X_10H_PWRCTRL,
+ FS210X_10H_I2C_RESET);
+ if (ret)
+ goto tag_power_down;
+ } else {
+ /* gpio reset, deactivate */
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 0);
+ }
+
+ fsleep(10000); /* >= 10ms */
+
+ /* Backup scene id */
+ scene_id = fs210x->scene_id;
+ fs210x->scene_id = -1;
+
+ /* Init registers/RAM by init scene */
+ ret = fs210x_set_scene(fs210x, FS210X_INIT_SCENE);
+ if (ret)
+ goto tag_power_down;
+
+ /*
+ * If the firmware has effect scene(s),
+ * we load effect scene by default scene or scene_id
+ */
+ if (fs210x->amp_lib.scene_count > 1) {
+ if (scene_id < FS210X_DEFAULT_SCENE)
+ scene_id = FS210X_DEFAULT_SCENE;
+ ret = fs210x_set_scene(fs210x, scene_id);
+ if (ret)
+ goto tag_power_down;
+ }
+
+tag_power_down:
+ /* Power down the device */
+ ret |= fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PWDN);
+ fsleep(10000); /* >= 10ms */
+
+ regcache_cache_bypass(fs210x->regmap, false);
+ if (!ret) {
+ regcache_mark_dirty(fs210x->regmap);
+ regcache_sync(fs210x->regmap);
+ fs210x->is_inited = true;
+ }
+
+ return ret;
+}
+
+static int fs210x_set_i2s_params(struct fs210x_priv *fs210x)
+{
+ const struct fs_i2s_srate params[] = {
+ { 16000, 0x3 },
+ { 32000, 0x7 },
+ { 44100, 0x8 },
+ { 48000, 0x9 },
+ { 88200, 0xA },
+ { 96000, 0xB },
+ };
+ u16 val;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(params); i++) {
+ if (params[i].srate != fs210x->srate)
+ continue;
+ val = params[i].i2ssr << FS210X_17H_I2SSR_SHIFT;
+ ret = fs210x_reg_update_bits(fs210x,
+ FS210X_17H_I2SCTRL,
+ FS210X_17H_I2SSR_MASK,
+ val);
+ return ret;
+ }
+
+ dev_err(fs210x->dev, "Invalid sample rate: %d\n", fs210x->srate);
+
+ return -EINVAL;
+}
+
+static int fs210x_get_pll_div(struct fs210x_priv *fs210x,
+ const struct fs_pll_div **pll_div)
+{
+ int i;
+
+ if (!fs210x || !pll_div)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(fs210x_pll_div); i++) {
+ if (fs210x_pll_div[i].bclk != fs210x->bclk)
+ continue;
+ *pll_div = fs210x_pll_div + i;
+ return 0;
+ }
+
+ dev_err(fs210x->dev, "No PLL table for bclk: %d\n", fs210x->bclk);
+
+ return -EFAULT;
+}
+
+static int fs210x_set_hw_params(struct fs210x_priv *fs210x)
+{
+ const struct fs_pll_div *pll_div;
+ int ret;
+
+ ret = fs210x_set_i2s_params(fs210x);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set i2s params: %d\n", ret);
+ return ret;
+ }
+
+ /* Set pll params */
+ ret = fs210x_get_pll_div(fs210x, &pll_div);
+ if (ret)
+ return ret;
+
+ ret = fs210x_reg_write(fs210x, FS210X_A1H_PLLCTRL1, pll_div->pll1);
+ ret |= fs210x_reg_write(fs210x, FS210X_A2H_PLLCTRL2, pll_div->pll2);
+ ret |= fs210x_reg_write(fs210x, FS210X_A3H_PLLCTRL3, pll_div->pll3);
+
+ return ret;
+}
+
+static int fs210x_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ const struct snd_pcm_hw_constraint_list *list;
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+ if (!fs210x) {
+ pr_err("dai_startup: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ if (!substream->runtime)
+ return 0;
+
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ FS210X_FORMATS);
+ if (ret < 0) {
+ dev_err(fs210x->dev,
+ "Failed to set hw param format: %d\n", ret);
+ return ret;
+ }
+
+ if (fs210x->devid == FS2105S_DEVICE_ID)
+ list = &fs2105s_constraints;
+ else
+ list = &fs210x_constraints;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ list);
+ if (ret < 0) {
+ dev_err(fs210x->dev,
+ "Failed to set hw param rate: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct fs210x_priv *fs210x;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Only supports consumer mode */
+ break;
+ default:
+ dev_err(fs210x->dev, "Only supports consumer mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fs210x_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fs210x_priv *fs210x;
+ int chn_num;
+ int ret;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ fs210x->srate = params_rate(params);
+ fs210x->bclk = snd_soc_params_to_bclk(params);
+ chn_num = params_channels(params);
+ if (chn_num == 1) /* mono */
+ fs210x->bclk *= 2; /* I2S bus has 2 channels */
+
+ /* The FS2105S can't support 16kHz sample rate. */
+ if (fs210x->devid == FS2105S_DEVICE_ID && fs210x->srate == 16000)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&fs210x->lock);
+ ret = fs210x_set_hw_params(fs210x);
+ mutex_unlock(&fs210x->lock);
+ if (ret)
+ dev_err(fs210x->dev, "Failed to set hw params: %d\n", ret);
+
+ return ret;
+}
+
+static int fs210x_dai_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct fs210x_priv *fs210x;
+ unsigned long delay;
+
+ if (stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ mutex_lock(&fs210x->lock);
+
+ if (!fs210x->is_inited || fs210x->is_suspended) {
+ mutex_unlock(&fs210x->lock);
+ return 0;
+ }
+
+ mutex_unlock(&fs210x->lock);
+
+ if (mute) {
+ cancel_delayed_work_sync(&fs210x->fault_check_work);
+ cancel_delayed_work_sync(&fs210x->start_work);
+ } else {
+ delay = msecs_to_jiffies(fs210x->check_interval_ms);
+ schedule_delayed_work(&fs210x->fault_check_work, delay);
+ }
+
+ return 0;
+}
+
+static int fs210x_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct fs210x_priv *fs210x;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ mutex_lock(&fs210x->lock);
+
+ if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) {
+ mutex_unlock(&fs210x->lock);
+ return 0;
+ }
+
+ mutex_unlock(&fs210x->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /*
+ * According to the power up/down sequence of FS210x,
+ * it requests the I2S clock has been present
+ * and stable(>= 2ms) before playing.
+ */
+ schedule_delayed_work(&fs210x->start_work,
+ msecs_to_jiffies(FS210X_START_DELAY_MS));
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void fs210x_start_work(struct work_struct *work)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = container_of(work, struct fs210x_priv, start_work.work);
+
+ mutex_lock(&fs210x->lock);
+
+ ret = fs210x_dev_play(fs210x);
+ if (ret)
+ dev_err(fs210x->dev, "Failed to start playing: %d\n", ret);
+
+ mutex_unlock(&fs210x->lock);
+}
+
+static void fs210x_fault_check_work(struct work_struct *work)
+{
+ struct fs210x_priv *fs210x;
+ u16 status;
+ int ret;
+
+ fs210x = container_of(work, struct fs210x_priv, fault_check_work.work);
+
+ mutex_lock(&fs210x->lock);
+
+ if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) {
+ mutex_unlock(&fs210x->lock);
+ return;
+ }
+
+ ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status);
+ mutex_unlock(&fs210x->lock);
+ if (ret)
+ return;
+
+ if (!(status & FS210X_05H_PVDD_MASK))
+ dev_err(fs210x->dev, "PVDD fault\n");
+ if (status & FS210X_05H_OCDL_MASK)
+ dev_err(fs210x->dev, "OC detected\n");
+ if (status & FS210X_05H_UVDL_MASK)
+ dev_err(fs210x->dev, "UV detected\n");
+ if (status & FS210X_05H_OVDL_MASK)
+ dev_err(fs210x->dev, "OV detected\n");
+ if (status & FS210X_05H_OTPDL_MASK)
+ dev_err(fs210x->dev, "OT detected\n");
+ if (status & FS210X_05H_OCRDL_MASK)
+ dev_err(fs210x->dev, "OCR detected\n");
+ if (status & FS210X_05H_OCLDL_MASK)
+ dev_err(fs210x->dev, "OCL detected\n");
+ if (status & FS210X_05H_DCRDL_MASK)
+ dev_err(fs210x->dev, "DCR detected\n");
+ if (status & FS210X_05H_DCLDL_MASK)
+ dev_err(fs210x->dev, "DCL detected\n");
+ if (status & FS210X_05H_SRDL_MASK)
+ dev_err(fs210x->dev, "SR detected\n");
+ if (status & FS210X_05H_OTWDL_MASK)
+ dev_err(fs210x->dev, "OTW detected\n");
+ if (!(status & FS210X_05H_AMPS_MASK))
+ dev_dbg(fs210x->dev, "Amplifier unready\n");
+ if (!(status & FS210X_05H_PLLS_MASK))
+ dev_err(fs210x->dev, "PLL unlock\n");
+ if (!(status & FS210X_05H_ANAS_MASK))
+ dev_err(fs210x->dev, "Analog power fault\n");
+
+ schedule_delayed_work(&fs210x->fault_check_work,
+ msecs_to_jiffies(fs210x->check_interval_ms));
+}
+
+static int fs210x_get_drvdata_from_kctrl(struct snd_kcontrol *kctrl,
+ struct fs210x_priv **fs210x)
+{
+ struct snd_soc_component *cmpnt;
+
+ if (!kctrl) {
+ pr_err("fs210x: kcontrol is null\n");
+ return -EINVAL;
+ }
+
+ cmpnt = snd_soc_kcontrol_component(kctrl);
+ if (!cmpnt) {
+ pr_err("fs210x: component is null\n");
+ return -EINVAL;
+ }
+
+ *fs210x = snd_soc_component_get_drvdata(cmpnt);
+
+ return 0;
+}
+
+static int fs210x_effect_scene_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const struct fs_amp_scene *scene;
+ struct fs210x_priv *fs210x;
+ const char *name = "N/A";
+ int idx, count;
+ int ret;
+
+ ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+ if (ret || !fs210x->dev) {
+ pr_err("scene_effect_info: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */
+ if (count < 1) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ idx = uinfo->value.enumerated.item;
+ scene = fs210x->amp_lib.scene + idx + 1;
+ if (scene->name)
+ name = scene->name;
+
+ strscpy(uinfo->value.enumerated.name, name, strlen(name) + 1);
+
+ return 0;
+}
+
+static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct fs210x_priv *fs210x;
+ int index;
+ int ret;
+
+ ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+ if (ret || !fs210x->dev) {
+ pr_err("scene_effect_get: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ /* The id of effect scene is from 1 to N. */
+ if (fs210x->scene_id < 1)
+ return -EINVAL;
+
+ mutex_lock(&fs210x->lock);
+ /*
+ * FS210x has scene(s) as below:
+ * init scene: id = 0
+ * effect scene(s): id = 1~N (optional)
+ * effect_index = scene_id - 1
+ */
+ index = fs210x->scene_id - 1;
+ ucontrol->value.integer.value[0] = index;
+ mutex_unlock(&fs210x->lock);
+
+ return 0;
+}
+
+static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct fs210x_priv *fs210x;
+ int scene_id, scene_count;
+ bool is_changed = false;
+ int ret;
+
+ ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+ if (ret || !fs210x->dev) {
+ pr_err("scene_effect_put: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&fs210x->lock);
+
+ /*
+ * FS210x has scene(s) as below:
+ * init scene: id = 0 (It's set in fs210x_init_chip() only)
+ * effect scene(s): id = 1~N (optional)
+ * scene_id = effect_index + 1.
+ */
+ scene_id = ucontrol->value.integer.value[0] + 1;
+ scene_count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */
+ if (scene_id < 1 || scene_id > scene_count) {
+ mutex_unlock(&fs210x->lock);
+ return -ERANGE;
+ }
+
+ if (scene_id != fs210x->scene_id)
+ is_changed = true;
+
+ if (fs210x->is_suspended) {
+ fs210x->scene_id = scene_id;
+ mutex_unlock(&fs210x->lock);
+ return is_changed;
+ }
+
+ ret = fs210x_set_scene(fs210x, scene_id);
+ if (ret)
+ dev_err(fs210x->dev, "Failed to set scene: %d\n", ret);
+
+ mutex_unlock(&fs210x->lock);
+
+ if (!ret && is_changed)
+ return 1;
+
+ return ret;
+}
+
+static int fs210x_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct fs210x_priv *fs210x = snd_soc_component_get_drvdata(cmpnt);
+ int ret = 0;
+
+ mutex_lock(&fs210x->lock);
+
+ if (fs210x->is_suspended) {
+ mutex_unlock(&fs210x->lock);
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /*
+ * If there is no bclk for us to set the clock output,
+ * we will enable the device(start_work) in dai trigger.
+ */
+ if (!fs210x->clk_bclk)
+ break;
+ fs210x_bclk_set(fs210x, true);
+ ret = fs210x_dev_play(fs210x);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = fs210x_dev_stop(fs210x);
+ fs210x_bclk_set(fs210x, false);
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&fs210x->lock);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops fs210x_dai_ops = {
+ .startup = fs210x_dai_startup,
+ .set_fmt = fs210x_dai_set_fmt,
+ .hw_params = fs210x_dai_hw_params,
+ .mute_stream = fs210x_dai_mute,
+ .trigger = fs210x_dai_trigger,
+};
+
+static const struct snd_soc_dai_driver fs210x_dai = {
+ .name = FS210X_DEFAULT_DAI_NAME,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = FS210X_RATES,
+ .formats = FS210X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = FS210X_RATES,
+ .formats = FS210X_FORMATS,
+ },
+ .ops = &fs210x_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static const DECLARE_TLV_DB_SCALE(fs2105s_vol_tlv, -9709, 19, 1);
+static const DECLARE_TLV_DB_SCALE(fs210x_vol_tlv, -13357, 19, 1);
+
+static const struct snd_kcontrol_new fs2105s_vol_control[] = {
+ SOC_DOUBLE_R_TLV("PCM Playback Volume",
+ FS210X_39H_LVOLCTRL, FS210X_3AH_RVOLCTRL,
+ 7, 0x1FF, 0, fs2105s_vol_tlv),
+};
+
+static const struct snd_kcontrol_new fs210x_vol_control[] = {
+ SOC_DOUBLE_R_TLV("PCM Playback Volume",
+ FS210X_39H_LVOLCTRL, FS210X_3AH_RVOLCTRL,
+ 6, 0x2BF, 0, fs210x_vol_tlv),
+};
+
+static const struct snd_kcontrol_new fs210x_controls[] = {
+ SOC_DOUBLE("DAC Mute Switch", FS210X_30H_DACCTRL, 4, 8, 1, 0),
+ SOC_DOUBLE("DAC Fade Switch", FS210X_30H_DACCTRL, 5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new fs210x_scene_control[] = {
+ FS_SOC_ENUM_EXT("Effect Scene",
+ fs210x_effect_scene_info,
+ fs210x_effect_scene_get,
+ fs210x_effect_scene_put),
+};
+
+static const struct snd_soc_dapm_widget fs210x_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0,
+ fs210x_playback_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+ SND_SOC_DAPM_INPUT("SDO"),
+};
+
+static const struct snd_soc_dapm_route fs210x_dapm_routes[] = {
+ { "OUTL", NULL, "AIF IN" },
+ { "OUTR", NULL, "AIF IN" },
+ { "AIF OUT", NULL, "SDO" },
+};
+
+static int fs210x_add_mixer_controls(struct fs210x_priv *fs210x,
+ struct snd_soc_component *cmpnt)
+{
+ const struct snd_kcontrol_new *kctrl;
+ int count;
+ int ret;
+
+ if (!fs210x || !cmpnt)
+ return -EINVAL;
+
+ if (fs210x->devid == FS2105S_DEVICE_ID) {
+ kctrl = fs2105s_vol_control;
+ count = ARRAY_SIZE(fs2105s_vol_control);
+ } else {
+ kctrl = fs210x_vol_control;
+ count = ARRAY_SIZE(fs210x_vol_control);
+ }
+
+ ret = snd_soc_add_component_controls(cmpnt, kctrl, count);
+ if (ret)
+ return ret;
+
+ /*
+ * If the firmware has no scene or only init scene,
+ * we skip adding this mixer control.
+ */
+ if (fs210x->amp_lib.scene_count < 2)
+ return 0;
+
+ kctrl = fs210x_scene_control;
+ count = ARRAY_SIZE(fs210x_scene_control);
+
+ return snd_soc_add_component_controls(cmpnt, kctrl, count);
+}
+
+static int fs210x_probe(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ fs210x->amp_lib.dev = fs210x->dev;
+ fs210x->amp_lib.devid = fs210x->devid;
+
+ ret = fs_amp_load_firmware(&fs210x->amp_lib, fs210x->pdata.fwm_name);
+ if (ret)
+ return ret;
+
+ ret = fs210x_add_mixer_controls(fs210x, cmpnt);
+ if (ret)
+ return ret;
+
+ mutex_lock(&fs210x->lock);
+ ret = fs210x_init_chip(fs210x);
+ mutex_unlock(&fs210x->lock);
+
+ return ret;
+}
+
+static void fs210x_remove(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return;
+
+ cancel_delayed_work_sync(&fs210x->start_work);
+ cancel_delayed_work_sync(&fs210x->fault_check_work);
+}
+
+#ifdef CONFIG_PM
+static int fs210x_suspend(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ regcache_cache_only(fs210x->regmap, true);
+
+ mutex_lock(&fs210x->lock);
+ fs210x->cur_scene = NULL;
+ fs210x->is_inited = false;
+ fs210x->is_playing = false;
+ fs210x->is_suspended = true;
+
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */
+ fsleep(30000); /* >= 30ms */
+ mutex_unlock(&fs210x->lock);
+
+ cancel_delayed_work_sync(&fs210x->start_work);
+ cancel_delayed_work_sync(&fs210x->fault_check_work);
+
+ ret = regulator_bulk_disable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to suspend: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_resume(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ ret = regulator_bulk_enable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&fs210x->lock);
+
+ fs210x->is_suspended = false;
+ ret = fs210x_init_chip(fs210x);
+
+ mutex_unlock(&fs210x->lock);
+
+ return ret;
+}
+#else
+#define fs210x_suspend NULL
+#define fs210x_resume NULL
+#endif // CONFIG_PM
+
+static bool fs210x_volatile_registers(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FS210X_00H_STATUS ... FS210X_0FH_I2CADDR:
+ case FS210X_ABH_INTSTAT:
+ case FS210X_ACH_INTSTATR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct snd_soc_component_driver fs210x_soc_component_dev = {
+ .probe = fs210x_probe,
+ .remove = fs210x_remove,
+ .suspend = fs210x_suspend,
+ .resume = fs210x_resume,
+ .controls = fs210x_controls,
+ .num_controls = ARRAY_SIZE(fs210x_controls),
+ .dapm_widgets = fs210x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(fs210x_dapm_widgets),
+ .dapm_routes = fs210x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(fs210x_dapm_routes),
+};
+
+static const struct regmap_config fs210x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = FS210X_REG_MAX,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = fs210x_volatile_registers,
+};
+
+static int fs210x_detect_device(struct fs210x_priv *fs210x)
+{
+ u16 devid;
+ int ret;
+
+ ret = fs210x_reg_read(fs210x, FS210X_03H_DEVID, &devid);
+ if (ret)
+ return ret;
+
+ fs210x->devid = HI_U16(devid);
+
+ switch (fs210x->devid) {
+ case FS210X_DEVICE_ID:
+ dev_info(fs210x->dev, "FS2104 detected\n");
+ break;
+ case FS2105S_DEVICE_ID:
+ dev_info(fs210x->dev, "FS2105S detected\n");
+ break;
+ default:
+ dev_err(fs210x->dev, "DEVID: 0x%04X dismatch\n", devid);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int fs210x_parse_dts(struct fs210x_priv *fs210x,
+ struct fs210x_platform_data *pdata)
+{
+ struct device_node *node = fs210x->dev->of_node;
+ int i, ret;
+
+ if (!node)
+ return 0;
+
+ ret = of_property_read_string(node, "firmware-name", &pdata->fwm_name);
+ if (ret)
+ pdata->fwm_name = FS210X_DEFAULT_FWM_NAME;
+
+ fs210x->gpio_sdz = devm_gpiod_get_optional(fs210x->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(fs210x->gpio_sdz))
+ return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->gpio_sdz),
+ "Failed to get reset-gpios\n");
+
+ for (i = 0; i < FS210X_NUM_SUPPLIES; i++)
+ fs210x->supplies[i].supply = fs210x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(fs210x->dev,
+ ARRAY_SIZE(fs210x->supplies),
+ fs210x->supplies);
+ if (ret)
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to get supplies\n");
+
+ return 0;
+}
+
+static void fs210x_deinit(struct fs210x_priv *fs210x)
+{
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */
+ fsleep(10000); /* >= 10ms */
+
+ regulator_bulk_disable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+}
+
+static int fs210x_init(struct fs210x_priv *fs210x)
+{
+ int ret;
+
+ ret = fs210x_parse_dts(fs210x, &fs210x->pdata);
+ if (ret)
+ return ret;
+
+ fs210x->clk_bclk = devm_clk_get_optional(fs210x->dev, "bclk");
+ if (IS_ERR(fs210x->clk_bclk))
+ return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->clk_bclk),
+ "Failed to get bclk\n");
+
+ ret = regulator_bulk_enable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+ if (ret)
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to enable supplies\n");
+
+ /* Make sure the SDZ pin is pulled down enough time. */
+ fsleep(10000); /* >= 10ms */
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 0); /* Deactivate */
+ fsleep(10000); /* >= 10ms */
+
+ ret = fs210x_detect_device(fs210x);
+ if (ret) {
+ fs210x_deinit(fs210x);
+ return ret;
+ }
+
+ fs210x->scene_id = -1; /* Invalid scene */
+ fs210x->cur_scene = NULL;
+ fs210x->is_playing = false;
+ fs210x->is_inited = false;
+ fs210x->is_suspended = false;
+ fs210x->check_interval_ms = FS210X_FAULT_CHECK_INTERVAL_MS;
+
+ INIT_DELAYED_WORK(&fs210x->fault_check_work, fs210x_fault_check_work);
+ INIT_DELAYED_WORK(&fs210x->start_work, fs210x_start_work);
+ mutex_init(&fs210x->lock);
+
+ return 0;
+}
+
+static int fs210x_register_snd_component(struct fs210x_priv *fs210x)
+{
+ struct snd_soc_dai_driver *dai_drv;
+ static int instance_id;
+ int ret;
+
+ dai_drv = devm_kmemdup(fs210x->dev, &fs210x_dai,
+ sizeof(fs210x_dai), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+
+ dai_drv->name = devm_kasprintf(fs210x->dev,
+ GFP_KERNEL, "%s-%d",
+ dai_drv->name, instance_id);
+ if (!dai_drv->name)
+ return -ENOMEM;
+
+ instance_id++;
+
+ if (fs210x->devid == FS2105S_DEVICE_ID) {
+ dai_drv->playback.rates = FS2105S_RATES;
+ dai_drv->capture.rates = FS2105S_RATES;
+ }
+
+ ret = snd_soc_register_component(fs210x->dev,
+ &fs210x_soc_component_dev,
+ dai_drv, 1);
+ return ret;
+}
+
+static ssize_t check_interval_ms_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct fs210x_priv *fs210x = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", fs210x->check_interval_ms);
+}
+
+static ssize_t check_interval_ms_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf,
+ size_t count)
+{
+ struct fs210x_priv *fs210x = dev_get_drvdata(dev);
+ int ret;
+
+ ret = kstrtouint(buf, 10, &fs210x->check_interval_ms);
+ if (ret)
+ return -EINVAL;
+
+ return (ssize_t)count;
+}
+
+static DEVICE_ATTR_RW(check_interval_ms);
+
+static struct attribute *fs210x_attrs[] = {
+ &dev_attr_check_interval_ms.attr,
+ NULL,
+};
+
+static struct attribute_group fs210x_attr_group = {
+ .attrs = fs210x_attrs,
+};
+
+static int fs210x_i2c_probe(struct i2c_client *client)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = devm_kzalloc(&client->dev, sizeof(*fs210x), GFP_KERNEL);
+ if (!fs210x)
+ return -ENOMEM;
+
+ fs210x->i2c = client;
+ fs210x->dev = &client->dev;
+ i2c_set_clientdata(client, fs210x);
+
+ fs210x->regmap = devm_regmap_init_i2c(client, &fs210x_regmap);
+ if (IS_ERR(fs210x->regmap))
+ return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->regmap),
+ "Failed to get regmap\n");
+
+ ret = fs210x_init(fs210x);
+ if (ret)
+ return ret;
+
+ ret = devm_device_add_group(fs210x->dev, &fs210x_attr_group);
+ if (ret) {
+ fs210x_deinit(fs210x);
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to create sysfs group\n");
+ }
+
+ ret = fs210x_register_snd_component(fs210x);
+ if (ret) {
+ fs210x_deinit(fs210x);
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to register component\n");
+ }
+
+ return 0;
+}
+
+static void fs210x_i2c_remove(struct i2c_client *client)
+{
+ struct fs210x_priv *fs210x = i2c_get_clientdata(client);
+
+ snd_soc_unregister_component(fs210x->dev);
+ fs210x_deinit(fs210x);
+}
+
+static const struct i2c_device_id fs210x_i2c_id[] = {
+ { "fs2104" },
+ { "fs2105s" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, fs210x_i2c_id);
+
+static const struct of_device_id fs210x_of_match[] = {
+ { .compatible = "foursemi,fs2105s", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, fs210x_of_match);
+
+static struct i2c_driver fs210x_i2c_driver = {
+ .driver = {
+ .name = "fs210x",
+ .of_match_table = fs210x_of_match,
+ },
+ .id_table = fs210x_i2c_id,
+ .probe = fs210x_i2c_probe,
+ .remove = fs210x_i2c_remove,
+};
+
+module_i2c_driver(fs210x_i2c_driver);
+
+MODULE_AUTHOR("Nick Li <nick.li@foursemi.com>");
+MODULE_DESCRIPTION("FS2104/5S Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs210x.h b/sound/soc/codecs/fs210x.h
new file mode 100644
index 0000000..78e1760
--- /dev/null
+++ b/sound/soc/codecs/fs210x.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * fs210x.h -- Driver for the FS2104/5S Audio Amplifier
+ *
+ * Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+ */
+
+#ifndef __FS210X_H__
+#define __FS210X_H__
+
+#define FS210X_00H_STATUS 0x00
+#define FS210X_03H_DEVID 0x03
+#define FS210X_05H_ANASTAT 0x05
+#define FS210X_06H_DIGSTAT 0x06
+#define FS210X_0BH_ACCKEY 0x0B
+#define FS210X_0FH_I2CADDR 0x0F
+#define FS210X_10H_PWRCTRL 0x10
+#define FS210X_11H_SYSCTRL 0x11
+#define FS210X_17H_I2SCTRL 0x17
+#define FS210X_30H_DACCTRL 0x30
+#define FS210X_39H_LVOLCTRL 0x39
+#define FS210X_3AH_RVOLCTRL 0x3A
+#define FS210X_42H_DACEQWL 0x42
+#define FS210X_46H_DACEQA 0x46
+#define FS210X_A1H_PLLCTRL1 0xA1
+#define FS210X_A2H_PLLCTRL2 0xA2
+#define FS210X_A3H_PLLCTRL3 0xA3
+#define FS210X_ABH_INTSTAT 0xAB
+#define FS210X_ACH_INTSTATR 0xAC
+
+#define FS210X_05H_PVDD_SHIFT 14
+#define FS210X_05H_PVDD_MASK BIT(14)
+#define FS210X_05H_OCDL_SHIFT 13
+#define FS210X_05H_OCDL_MASK BIT(13)
+#define FS210X_05H_UVDL_SHIFT 12
+#define FS210X_05H_UVDL_MASK BIT(12)
+#define FS210X_05H_OVDL_SHIFT 11
+#define FS210X_05H_OVDL_MASK BIT(11)
+#define FS210X_05H_OTPDL_SHIFT 10
+#define FS210X_05H_OTPDL_MASK BIT(10)
+#define FS210X_05H_OCRDL_SHIFT 9
+#define FS210X_05H_OCRDL_MASK BIT(9)
+#define FS210X_05H_OCLDL_SHIFT 8
+#define FS210X_05H_OCLDL_MASK BIT(8)
+#define FS210X_05H_DCRDL_SHIFT 7
+#define FS210X_05H_DCRDL_MASK BIT(7)
+#define FS210X_05H_DCLDL_SHIFT 6
+#define FS210X_05H_DCLDL_MASK BIT(6)
+#define FS210X_05H_SRDL_SHIFT 5
+#define FS210X_05H_SRDL_MASK BIT(5)
+#define FS210X_05H_OTWDL_SHIFT 4
+#define FS210X_05H_OTWDL_MASK BIT(4)
+#define FS210X_05H_AMPS_SHIFT 3
+#define FS210X_05H_AMPS_MASK BIT(3)
+#define FS210X_05H_PLLS_SHIFT 1
+#define FS210X_05H_PLLS_MASK BIT(1)
+#define FS210X_05H_ANAS_SHIFT 0
+#define FS210X_05H_ANAS_MASK BIT(0)
+#define FS210X_17H_I2SSR_SHIFT 12
+#define FS210X_17H_I2SSR_MASK GENMASK(15, 12)
+#define FS210X_30H_RMUTE_SHIFT 8
+#define FS210X_30H_LMUTE_SHIFT 4
+
+#define FS210X_0BH_ACCKEY_ON 0x0091
+#define FS210X_0BH_ACCKEY_OFF 0x0000
+#define FS210X_10H_I2C_RESET 0x0002
+#define FS210X_11H_DPS_HIZ 0x0100
+#define FS210X_11H_DPS_PWDN 0x0000
+#define FS210X_11H_DPS_PLAY 0x0300
+#define FS210X_46H_CAM_BURST_L 0x8000
+#define FS210X_46H_CAM_BURST_R 0x8200
+#define FS2105S_46H_CAM_BURST_W 0x8400
+#define FS210X_46H_CAM_CLEAR 0x0000
+
+#endif /* __FS210X_H__ */
diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h
index fb4b96c..10ad682 100644
--- a/sound/soc/codecs/lpass-macro-common.h
+++ b/sound/soc/codecs/lpass-macro-common.h
@@ -29,6 +29,7 @@ enum lpass_codec_version {
LPASS_CODEC_VERSION_2_6,
LPASS_CODEC_VERSION_2_7,
LPASS_CODEC_VERSION_2_8,
+ LPASS_CODEC_VERSION_2_9,
};
struct lpass_macro {
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
index a49551f..2e1b779 100644
--- a/sound/soc/codecs/lpass-va-macro.c
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -1485,6 +1485,8 @@ static void va_macro_set_lpass_codec_version(struct va_macro *va)
version = LPASS_CODEC_VERSION_2_7;
if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x80 || core_id_2 == 0x81))
version = LPASS_CODEC_VERSION_2_8;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x90 || core_id_2 == 0x91))
+ version = LPASS_CODEC_VERSION_2_9;
if (version == LPASS_CODEC_VERSION_UNKNOWN)
dev_warn(va->dev, "Unknown Codec version, ID: %02x / %02x / %02x\n",
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index da6adb3..187805b 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -2690,6 +2690,7 @@ static int wsa_macro_component_probe(struct snd_soc_component *comp)
case LPASS_CODEC_VERSION_2_6:
case LPASS_CODEC_VERSION_2_7:
case LPASS_CODEC_VERSION_2_8:
+ case LPASS_CODEC_VERSION_2_9:
widgets = wsa_macro_dapm_widgets_v2_5;
num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_5);
break;
@@ -2838,6 +2839,7 @@ static int wsa_macro_probe(struct platform_device *pdev)
case LPASS_CODEC_VERSION_2_6:
case LPASS_CODEC_VERSION_2_7:
case LPASS_CODEC_VERSION_2_8:
+ case LPASS_CODEC_VERSION_2_9:
wsa->reg_layout = &wsa_codec_v2_5;
def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_5);
reg_defaults = kmalloc_array(def_count, sizeof(*reg_defaults),
diff --git a/sound/soc/codecs/pcm1754.c b/sound/soc/codecs/pcm1754.c
new file mode 100644
index 0000000..b68a528
--- /dev/null
+++ b/sound/soc/codecs/pcm1754.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PCM1754 DAC ASoC codec driver
+ *
+ * Copyright (c) 2022 Alvin Šipraga <alsi@bang-olufsen.dk>
+ * Copyright (c) 2025 Stefan Kerkmann <s.kerkmann@pengutronix.de>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+struct pcm1754_priv {
+ unsigned int format;
+ struct gpio_desc *gpiod_mute;
+ struct gpio_desc *gpiod_format;
+};
+
+static int pcm1754_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1754_priv *priv = snd_soc_component_get_drvdata(component);
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm1754_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1754_priv *priv = snd_soc_component_get_drvdata(component);
+ int format;
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ format = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ switch (params_width(params)) {
+ case 16:
+ fallthrough;
+ case 24:
+ format = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ gpiod_set_value_cansleep(priv->gpiod_format, format);
+
+ return 0;
+}
+
+static int pcm1754_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct pcm1754_priv *priv = snd_soc_component_get_drvdata(dai->component);
+
+ gpiod_set_value_cansleep(priv->gpiod_mute, mute);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm1754_dai_ops = {
+ .set_fmt = pcm1754_set_dai_fmt,
+ .hw_params = pcm1754_hw_params,
+ .mute_stream = pcm1754_mute_stream,
+};
+
+static const struct snd_soc_dai_driver pcm1754_dai = {
+ .name = "pcm1754",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5000,
+ .rate_max = 200000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE
+ },
+ .ops = &pcm1754_dai_ops,
+};
+
+static const struct snd_soc_dapm_widget pcm1754_dapm_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VCC", 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC1", "Channel 1 Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC2", "Channel 2 Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm1754_dapm_routes[] = {
+ { "DAC1", NULL, "Playback" },
+ { "DAC2", NULL, "Playback" },
+
+ { "DAC1", NULL, "VCC" },
+ { "DAC2", NULL, "VCC" },
+
+ { "VOUTL", NULL, "DAC1" },
+ { "VOUTR", NULL, "DAC2" },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_pcm1754 = {
+ .dapm_widgets = pcm1754_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1754_dapm_widgets),
+ .dapm_routes = pcm1754_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1754_dapm_routes),
+};
+
+static int pcm1754_probe(struct platform_device *pdev)
+{
+ struct pcm1754_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_dai_driver *dai_drv;
+ int ret;
+
+ dai_drv = devm_kmemdup(dev, &pcm1754_dai, sizeof(*dai_drv), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->gpiod_mute = devm_gpiod_get_optional(dev, "mute", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_mute))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_mute),
+ "failed to get mute gpio");
+
+ priv->gpiod_format = devm_gpiod_get_optional(dev, "format", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_format))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_format),
+ "failed to get format gpio");
+
+ dev_set_drvdata(dev, priv);
+
+ ret = devm_snd_soc_register_component(
+ &pdev->dev, &soc_component_dev_pcm1754, dai_drv, 1);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register");
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm1754_of_match[] = {
+ { .compatible = "ti,pcm1754" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm1754_of_match);
+#endif
+
+static struct platform_driver pcm1754_codec_driver = {
+ .driver = {
+ .name = "pcm1754-codec",
+ .of_match_table = of_match_ptr(pcm1754_of_match),
+ },
+ .probe = pcm1754_probe,
+};
+
+module_platform_driver(pcm1754_codec_driver);
+
+MODULE_DESCRIPTION("ASoC PCM1754 driver");
+MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>");
+MODULE_AUTHOR("Stefan Kerkmann <s.kerkmann@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c
index 75af122..08cc52b 100644
--- a/sound/soc/codecs/pcm6240.c
+++ b/sound/soc/codecs/pcm6240.c
@@ -1353,8 +1353,8 @@ static int pcmdev_gain_ctrl_add(struct pcmdevice_priv *pcm_dev,
return 0;
}
- pcmdev_controls = devm_kzalloc(pcm_dev->dev,
- nr_chn * sizeof(struct snd_kcontrol_new), GFP_KERNEL);
+ pcmdev_controls = devm_kcalloc(pcm_dev->dev, nr_chn,
+ sizeof(struct snd_kcontrol_new), GFP_KERNEL);
if (!pcmdev_controls)
return -ENOMEM;
diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c
new file mode 100644
index 0000000..4ed09fb
--- /dev/null
+++ b/sound/soc/codecs/pm4125-sdw.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+// Copyright, 2025 Linaro Ltd
+
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include "pm4125.h"
+
+static struct pm4125_sdw_ch_info pm4125_sdw_rx_ch_info[] = {
+ WCD_SDW_CH(PM4125_HPH_L, PM4125_HPH_PORT, BIT(0)),
+ WCD_SDW_CH(PM4125_HPH_R, PM4125_HPH_PORT, BIT(1)),
+};
+
+static struct pm4125_sdw_ch_info pm4125_sdw_tx_ch_info[] = {
+ WCD_SDW_CH(PM4125_ADC1, PM4125_ADC_1_2_DMIC1L_BCS_PORT, BIT(0)),
+ WCD_SDW_CH(PM4125_ADC2, PM4125_ADC_1_2_DMIC1L_BCS_PORT, BIT(1)),
+};
+
+static struct sdw_dpn_prop pm4125_dpn_prop[PM4125_MAX_SWR_PORTS] = {
+ {
+ .num = 1,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 8,
+ .simple_ch_prep_sm = true,
+ }, {
+ .num = 2,
+ .type = SDW_DPN_SIMPLE,
+ .min_ch = 1,
+ .max_ch = 4,
+ .simple_ch_prep_sm = true,
+ }
+};
+
+struct device *pm4125_sdw_device_get(struct device_node *np)
+{
+ return bus_find_device_by_of_node(&sdw_bus_type, np);
+}
+EXPORT_SYMBOL_GPL(pm4125_sdw_device_get);
+
+int pm4125_sdw_hw_params(struct pm4125_sdw_priv *priv, struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct sdw_port_config port_config[PM4125_MAX_SWR_PORTS];
+ unsigned long ch_mask;
+ int i, j;
+
+ priv->sconfig.ch_count = 1;
+ priv->active_ports = 0;
+ for (i = 0; i < PM4125_MAX_SWR_PORTS; i++) {
+ ch_mask = priv->port_config[i].ch_mask;
+ if (!ch_mask)
+ continue;
+
+ for_each_set_bit(j, &ch_mask, 4)
+ priv->sconfig.ch_count++;
+
+ port_config[priv->active_ports] = priv->port_config[i];
+ priv->active_ports++;
+ }
+
+ priv->sconfig.bps = 1;
+ priv->sconfig.frame_rate = params_rate(params);
+ priv->sconfig.direction = priv->is_tx ? SDW_DATA_DIR_TX : SDW_DATA_DIR_RX;
+ priv->sconfig.type = SDW_STREAM_PCM;
+
+ return sdw_stream_add_slave(priv->sdev, &priv->sconfig, &port_config[0], priv->active_ports,
+ priv->sruntime);
+}
+EXPORT_SYMBOL_GPL(pm4125_sdw_hw_params);
+
+static int pm4125_update_status(struct sdw_slave *slave, enum sdw_slave_status status)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(&slave->dev);
+
+ if (priv->regmap && status == SDW_SLAVE_ATTACHED) {
+ /* Write out any cached changes that happened between probe and attach */
+ regcache_cache_only(priv->regmap, false);
+ return regcache_sync(priv->regmap);
+ }
+
+ return 0;
+}
+
+/*
+ * Handle Soundwire out-of-band interrupt event by triggering the first irq of the slave_irq
+ * irq domain, which then will be handled by the regmap_irq threaded irq.
+ * Looping is to ensure no interrupts were missed in the process.
+ */
+static int pm4125_interrupt_callback(struct sdw_slave *slave, struct sdw_slave_intr_status *status)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(&slave->dev);
+ struct irq_domain *slave_irq = priv->slave_irq;
+ u32 sts1, sts2, sts3;
+
+ do {
+ handle_nested_irq(irq_find_mapping(slave_irq, 0));
+ regmap_read(priv->regmap, PM4125_DIG_SWR_INTR_STATUS_0, &sts1);
+ regmap_read(priv->regmap, PM4125_DIG_SWR_INTR_STATUS_1, &sts2);
+ regmap_read(priv->regmap, PM4125_DIG_SWR_INTR_STATUS_2, &sts3);
+
+ } while (sts1 || sts2 || sts3);
+
+ return IRQ_HANDLED;
+}
+
+static const struct reg_default pm4125_defaults[] = {
+ { PM4125_ANA_MICBIAS_MICB_1_2_EN, 0x01 },
+ { PM4125_ANA_MICBIAS_MICB_3_EN, 0x00 },
+ { PM4125_ANA_MICBIAS_LDO_1_SETTING, 0x21 },
+ { PM4125_ANA_MICBIAS_LDO_1_CTRL, 0x01 },
+ { PM4125_ANA_TX_AMIC1, 0x00 },
+ { PM4125_ANA_TX_AMIC2, 0x00 },
+ { PM4125_ANA_MBHC_MECH, 0x39 },
+ { PM4125_ANA_MBHC_ELECT, 0x08 },
+ { PM4125_ANA_MBHC_ZDET, 0x10 },
+ { PM4125_ANA_MBHC_RESULT_1, 0x00 },
+ { PM4125_ANA_MBHC_RESULT_2, 0x00 },
+ { PM4125_ANA_MBHC_RESULT_3, 0x00 },
+ { PM4125_ANA_MBHC_BTN0_ZDET_VREF1, 0x00 },
+ { PM4125_ANA_MBHC_BTN1_ZDET_VREF2, 0x10 },
+ { PM4125_ANA_MBHC_BTN2_ZDET_VREF3, 0x20 },
+ { PM4125_ANA_MBHC_BTN3_ZDET_DBG_400, 0x30 },
+ { PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400, 0x40 },
+ { PM4125_ANA_MBHC_MICB2_RAMP, 0x00 },
+ { PM4125_ANA_MBHC_CTL_1, 0x02 },
+ { PM4125_ANA_MBHC_CTL_2, 0x05 },
+ { PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0xE9 },
+ { PM4125_ANA_MBHC_ZDET_ANA_CTL, 0x0F },
+ { PM4125_ANA_MBHC_ZDET_RAMP_CTL, 0x00 },
+ { PM4125_ANA_MBHC_FSM_STATUS, 0x00 },
+ { PM4125_ANA_MBHC_ADC_RESULT, 0x00 },
+ { PM4125_ANA_MBHC_CTL_CLK, 0x30 },
+ { PM4125_ANA_MBHC_ZDET_CALIB_RESULT, 0x00 },
+ { PM4125_ANA_NCP_EN, 0x00 },
+ { PM4125_ANA_NCP_VCTRL, 0xA7 },
+ { PM4125_ANA_HPHPA_CNP_CTL_1, 0x54 },
+ { PM4125_ANA_HPHPA_CNP_CTL_2, 0x2B },
+ { PM4125_ANA_HPHPA_PA_STATUS, 0x00 },
+ { PM4125_ANA_HPHPA_FSM_CLK, 0x12 },
+ { PM4125_ANA_HPHPA_L_GAIN, 0x00 },
+ { PM4125_ANA_HPHPA_R_GAIN, 0x00 },
+ { PM4125_SWR_HPHPA_HD2, 0x1B },
+ { PM4125_ANA_HPHPA_SPARE_CTL, 0x02 },
+ { PM4125_ANA_SURGE_EN, 0x38 },
+ { PM4125_ANA_COMBOPA_CTL, 0x35 },
+ { PM4125_ANA_COMBOPA_CTL_4, 0x84 },
+ { PM4125_ANA_COMBOPA_CTL_5, 0x05 },
+ { PM4125_ANA_RXLDO_CTL, 0x86 },
+ { PM4125_ANA_MBIAS_EN, 0x00 },
+ { PM4125_DIG_SWR_CHIP_ID0, 0x00 },
+ { PM4125_DIG_SWR_CHIP_ID1, 0x00 },
+ { PM4125_DIG_SWR_CHIP_ID2, 0x0C },
+ { PM4125_DIG_SWR_CHIP_ID3, 0x01 },
+ { PM4125_DIG_SWR_SWR_TX_CLK_RATE, 0x00 },
+ { PM4125_DIG_SWR_CDC_RST_CTL, 0x03 },
+ { PM4125_DIG_SWR_TOP_CLK_CFG, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX_CLK_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_TX_CLK_CTL, 0x33 },
+ { PM4125_DIG_SWR_SWR_RST_EN, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX_RST, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX0_CTL, 0xFC },
+ { PM4125_DIG_SWR_CDC_RX1_CTL, 0xFC },
+ { PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1, 0x00 },
+ { PM4125_DIG_SWR_CDC_COMP_CTL_0, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX_DELAY_CTL, 0x66 },
+ { PM4125_DIG_SWR_CDC_RX_GAIN_0, 0x55 },
+ { PM4125_DIG_SWR_CDC_RX_GAIN_1, 0xA9 },
+ { PM4125_DIG_SWR_CDC_RX_GAIN_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_TX0_CTL, 0x68 },
+ { PM4125_DIG_SWR_CDC_TX1_CTL, 0x68 },
+ { PM4125_DIG_SWR_CDC_TX_RST, 0x00 },
+ { PM4125_DIG_SWR_CDC_REQ0_CTL, 0x01 },
+ { PM4125_DIG_SWR_CDC_REQ1_CTL, 0x01 },
+ { PM4125_DIG_SWR_CDC_RST, 0x00 },
+ { PM4125_DIG_SWR_CDC_AMIC_CTL, 0x02 },
+ { PM4125_DIG_SWR_CDC_DMIC_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_DMIC1_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_DMIC1_RATE, 0x01 },
+ { PM4125_DIG_SWR_PDM_WD_CTL0, 0x00 },
+ { PM4125_DIG_SWR_PDM_WD_CTL1, 0x00 },
+ { PM4125_DIG_SWR_INTR_MODE, 0x00 },
+ { PM4125_DIG_SWR_INTR_MASK_0, 0xFF },
+ { PM4125_DIG_SWR_INTR_MASK_1, 0x7F },
+ { PM4125_DIG_SWR_INTR_MASK_2, 0x0C },
+ { PM4125_DIG_SWR_INTR_STATUS_0, 0x00 },
+ { PM4125_DIG_SWR_INTR_STATUS_1, 0x00 },
+ { PM4125_DIG_SWR_INTR_STATUS_2, 0x00 },
+ { PM4125_DIG_SWR_INTR_CLEAR_0, 0x00 },
+ { PM4125_DIG_SWR_INTR_CLEAR_1, 0x00 },
+ { PM4125_DIG_SWR_INTR_CLEAR_2, 0x00 },
+ { PM4125_DIG_SWR_INTR_LEVEL_0, 0x00 },
+ { PM4125_DIG_SWR_INTR_LEVEL_1, 0x2A },
+ { PM4125_DIG_SWR_INTR_LEVEL_2, 0x00 },
+ { PM4125_DIG_SWR_CDC_CONN_RX0_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_CONN_RX1_CTL, 0x00 },
+ { PM4125_DIG_SWR_LOOP_BACK_MODE, 0x00 },
+ { PM4125_DIG_SWR_DRIVE_STRENGTH_0, 0x00 },
+ { PM4125_DIG_SWR_DIG_DEBUG_CTL, 0x00 },
+ { PM4125_DIG_SWR_DIG_DEBUG_EN, 0x00 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA0, 0x55 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA1, 0x55 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA2, 0x55 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA3, 0x01 },
+};
+
+static bool pm4125_rdwr_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PM4125_ANA_MICBIAS_MICB_1_2_EN:
+ case PM4125_ANA_MICBIAS_MICB_3_EN:
+ case PM4125_ANA_MICBIAS_LDO_1_SETTING:
+ case PM4125_ANA_MICBIAS_LDO_1_CTRL:
+ case PM4125_ANA_TX_AMIC1:
+ case PM4125_ANA_TX_AMIC2:
+ case PM4125_ANA_MBHC_MECH:
+ case PM4125_ANA_MBHC_ELECT:
+ case PM4125_ANA_MBHC_ZDET:
+ case PM4125_ANA_MBHC_BTN0_ZDET_VREF1:
+ case PM4125_ANA_MBHC_BTN1_ZDET_VREF2:
+ case PM4125_ANA_MBHC_BTN2_ZDET_VREF3:
+ case PM4125_ANA_MBHC_BTN3_ZDET_DBG_400:
+ case PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400:
+ case PM4125_ANA_MBHC_MICB2_RAMP:
+ case PM4125_ANA_MBHC_CTL_1:
+ case PM4125_ANA_MBHC_CTL_2:
+ case PM4125_ANA_MBHC_PLUG_DETECT_CTL:
+ case PM4125_ANA_MBHC_ZDET_ANA_CTL:
+ case PM4125_ANA_MBHC_ZDET_RAMP_CTL:
+ case PM4125_ANA_MBHC_CTL_CLK:
+ case PM4125_ANA_NCP_EN:
+ case PM4125_ANA_NCP_VCTRL:
+ case PM4125_ANA_HPHPA_CNP_CTL_1:
+ case PM4125_ANA_HPHPA_CNP_CTL_2:
+ case PM4125_ANA_HPHPA_FSM_CLK:
+ case PM4125_ANA_HPHPA_L_GAIN:
+ case PM4125_ANA_HPHPA_R_GAIN:
+ case PM4125_ANA_HPHPA_SPARE_CTL:
+ case PM4125_SWR_HPHPA_HD2:
+ case PM4125_ANA_SURGE_EN:
+ case PM4125_ANA_COMBOPA_CTL:
+ case PM4125_ANA_COMBOPA_CTL_4:
+ case PM4125_ANA_COMBOPA_CTL_5:
+ case PM4125_ANA_RXLDO_CTL:
+ case PM4125_ANA_MBIAS_EN:
+ case PM4125_DIG_SWR_SWR_TX_CLK_RATE:
+ case PM4125_DIG_SWR_CDC_RST_CTL:
+ case PM4125_DIG_SWR_TOP_CLK_CFG:
+ case PM4125_DIG_SWR_CDC_RX_CLK_CTL:
+ case PM4125_DIG_SWR_CDC_TX_CLK_CTL:
+ case PM4125_DIG_SWR_SWR_RST_EN:
+ case PM4125_DIG_SWR_CDC_RX_RST:
+ case PM4125_DIG_SWR_CDC_RX0_CTL:
+ case PM4125_DIG_SWR_CDC_RX1_CTL:
+ case PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1:
+ case PM4125_DIG_SWR_CDC_COMP_CTL_0:
+ case PM4125_DIG_SWR_CDC_RX_DELAY_CTL:
+ case PM4125_DIG_SWR_CDC_RX_GAIN_0:
+ case PM4125_DIG_SWR_CDC_RX_GAIN_1:
+ case PM4125_DIG_SWR_CDC_RX_GAIN_CTL:
+ case PM4125_DIG_SWR_CDC_TX0_CTL:
+ case PM4125_DIG_SWR_CDC_TX1_CTL:
+ case PM4125_DIG_SWR_CDC_TX_RST:
+ case PM4125_DIG_SWR_CDC_REQ0_CTL:
+ case PM4125_DIG_SWR_CDC_REQ1_CTL:
+ case PM4125_DIG_SWR_CDC_RST:
+ case PM4125_DIG_SWR_CDC_AMIC_CTL:
+ case PM4125_DIG_SWR_CDC_DMIC_CTL:
+ case PM4125_DIG_SWR_CDC_DMIC1_CTL:
+ case PM4125_DIG_SWR_CDC_DMIC1_RATE:
+ case PM4125_DIG_SWR_PDM_WD_CTL0:
+ case PM4125_DIG_SWR_PDM_WD_CTL1:
+ case PM4125_DIG_SWR_INTR_MODE:
+ case PM4125_DIG_SWR_INTR_MASK_0:
+ case PM4125_DIG_SWR_INTR_MASK_1:
+ case PM4125_DIG_SWR_INTR_MASK_2:
+ case PM4125_DIG_SWR_INTR_CLEAR_0:
+ case PM4125_DIG_SWR_INTR_CLEAR_1:
+ case PM4125_DIG_SWR_INTR_CLEAR_2:
+ case PM4125_DIG_SWR_INTR_LEVEL_0:
+ case PM4125_DIG_SWR_INTR_LEVEL_1:
+ case PM4125_DIG_SWR_INTR_LEVEL_2:
+ case PM4125_DIG_SWR_CDC_CONN_RX0_CTL:
+ case PM4125_DIG_SWR_CDC_CONN_RX1_CTL:
+ case PM4125_DIG_SWR_LOOP_BACK_MODE:
+ case PM4125_DIG_SWR_DRIVE_STRENGTH_0:
+ case PM4125_DIG_SWR_DIG_DEBUG_CTL:
+ case PM4125_DIG_SWR_DIG_DEBUG_EN:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA0:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA1:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA2:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA3:
+ return true;
+ }
+
+ return false;
+}
+
+static bool pm4125_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PM4125_ANA_MBHC_RESULT_1:
+ case PM4125_ANA_MBHC_RESULT_2:
+ case PM4125_ANA_MBHC_RESULT_3:
+ case PM4125_ANA_MBHC_FSM_STATUS:
+ case PM4125_ANA_MBHC_ADC_RESULT:
+ case PM4125_ANA_MBHC_ZDET_CALIB_RESULT:
+ case PM4125_ANA_HPHPA_PA_STATUS:
+ case PM4125_DIG_SWR_CHIP_ID0:
+ case PM4125_DIG_SWR_CHIP_ID1:
+ case PM4125_DIG_SWR_CHIP_ID2:
+ case PM4125_DIG_SWR_CHIP_ID3:
+ case PM4125_DIG_SWR_INTR_STATUS_0:
+ case PM4125_DIG_SWR_INTR_STATUS_1:
+ case PM4125_DIG_SWR_INTR_STATUS_2:
+ return true;
+ }
+ return pm4125_rdwr_register(dev, reg);
+}
+
+static bool pm4125_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PM4125_ANA_MBHC_RESULT_1:
+ case PM4125_ANA_MBHC_RESULT_2:
+ case PM4125_ANA_MBHC_RESULT_3:
+ case PM4125_ANA_MBHC_FSM_STATUS:
+ case PM4125_ANA_MBHC_ADC_RESULT:
+ case PM4125_ANA_MBHC_ZDET_CALIB_RESULT:
+ case PM4125_ANA_HPHPA_PA_STATUS:
+ case PM4125_DIG_SWR_CHIP_ID0:
+ case PM4125_DIG_SWR_CHIP_ID1:
+ case PM4125_DIG_SWR_CHIP_ID2:
+ case PM4125_DIG_SWR_CHIP_ID3:
+ case PM4125_DIG_SWR_INTR_STATUS_0:
+ case PM4125_DIG_SWR_INTR_STATUS_1:
+ case PM4125_DIG_SWR_INTR_STATUS_2:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_config pm4125_regmap_config = {
+ .name = "pm4125_csr",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = pm4125_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pm4125_defaults),
+ .max_register = PM4125_MAX_REGISTER,
+ .readable_reg = pm4125_readable_register,
+ .writeable_reg = pm4125_rdwr_register,
+ .volatile_reg = pm4125_volatile_register,
+};
+
+static const struct sdw_slave_ops pm4125_slave_ops = {
+ .update_status = pm4125_update_status,
+ .interrupt_callback = pm4125_interrupt_callback,
+};
+
+static int pm4125_sdw_component_bind(struct device *dev, struct device *master, void *data)
+{
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static void pm4125_sdw_component_unbind(struct device *dev, struct device *master, void *data)
+{
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+}
+
+static const struct component_ops pm4125_sdw_component_ops = {
+ .bind = pm4125_sdw_component_bind,
+ .unbind = pm4125_sdw_component_unbind,
+};
+
+static int pm4125_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct pm4125_sdw_priv *priv;
+ u8 master_ch_mask[PM4125_MAX_SWR_CH_IDS];
+ int master_ch_mask_size = 0;
+ int ret, i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Port map index starts at 0, however the data port for this codec starts at index 1 */
+ if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) {
+ priv->is_tx = true;
+ ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping",
+ &pdev->m_port_map[1], PM4125_MAX_TX_SWR_PORTS);
+ } else {
+ ret = of_property_read_u32_array(dev->of_node, "qcom,rx-port-mapping",
+ &pdev->m_port_map[1], PM4125_MAX_SWR_PORTS);
+ }
+
+ if (ret < 0)
+ dev_info(dev, "Error getting static port mapping for %s (%d)\n",
+ priv->is_tx ? "TX" : "RX", ret);
+
+ priv->sdev = pdev;
+ dev_set_drvdata(dev, priv);
+
+ pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF |
+ SDW_SCP_INT1_BUS_CLASH |
+ SDW_SCP_INT1_PARITY;
+ pdev->prop.lane_control_support = true;
+ pdev->prop.simple_clk_stop_capable = true;
+
+ memset(master_ch_mask, 0, PM4125_MAX_SWR_CH_IDS);
+
+ if (priv->is_tx) {
+ master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+ "qcom,tx-channel-mapping");
+
+ if (master_ch_mask_size)
+ ret = of_property_read_u8_array(dev->of_node, "qcom,tx-channel-mapping",
+ master_ch_mask, master_ch_mask_size);
+ } else {
+ master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+ "qcom,rx-channel-mapping");
+
+ if (master_ch_mask_size)
+ ret = of_property_read_u8_array(dev->of_node, "qcom,rx-channel-mapping",
+ master_ch_mask, master_ch_mask_size);
+ }
+
+ if (ret < 0)
+ dev_info(dev, "Static channel mapping not specified using device channel maps\n");
+
+ if (priv->is_tx) {
+ pdev->prop.source_ports = GENMASK(PM4125_MAX_TX_SWR_PORTS, 0);
+ pdev->prop.src_dpn_prop = pm4125_dpn_prop;
+ priv->ch_info = &pm4125_sdw_tx_ch_info[0];
+
+ for (i = 0; i < master_ch_mask_size; i++)
+ priv->ch_info[i].master_ch_mask = PM4125_SWRM_CH_MASK(master_ch_mask[i]);
+
+ pdev->prop.wake_capable = true;
+
+ priv->regmap = devm_regmap_init_sdw(pdev, &pm4125_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap), "regmap init failed\n");
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(priv->regmap, true);
+ } else {
+ pdev->prop.sink_ports = GENMASK(PM4125_MAX_SWR_PORTS - 1, 0);
+ pdev->prop.sink_dpn_prop = pm4125_dpn_prop;
+ priv->ch_info = &pm4125_sdw_rx_ch_info[0];
+
+ for (i = 0; i < master_ch_mask_size; i++)
+ priv->ch_info[i].master_ch_mask = PM4125_SWRM_CH_MASK(master_ch_mask[i]);
+ }
+
+ ret = component_add(dev, &pm4125_sdw_component_ops);
+ if (ret)
+ return ret;
+
+ /* Set suspended until aggregate device is bind */
+ pm_runtime_set_suspended(dev);
+
+ return 0;
+}
+
+static int pm4125_remove(struct sdw_slave *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_del(dev, &pm4125_sdw_component_ops);
+
+ return 0;
+}
+
+static const struct sdw_device_id pm4125_slave_id[] = {
+ SDW_SLAVE_ENTRY(0x0217, 0x10c, 0), /* Soundwire pm4125 RX/TX Device ID */
+ { }
+};
+MODULE_DEVICE_TABLE(sdw, pm4125_slave_id);
+
+static int __maybe_unused pm4125_sdw_runtime_suspend(struct device *dev)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->regmap) {
+ regcache_cache_only(priv->regmap, true);
+ regcache_mark_dirty(priv->regmap);
+ }
+
+ return 0;
+}
+
+static int __maybe_unused pm4125_sdw_runtime_resume(struct device *dev)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->regmap) {
+ regcache_cache_only(priv->regmap, false);
+ regcache_sync(priv->regmap);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops pm4125_sdw_pm_ops = {
+ SET_RUNTIME_PM_OPS(pm4125_sdw_runtime_suspend, pm4125_sdw_runtime_resume, NULL)
+};
+
+static struct sdw_driver pm4125_codec_driver = {
+ .probe = pm4125_probe,
+ .remove = pm4125_remove,
+ .ops = &pm4125_slave_ops,
+ .id_table = pm4125_slave_id,
+ .driver = {
+ .name = "pm4125-codec",
+ .pm = &pm4125_sdw_pm_ops,
+ }
+};
+module_sdw_driver(pm4125_codec_driver);
+
+MODULE_DESCRIPTION("PM4125 SDW codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c
new file mode 100644
index 0000000..706fc66
--- /dev/null
+++ b/sound/soc/codecs/pm4125.c
@@ -0,0 +1,1780 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+// Copyright (c) 2025, Linaro Ltd
+
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "pm4125.h"
+#include "wcd-mbhc-v2.h"
+
+#define WCD_MBHC_HS_V_MAX 1600
+#define PM4125_MBHC_MAX_BUTTONS 8
+
+#define PM4125_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_384000)
+
+/* Fractional Rates */
+#define PM4125_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+
+#define PM4125_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/* Registers in SPMI addr space */
+#define PM4125_CODEC_RESET_REG 0xF3DB
+#define PM4125_CODEC_OFF 0x1
+#define PM4125_CODEC_ON 0x0
+#define PM4125_CODEC_FOUNDRY_ID_REG 0x7
+
+enum {
+ HPH_COMP_DELAY,
+ HPH_PA_DELAY,
+ AMIC2_BCS_ENABLE,
+};
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ NUM_CODEC_DAIS,
+};
+
+struct pm4125_priv {
+ struct sdw_slave *tx_sdw_dev;
+ struct pm4125_sdw_priv *sdw_priv[NUM_CODEC_DAIS];
+ struct device *txdev;
+ struct device *rxdev;
+ struct device_node *rxnode;
+ struct device_node *txnode;
+ struct regmap *regmap;
+ struct regmap *spmi_regmap;
+ /* mbhc module */
+ struct wcd_mbhc *wcd_mbhc;
+ struct wcd_mbhc_config mbhc_cfg;
+ struct wcd_mbhc_intr intr_ids;
+ struct irq_domain *virq;
+ const struct regmap_irq_chip *pm4125_regmap_irq_chip;
+ struct regmap_irq_chip_data *irq_chip;
+ struct snd_soc_jack *jack;
+ unsigned long status_mask;
+ s32 micb_ref[PM4125_MAX_MICBIAS];
+ s32 pullup_ref[PM4125_MAX_MICBIAS];
+ u32 micb1_mv;
+ u32 micb2_mv;
+ u32 micb3_mv;
+
+ int hphr_pdm_wd_int;
+ int hphl_pdm_wd_int;
+ bool comp1_enable;
+ bool comp2_enable;
+
+ atomic_t gloal_mbias_cnt;
+};
+
+static const char * const pm4125_power_supplies[] = {
+ "vdd-io", "vdd-cp", "vdd-mic-bias", "vdd-pa-vpos",
+};
+
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+static const struct wcd_mbhc_field pm4125_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = {
+ WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, PM4125_ANA_MBHC_MECH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, PM4125_ANA_MBHC_MECH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, PM4125_ANA_MBHC_MECH, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x30),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, PM4125_ANA_MBHC_ELECT, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x1F),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, PM4125_ANA_MBHC_MECH, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, PM4125_ANA_MBHC_MECH, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, PM4125_ANA_MBHC_MECH, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, PM4125_ANA_MBHC_MECH, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, PM4125_ANA_MBHC_ELECT, 0x06),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, PM4125_ANA_MBHC_ELECT, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x0F),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, PM4125_ANA_MBHC_CTL_1, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, PM4125_ANA_MBHC_CTL_2, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, PM4125_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x07),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, PM4125_ANA_MBHC_ELECT, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, PM4125_ANA_MICBIAS_MICB_1_2_EN, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, PM4125_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, PM4125_ANA_MBHC_FSM_STATUS, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, PM4125_ANA_MBHC_CTL_2, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, PM4125_ANA_MBHC_FSM_STATUS, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, PM4125_DIG_SWR_INTR_STATUS_0, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, PM4125_DIG_SWR_INTR_STATUS_0, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, PM4125_ANA_MBHC_CTL_1, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, PM4125_ANA_MBHC_FSM_STATUS, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, PM4125_ANA_MBHC_FSM_STATUS, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, PM4125_ANA_MBHC_ADC_RESULT, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, PM4125_ANA_MICBIAS_LDO_1_SETTING, 0x3F),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, PM4125_ANA_MBHC_CTL_1, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, PM4125_ANA_MBHC_CTL_1, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, PM4125_ANA_MBHC_ZDET, 0x02),
+};
+
+static const struct regmap_irq pm4125_irqs[PM4125_NUM_IRQS] = {
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_BUTTON_PRESS_DET, 0, BIT(0)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_BUTTON_RELEASE_DET, 0, BIT(1)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_ELECT_INS_REM_DET, 0, BIT(2)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, BIT(3)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_SW_DET, 0, BIT(4)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_OCP_INT, 0, BIT(5)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_CNP_INT, 0, BIT(6)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_OCP_INT, 0, BIT(7)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_CNP_INT, 1, BIT(0)),
+ REGMAP_IRQ_REG(PM4125_IRQ_EAR_CNP_INT, 1, BIT(1)),
+ REGMAP_IRQ_REG(PM4125_IRQ_EAR_SCD_INT, 1, BIT(2)),
+ REGMAP_IRQ_REG(PM4125_IRQ_AUX_CNP_INT, 1, BIT(3)),
+ REGMAP_IRQ_REG(PM4125_IRQ_AUX_SCD_INT, 1, BIT(4)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_PDM_WD_INT, 1, BIT(5)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_PDM_WD_INT, 1, BIT(6)),
+ REGMAP_IRQ_REG(PM4125_IRQ_AUX_PDM_WD_INT, 1, BIT(7)),
+ REGMAP_IRQ_REG(PM4125_IRQ_LDORT_SCD_INT, 2, BIT(0)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_MOISTURE_INT, 2, BIT(1)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_SURGE_DET_INT, 2, BIT(2)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_SURGE_DET_INT, 2, BIT(3)),
+};
+
+static int pm4125_handle_post_irq(void *data)
+{
+ struct pm4125_priv *pm4125 = (struct pm4125_priv *)data;
+
+ regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_0, 0);
+ regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_1, 0);
+ regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_2, 0);
+
+ return IRQ_HANDLED;
+}
+
+static const u32 pm4125_config_regs[] = {
+ PM4125_DIG_SWR_INTR_LEVEL_0,
+};
+
+static struct regmap_irq_chip pm4125_regmap_irq_chip = {
+ .name = "pm4125",
+ .irqs = pm4125_irqs,
+ .num_irqs = ARRAY_SIZE(pm4125_irqs),
+ .num_regs = 3,
+ .status_base = PM4125_DIG_SWR_INTR_STATUS_0,
+ .mask_base = PM4125_DIG_SWR_INTR_MASK_0,
+ .ack_base = PM4125_DIG_SWR_INTR_CLEAR_0,
+ .use_ack = 1,
+ .clear_ack = 1,
+ .config_base = pm4125_config_regs,
+ .num_config_bases = ARRAY_SIZE(pm4125_config_regs),
+ .num_config_regs = 1,
+ .runtime_pm = true,
+ .handle_post_irq = pm4125_handle_post_irq,
+};
+
+static void pm4125_reset(struct pm4125_priv *pm4125)
+{
+ regmap_write(pm4125->spmi_regmap, PM4125_CODEC_RESET_REG, PM4125_CODEC_OFF);
+ usleep_range(20, 30);
+ regmap_write(pm4125->spmi_regmap, PM4125_CODEC_RESET_REG, PM4125_CODEC_ON);
+ usleep_range(5000, 5010);
+}
+
+static void pm4125_io_init(struct regmap *regmap)
+{
+ /* Disable HPH OCP */
+ regmap_update_bits(regmap, PM4125_ANA_HPHPA_CNP_CTL_2,
+ PM4125_ANA_HPHPA_CNP_OCP_EN_L_MASK | PM4125_ANA_HPHPA_CNP_OCP_EN_R_MASK,
+ PM4125_ANA_HPHPA_CNP_OCP_DISABLE);
+
+ /* Enable surge protection */
+ regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, PM4125_ANA_SURGE_PROTECTION_HPHL_MASK,
+ FIELD_PREP(PM4125_ANA_SURGE_PROTECTION_HPHL_MASK,
+ PM4125_ANA_SURGE_PROTECTION_ENABLE));
+ regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, PM4125_ANA_SURGE_PROTECTION_HPHR_MASK,
+ FIELD_PREP(PM4125_ANA_SURGE_PROTECTION_HPHR_MASK,
+ PM4125_ANA_SURGE_PROTECTION_ENABLE));
+
+ /* Disable mic bias 2 pull down */
+ regmap_update_bits(regmap, PM4125_ANA_MICBIAS_MICB_1_2_EN,
+ PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK,
+ FIELD_PREP(PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK,
+ PM4125_ANA_MICBIAS_MICB_PULL_DISABLE));
+}
+
+static int pm4125_global_mbias_disable(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ if (atomic_dec_and_test(&pm4125->gloal_mbias_cnt)) {
+
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_V2I_MASK,
+ PM4125_ANA_MBIAS_EN_DISABLE);
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_GLOBAL_MASK,
+ PM4125_ANA_MBIAS_EN_DISABLE);
+ }
+
+ return 0;
+}
+
+static int pm4125_global_mbias_enable(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ if (atomic_inc_return(&pm4125->gloal_mbias_cnt) == 1) {
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_GLOBAL_MASK,
+ PM4125_ANA_MBIAS_EN_ENABLE);
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_V2I_MASK,
+ PM4125_ANA_MBIAS_EN_ENABLE);
+ usleep_range(1000, 1100);
+ }
+
+ return 0;
+}
+
+static int pm4125_rx_clk_enable(struct snd_soc_component *component)
+{
+ pm4125_global_mbias_enable(component);
+
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ usleep_range(5000, 5100);
+
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK,
+ PM4125_ANA_HPHPA_FSM_DIV_RATIO_68);
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_ENABLE);
+ snd_soc_component_update_bits(component, PM4125_ANA_NCP_VCTRL, 0x07, 0x06);
+ snd_soc_component_write_field(component, PM4125_ANA_NCP_EN,
+ PM4125_ANA_NCP_ENABLE_MASK,
+ PM4125_ANA_NCP_ENABLE);
+ usleep_range(500, 510);
+
+ return 0;
+}
+
+static int pm4125_rx_clk_disable(struct snd_soc_component *component)
+{
+
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_DISABLE);
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK,
+ 0x00);
+ snd_soc_component_write_field(component, PM4125_ANA_NCP_EN,
+ PM4125_ANA_NCP_ENABLE_MASK,
+ PM4125_ANA_NCP_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+
+ pm4125_global_mbias_disable(component);
+
+ return 0;
+}
+
+
+static int pm4125_codec_enable_rxclk(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ pm4125_rx_clk_enable(component);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ pm4125_rx_clk_disable(component);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_CNP_CTL_1,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN);
+ snd_soc_component_write_field(component, PM4125_SWR_HPHPA_HD2,
+ PM4125_SWR_HPHPA_HD2_LEFT_MASK,
+ PM4125_SWR_HPHPA_HD2_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (pm4125->comp1_enable) {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+
+ if (pm4125->comp2_enable)
+ snd_soc_component_write_field(component,
+ PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+ /*
+ * 5ms sleep is required after COMP is enabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5100);
+ } else {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ }
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+ if (pm4125->comp1_enable)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_CNP_CTL_1,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN);
+ snd_soc_component_write_field(component, PM4125_SWR_HPHPA_HD2,
+ PM4125_SWR_HPHPA_HD2_RIGHT_MASK,
+ PM4125_SWR_HPHPA_HD2_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (pm4125->comp2_enable) {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+ if (pm4125->comp1_enable)
+ snd_soc_component_write_field(component,
+ PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+ /*
+ * 5ms sleep is required after COMP is enabled
+ * as per HW requirement
+ */
+ usleep_range(5000, 5100);
+ } else {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ }
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX1_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX1_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX1_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX1_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX1_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX1_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+ if (pm4125->comp2_enable)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_ear_lo_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int pm4125_codec_enable_hphl_wdt_irq(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ enable_irq(pm4125->hphl_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_hphr_wdt_irq(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ enable_irq(pm4125->hphr_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(pm4125->hphr_pdm_wd_int);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ usleep_range(200, 210);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL1,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX1_M | PM4125_WDT_ENABLE_RX1_L));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL1,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ usleep_range(200, 210);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_lo_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_5, 0x04, 0x00);
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x0F);
+ usleep_range(1000, 1010);
+ snd_soc_component_write_field(component, PM4125_ANA_COMBOPA_CTL,
+ PM4125_ANA_COMBO_PA_SELECT_MASK,
+ PM4125_ANA_COMBO_PA_SELECT_LO);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x04);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(2000, 2010);
+ snd_soc_component_write_field(component, PM4125_ANA_COMBOPA_CTL,
+ PM4125_ANA_COMBO_PA_SELECT_MASK,
+ PM4125_ANA_COMBO_PA_SELECT_EAR);
+ usleep_range(5000, 5100);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_5, 0x04, 0x00);
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x0F);
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL,
+ PM4125_ANA_COMBO_PA_SELECT_MASK,
+ PM4125_ANA_COMBO_PA_SELECT_EAR);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x04);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5010);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_get_micb_vout_ctl_val(struct device *dev, u32 micb_mv)
+{
+ if (micb_mv < 1600 || micb_mv > 2850) {
+ dev_err(dev, "%s: unsupported micbias voltage (%u mV)\n", __func__, micb_mv);
+ return -EINVAL;
+ }
+
+ return (micb_mv - 1600) / 50;
+}
+
+static int pm4125_codec_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable BCS for Headset mic */
+ if (w->shift == 1 &&
+ !(snd_soc_component_read(component, PM4125_ANA_TX_AMIC2) & 0x10)) {
+ set_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask);
+ }
+ pm4125_global_mbias_enable(component);
+ if (w->shift)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK,
+ PM4125_DIG_SWR_TXD_MODE_NORMAL);
+ else
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK,
+ PM4125_DIG_SWR_TXD_MODE_NORMAL);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (w->shift == 1 && test_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask))
+ clear_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask);
+
+ if (w->shift)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK,
+ 0x00);
+ else
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK,
+ 0x00);
+ pm4125_global_mbias_disable(component);
+ break;
+ };
+
+ return 0;
+}
+
+static int pm4125_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ u16 dmic_clk_reg = w->reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_AMIC_CTL,
+ PM4125_DIG_SWR_AMIC_SELECT_MASK,
+ PM4125_DIG_SWR_AMIC_SELECT_DMIC1);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ PM4125_DIG_SWR_DMIC1_CLK_EN_MASK,
+ PM4125_DIG_SWR_DMIC1_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ PM4125_DIG_SWR_DMIC1_CLK_EN_MASK,
+ PM4125_DIG_SWR_DMIC1_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_AMIC_CTL,
+ PM4125_DIG_SWR_AMIC_SELECT_MASK,
+ PM4125_DIG_SWR_AMIC_SELECT_AMIC3);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_micbias_control(struct snd_soc_component *component, int micb_num, int req,
+ bool is_dapm)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ int micb_index = micb_num - 1;
+ u16 micb_reg;
+ u8 pullup_mask = 0, enable_mask = 0;
+
+ if ((micb_index < 0) || (micb_index > PM4125_MAX_MICBIAS - 1)) {
+ dev_err(component->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+ __func__, micb_index);
+ return -EINVAL;
+ }
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = PM4125_ANA_MICBIAS_MICB_1_2_EN;
+ pullup_mask = PM4125_ANA_MICBIAS_MICB1_PULL_UP_MASK;
+ enable_mask = 0x40;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = PM4125_ANA_MICBIAS_MICB_1_2_EN;
+ pullup_mask = PM4125_ANA_MICBIAS_MICB2_PULL_UP_MASK;
+ enable_mask = 0x04;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = PM4125_ANA_MICBIAS_MICB_3_EN;
+ pullup_mask = 0x02;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+ __func__, micb_num);
+ return -EINVAL;
+ };
+
+ switch (req) {
+ case MICB_PULLUP_ENABLE:
+ pm4125->pullup_ref[micb_index]++;
+ if ((pm4125->pullup_ref[micb_index] == 1) &&
+ (pm4125->micb_ref[micb_index] == 0))
+ snd_soc_component_update_bits(component, micb_reg,
+ pullup_mask, pullup_mask);
+ break;
+ case MICB_PULLUP_DISABLE:
+ if (pm4125->pullup_ref[micb_index] > 0)
+ pm4125->pullup_ref[micb_index]--;
+ if ((pm4125->pullup_ref[micb_index] == 0) &&
+ (pm4125->micb_ref[micb_index] == 0))
+ snd_soc_component_update_bits(component, micb_reg,
+ pullup_mask, 0x00);
+ break;
+ case MICB_ENABLE:
+ pm4125->micb_ref[micb_index]++;
+ if (pm4125->micb_ref[micb_index] == 1) {
+ pm4125_global_mbias_enable(component);
+ snd_soc_component_update_bits(component, micb_reg,
+ enable_mask, enable_mask);
+ }
+ break;
+ case MICB_DISABLE:
+ if (pm4125->micb_ref[micb_index] > 0)
+ pm4125->micb_ref[micb_index]--;
+ if ((pm4125->micb_ref[micb_index] == 0) &&
+ (pm4125->pullup_ref[micb_index] > 0)) {
+ snd_soc_component_update_bits(component, micb_reg,
+ pullup_mask, pullup_mask);
+ snd_soc_component_update_bits(component, micb_reg,
+ enable_mask, 0x00);
+ pm4125_global_mbias_disable(component);
+ } else if ((pm4125->micb_ref[micb_index] == 0) &&
+ (pm4125->pullup_ref[micb_index] == 0)) {
+ snd_soc_component_update_bits(component, micb_reg,
+ enable_mask, 0x00);
+ pm4125_global_mbias_disable(component);
+ }
+ break;
+ };
+
+ return 0;
+}
+
+static int pm4125_codec_enable_micbias(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (micb_num == MIC_BIAS_3)
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true);
+ else
+ pm4125_micbias_control(component, micb_num, MICB_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (micb_num == MIC_BIAS_3)
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true);
+ else
+ pm4125_micbias_control(component, micb_num, MICB_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_connect_port(struct pm4125_sdw_priv *sdw_priv, u8 port_idx, u8 ch_id, bool enable)
+{
+ struct sdw_port_config *port_config = &sdw_priv->port_config[port_idx - 1];
+ const struct pm4125_sdw_ch_info *ch_info = &sdw_priv->ch_info[ch_id];
+ struct sdw_slave *sdev = sdw_priv->sdev;
+ u8 port_num = ch_info->port_num;
+ u8 ch_mask = ch_info->ch_mask;
+ u8 mstr_port_num, mstr_ch_mask;
+
+ port_config->num = port_num;
+
+ mstr_port_num = sdev->m_port_map[port_num];
+ mstr_ch_mask = ch_info->master_ch_mask;
+
+ if (enable) {
+ port_config->ch_mask |= ch_mask;
+ sdw_priv->master_channel_map[mstr_port_num] |= mstr_ch_mask;
+ } else {
+ port_config->ch_mask &= ~ch_mask;
+ sdw_priv->master_channel_map[mstr_port_num] &= ~mstr_ch_mask;
+ }
+
+ return 0;
+}
+
+static int pm4125_get_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ ucontrol->value.integer.value[0] = hphr ? pm4125->comp2_enable : pm4125->comp1_enable;
+ return 0;
+}
+
+static int pm4125_set_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[AIF1_PB];
+ int value = ucontrol->value.integer.value[0];
+ struct soc_mixer_control *mc;
+ int portidx;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ if (hphr) {
+ if (value == pm4125->comp2_enable)
+ return 0;
+
+ pm4125->comp2_enable = value;
+ } else {
+ if (value == pm4125->comp1_enable)
+ return 0;
+
+ pm4125->comp1_enable = value;
+ }
+
+ portidx = sdw_priv->ch_info[mc->reg].port_num;
+
+ pm4125_connect_port(sdw_priv, portidx, mc->reg, value ? true : false);
+
+ return 1;
+}
+
+static int pm4125_get_swr_port(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(comp);
+ struct pm4125_sdw_priv *sdw_priv;
+ int dai_id = mixer->shift;
+ int ch_idx = mixer->reg;
+ int portidx;
+
+ sdw_priv = pm4125->sdw_priv[dai_id];
+ portidx = sdw_priv->ch_info[ch_idx].port_num;
+
+ ucontrol->value.integer.value[0] = sdw_priv->port_enable[portidx];
+
+ return 0;
+}
+
+static int pm4125_set_swr_port(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(comp);
+ struct pm4125_sdw_priv *sdw_priv;
+ int dai_id = mixer->shift;
+ int ch_idx = mixer->reg;
+ int portidx;
+ bool enable;
+
+ sdw_priv = pm4125->sdw_priv[dai_id];
+
+ portidx = sdw_priv->ch_info[ch_idx].port_num;
+
+ enable = ucontrol->value.integer.value[0];
+
+ if (enable == sdw_priv->port_enable[portidx]) {
+ pm4125_connect_port(sdw_priv, portidx, ch_idx, enable);
+ return 0;
+ }
+
+ sdw_priv->port_enable[portidx] = enable;
+ pm4125_connect_port(sdw_priv, portidx, ch_idx, enable);
+
+ return 1;
+}
+
+static void pm4125_mbhc_bias_control(struct snd_soc_component *component, bool enable)
+{
+ snd_soc_component_write_field(component, PM4125_ANA_MBHC_ELECT,
+ PM4125_ANA_MBHC_ELECT_BIAS_EN_MASK,
+ enable ? PM4125_ANA_MBHC_ELECT_BIAS_ENABLE :
+ PM4125_ANA_MBHC_ELECT_BIAS_DISABLE);
+}
+
+static void pm4125_mbhc_program_btn_thr(struct snd_soc_component *component,
+ int *btn_low, int *btn_high,
+ int num_btn, bool is_micbias)
+{
+ int i, vth;
+
+ if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+ dev_err(component->dev, "%s: invalid number of buttons: %d\n",
+ __func__, num_btn);
+ return;
+ }
+
+ for (i = 0; i < num_btn; i++) {
+ vth = ((btn_high[i] * 2) / 25) & 0x3F;
+ snd_soc_component_write_field(component, PM4125_ANA_MBHC_BTN0_ZDET_VREF1 + i,
+ PM4125_ANA_MBHC_BTN0_THRESHOLD_MASK, vth << 2);
+ }
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+ .mbhc_bias = pm4125_mbhc_bias_control,
+ .set_btn_thr = pm4125_mbhc_program_btn_thr,
+};
+
+static int pm4125_mbhc_init(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct wcd_mbhc_intr *intr_ids = &pm4125->intr_ids;
+
+ intr_ids->mbhc_sw_intr = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_MBHC_SW_DET);
+
+ intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_BUTTON_PRESS_DET);
+
+ intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_BUTTON_RELEASE_DET);
+
+ intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET);
+
+ intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_DET);
+
+ intr_ids->hph_left_ocp = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHL_OCP_INT);
+
+ intr_ids->hph_right_ocp = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHR_OCP_INT);
+
+ pm4125->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, pm4125_mbhc_fields, false);
+ if (IS_ERR(pm4125->wcd_mbhc))
+ return PTR_ERR(pm4125->wcd_mbhc);
+
+ return 0;
+}
+
+static void pm4125_mbhc_deinit(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ wcd_mbhc_deinit(pm4125->wcd_mbhc);
+}
+
+static const struct snd_kcontrol_new pm4125_snd_controls[] = {
+ SOC_SINGLE_EXT("HPHL_COMP Switch", PM4125_COMP_L, 0, 1, 0,
+ pm4125_get_compander, pm4125_set_compander),
+ SOC_SINGLE_EXT("HPHR_COMP Switch", PM4125_COMP_R, 1, 1, 0,
+ pm4125_get_compander, pm4125_set_compander),
+
+ SOC_SINGLE_TLV("HPHL Volume", PM4125_ANA_HPHPA_L_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", PM4125_ANA_HPHPA_R_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("ADC1 Volume", PM4125_ANA_TX_AMIC1, 0, 8, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", PM4125_ANA_TX_AMIC2, 0, 8, 0,
+ analog_gain),
+
+ SOC_SINGLE_EXT("HPHL Switch", PM4125_HPH_L, 0, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+ SOC_SINGLE_EXT("HPHR Switch", PM4125_HPH_R, 0, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+
+ SOC_SINGLE_EXT("ADC1 Switch", PM4125_ADC1, 1, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+ SOC_SINGLE_EXT("ADC2 Switch", PM4125_ADC2, 1, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+};
+
+static const struct snd_kcontrol_new adc1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc2_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic2_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new ear_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new lo_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphl_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphr_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const char * const adc2_mux_text[] = {
+ "INP2", "INP3"
+};
+
+static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE(PM4125_ANA_TX_AMIC2, 4,
+ ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);
+
+static const struct snd_soc_dapm_widget pm4125_dapm_widgets[] = {
+ /* Input widgets */
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("IN1_HPHL"),
+ SND_SOC_DAPM_INPUT("IN2_HPHR"),
+
+ /* TX widgets */
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0, pm4125_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux),
+
+ /* TX mixers */
+ SND_SOC_DAPM_MIXER("ADC1_MIXER", SND_SOC_NOPM, 0, 0, adc1_switch, ARRAY_SIZE(adc1_switch)),
+ SND_SOC_DAPM_MIXER("ADC2_MIXER", SND_SOC_NOPM, 1, 0, adc2_switch, ARRAY_SIZE(adc2_switch)),
+
+ /* MIC_BIAS widgets */
+ SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, pm4125_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, pm4125_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, pm4125_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("PA_VPOS", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* RX widgets */
+ SND_SOC_DAPM_PGA_E("EAR PGA", PM4125_ANA_COMBOPA_CTL, 7, 0, NULL, 0,
+ pm4125_codec_enable_ear_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LO PGA", PM4125_ANA_COMBOPA_CTL, 7, 0, NULL, 0,
+ pm4125_codec_enable_lo_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHL PGA", PM4125_ANA_HPHPA_CNP_CTL_2, 7, 0, NULL, 0,
+ pm4125_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHR PGA", PM4125_ANA_HPHPA_CNP_CTL_2, 6, 0, NULL, 0,
+ pm4125_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_hphl_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_hphr_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_ear_lo_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+
+ SND_SOC_DAPM_SUPPLY("HPHL_WDT_IRQ", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_hphl_wdt_irq,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("HPHR_WDT_IRQ", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_hphr_wdt_irq,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("RXCLK", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_rxclk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+
+ /* RX mixer widgets */
+ SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0, ear_rdac_switch,
+ ARRAY_SIZE(ear_rdac_switch)),
+ SND_SOC_DAPM_MIXER("LO_RDAC", SND_SOC_NOPM, 0, 0, lo_rdac_switch,
+ ARRAY_SIZE(lo_rdac_switch)),
+ SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0, hphl_rdac_switch,
+ ARRAY_SIZE(hphl_rdac_switch)),
+ SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0, hphr_rdac_switch,
+ ARRAY_SIZE(hphr_rdac_switch)),
+
+ /* TX output widgets */
+ SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"),
+
+ /* RX output widgets */
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("LO"),
+ SND_SOC_DAPM_OUTPUT("HPHL"),
+ SND_SOC_DAPM_OUTPUT("HPHR"),
+
+ /* MIC_BIAS pull up widgets */
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+ pm4125_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+ pm4125_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+ pm4125_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* TX widgets */
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, PM4125_DIG_SWR_CDC_DMIC1_CTL, 0, 0,
+ pm4125_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, PM4125_DIG_SWR_CDC_DMIC1_CTL, 1, 0,
+ pm4125_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* TX mixer widgets */
+ SND_SOC_DAPM_MIXER("DMIC1_MIXER", SND_SOC_NOPM, 0, 0, dmic1_switch,
+ ARRAY_SIZE(dmic1_switch)),
+ SND_SOC_DAPM_MIXER("DMIC2_MIXER", SND_SOC_NOPM, 1, 0, dmic2_switch,
+ ARRAY_SIZE(dmic2_switch)),
+
+ /* Output widgets */
+ SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route pm4125_audio_map[] = {
+ { "ADC1_OUTPUT", NULL, "ADC1_MIXER" },
+ { "ADC1_MIXER", "Switch", "ADC1" },
+ { "ADC1", NULL, "AMIC1" },
+
+ { "ADC2_OUTPUT", NULL, "ADC2_MIXER" },
+ { "ADC2_MIXER", "Switch", "ADC2" },
+ { "ADC2", NULL, "ADC2 MUX" },
+ { "ADC2 MUX", "INP3", "AMIC3" },
+ { "ADC2 MUX", "INP2", "AMIC2" },
+
+ { "IN1_HPHL", NULL, "PA_VPOS" },
+ { "RX1", NULL, "IN1_HPHL" },
+ { "RX1", NULL, "RXCLK" },
+ { "RX1", NULL, "HPHL_WDT_IRQ" },
+ { "RDAC1", NULL, "RX1" },
+ { "HPHL_RDAC", "Switch", "RDAC1" },
+ { "HPHL PGA", NULL, "HPHL_RDAC" },
+ { "HPHL", NULL, "HPHL PGA" },
+
+ { "IN2_HPHR", NULL, "PA_VPOS" },
+ { "RX2", NULL, "IN2_HPHR" },
+ { "RX2", NULL, "RXCLK" },
+ { "RX2", NULL, "HPHR_WDT_IRQ" },
+ { "RDAC2", NULL, "RX2" },
+ { "HPHR_RDAC", "Switch", "RDAC2" },
+ { "HPHR PGA", NULL, "HPHR_RDAC" },
+ { "HPHR", NULL, "HPHR PGA" },
+
+ { "RDAC3", NULL, "RX1" },
+ { "EAR_RDAC", "Switch", "RDAC3" },
+ { "EAR PGA", NULL, "EAR_RDAC" },
+ { "EAR", NULL, "EAR PGA" },
+
+ { "LO_RDAC", "Switch", "RDAC3" },
+ { "LO PGA", NULL, "LO_RDAC" },
+ { "LO", NULL, "LO PGA" },
+
+ { "DMIC1_OUTPUT", NULL, "DMIC1_MIXER" },
+ { "DMIC1_MIXER", "Switch", "DMIC1" },
+
+ { "DMIC2_OUTPUT", NULL, "DMIC2_MIXER" },
+ { "DMIC2_MIXER", "Switch", "DMIC2" },
+};
+
+static int pm4125_set_micbias_data(struct device *dev, struct pm4125_priv *pm4125)
+{
+ int vout_ctl;
+
+ /* Set micbias voltage */
+ vout_ctl = pm4125_get_micb_vout_ctl_val(dev, pm4125->micb1_mv);
+ if (vout_ctl < 0)
+ return -EINVAL;
+
+ regmap_update_bits(pm4125->regmap, PM4125_ANA_MICBIAS_LDO_1_SETTING,
+ PM4125_ANA_MICBIAS_MICB_OUT_VAL_MASK, vout_ctl << 3);
+ return 0;
+}
+
+static irqreturn_t pm4125_wd_handle_irq(int irq, void *data)
+{
+ /*
+ * HPHR/HPHL Watchdog interrupt threaded handler
+ * Watchdog interrupts are expected to be enabled when switching on the HPHL/R
+ * in order to make sure the interrupts are acked by the regmap_irq handler
+ * io allow PDM sync. We could leave those interrupts masked but we would
+ * not haveany valid way to enable/disable them without violating irq layers.
+ *
+ * The HPHR/HPHL Watchdog interrupts are handled by regmap_irq, so requesting
+ * a threaded handler is the safest way to be able to ack those interrupts
+ * without colliding with the regmap_irq setup.
+ */
+ return IRQ_HANDLED;
+}
+
+static const struct irq_chip pm4125_codec_irq_chip = {
+ .name = "pm4125_codec",
+};
+
+static int pm4125_codec_irq_chip_map(struct irq_domain *irqd, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &pm4125_codec_irq_chip, handle_simple_irq);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops pm4125_domain_ops = {
+ .map = pm4125_codec_irq_chip_map,
+};
+
+static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev)
+{
+ pm4125->virq = irq_domain_add_linear(NULL, 1, &pm4125_domain_ops, NULL);
+ if (!(pm4125->virq)) {
+ dev_err(dev, "%s: Failed to add IRQ domain\n", __func__);
+ return -EINVAL;
+ }
+
+ pm4125_regmap_irq_chip.irq_drv_data = pm4125;
+
+ return devm_regmap_add_irq_chip(dev, pm4125->regmap, irq_create_mapping(pm4125->virq, 0),
+ IRQF_ONESHOT, 0, &pm4125_regmap_irq_chip,
+ &pm4125->irq_chip);
+}
+
+static int pm4125_soc_codec_probe(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct sdw_slave *tx_sdw_dev = pm4125->tx_sdw_dev;
+ struct device *dev = component->dev;
+ unsigned long time_left;
+ int i, ret;
+
+ time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
+ msecs_to_jiffies(5000));
+ if (!time_left) {
+ dev_err(dev, "soundwire device init timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ snd_soc_component_init_regmap(component, pm4125->regmap);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ pm4125_io_init(pm4125->regmap);
+
+ /* Set all interrupts as edge triggered */
+ for (i = 0; i < pm4125_regmap_irq_chip.num_regs; i++)
+ regmap_write(pm4125->regmap, (PM4125_DIG_SWR_INTR_LEVEL_0 + i), 0);
+
+ pm_runtime_put(dev);
+
+ pm4125->hphr_pdm_wd_int = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHR_PDM_WD_INT);
+ pm4125->hphl_pdm_wd_int = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHL_PDM_WD_INT);
+
+ /* Request for watchdog interrupts */
+ ret = devm_request_threaded_irq(dev, pm4125->hphr_pdm_wd_int, NULL, pm4125_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHR PDM WDOG INT", pm4125);
+ if (ret)
+ dev_err(dev, "Failed to request HPHR wdt interrupt: %d\n", ret);
+
+ ret = devm_request_threaded_irq(dev, pm4125->hphl_pdm_wd_int, NULL, pm4125_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHL PDM WDOG INT", pm4125);
+ if (ret)
+ dev_err(dev, "Failed to request HPHL wdt interrupt: %d\n", ret);
+
+ disable_irq_nosync(pm4125->hphr_pdm_wd_int);
+ disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+
+ ret = pm4125_mbhc_init(component);
+ if (ret)
+ dev_err(component->dev, "mbhc initialization failed\n");
+
+ return ret;
+}
+
+static void pm4125_soc_codec_remove(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ pm4125_mbhc_deinit(component);
+ free_irq(pm4125->hphl_pdm_wd_int, pm4125);
+ free_irq(pm4125->hphr_pdm_wd_int, pm4125);
+}
+
+static int pm4125_codec_set_jack(struct snd_soc_component *comp, struct snd_soc_jack *jack,
+ void *data)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(comp->dev);
+ int ret = 0;
+
+ if (jack)
+ ret = wcd_mbhc_start(pm4125->wcd_mbhc, &pm4125->mbhc_cfg, jack);
+ else
+ wcd_mbhc_stop(pm4125->wcd_mbhc);
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_pm4125 = {
+ .name = "pm4125_codec",
+ .probe = pm4125_soc_codec_probe,
+ .remove = pm4125_soc_codec_remove,
+ .controls = pm4125_snd_controls,
+ .num_controls = ARRAY_SIZE(pm4125_snd_controls),
+ .dapm_widgets = pm4125_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pm4125_dapm_widgets),
+ .dapm_routes = pm4125_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(pm4125_audio_map),
+ .set_jack = pm4125_codec_set_jack,
+ .endianness = 1,
+};
+
+static void pm4125_dt_parse_micbias_info(struct device *dev, struct pm4125_priv *priv)
+{
+ struct device_node *np = dev->of_node;
+ u32 prop_val = 0;
+ int ret;
+
+ ret = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val);
+ if (!ret)
+ priv->micb1_mv = prop_val / 1000;
+ else
+ dev_warn(dev, "Micbias1 DT property not found\n");
+
+ ret = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val);
+ if (!ret)
+ priv->micb2_mv = prop_val / 1000;
+ else
+ dev_warn(dev, "Micbias2 DT property not found\n");
+
+ ret = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val);
+ if (!ret)
+ priv->micb3_mv = prop_val / 1000;
+ else
+ dev_warn(dev, "Micbias3 DT property not found\n");
+}
+
+static int pm4125_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+ return pm4125_sdw_hw_params(sdw_priv, substream, params, dai);
+}
+
+static int pm4125_codec_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+ return sdw_stream_remove_slave(sdw_priv->sdev, sdw_priv->sruntime);
+}
+
+static int pm4125_codec_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+ sdw_priv->sruntime = stream;
+
+ return 0;
+}
+
+static int pm4125_get_channel_map(const struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+ int i;
+
+ switch (dai->id) {
+ case AIF1_PB:
+ if (!rx_slot || !rx_num) {
+ dev_err(dai->dev, "Invalid rx_slot %p or rx_num %p\n", rx_slot, rx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDW_MAX_PORTS; i++)
+ rx_slot[i] = sdw_priv->master_channel_map[i];
+
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ if (!tx_slot || !tx_num) {
+ dev_err(dai->dev, "Invalid tx_slot %p or tx_num %p\n", tx_slot, tx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDW_MAX_PORTS; i++)
+ tx_slot[i] = sdw_priv->master_channel_map[i];
+
+ *tx_num = i;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pm4125_sdw_dai_ops = {
+ .hw_params = pm4125_codec_hw_params,
+ .hw_free = pm4125_codec_free,
+ .set_stream = pm4125_codec_set_sdw_stream,
+ .get_channel_map = pm4125_get_channel_map,
+};
+
+static struct snd_soc_dai_driver pm4125_dais[] = {
+ [0] = {
+ .name = "pm4125-sdw-rx",
+ .playback = {
+ .stream_name = "PM4125 AIF Playback",
+ .rates = PM4125_RATES | PM4125_FRAC_RATES,
+ .formats = PM4125_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &pm4125_sdw_dai_ops,
+ },
+ [1] = {
+ .name = "pm4125-sdw-tx",
+ .capture = {
+ .stream_name = "PM4125 AIF Capture",
+ .rates = PM4125_RATES,
+ .formats = PM4125_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &pm4125_sdw_dai_ops,
+ },
+};
+
+static int pm4125_bind(struct device *dev)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
+ struct device_link *devlink;
+ int ret;
+
+ /* Give the soundwire subdevices some more time to settle */
+ usleep_range(15000, 15010);
+
+ ret = component_bind_all(dev, pm4125);
+ if (ret) {
+ dev_err(dev, "Slave bind failed, ret = %d\n", ret);
+ return ret;
+ }
+
+ pm4125->rxdev = pm4125_sdw_device_get(pm4125->rxnode);
+ if (!pm4125->rxdev) {
+ dev_err(dev, "could not find rxslave with matching of node\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ pm4125->sdw_priv[AIF1_PB] = dev_get_drvdata(pm4125->rxdev);
+ pm4125->sdw_priv[AIF1_PB]->pm4125 = pm4125;
+
+ pm4125->txdev = pm4125_sdw_device_get(pm4125->txnode);
+ if (!pm4125->txdev) {
+ dev_err(dev, "could not find txslave with matching of node\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ pm4125->sdw_priv[AIF1_CAP] = dev_get_drvdata(pm4125->txdev);
+ pm4125->sdw_priv[AIF1_CAP]->pm4125 = pm4125;
+
+ pm4125->tx_sdw_dev = dev_to_sdw_dev(pm4125->txdev);
+ if (!pm4125->tx_sdw_dev) {
+ dev_err(dev, "could not get txslave with matching of dev\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ /*
+ * As TX is the main CSR reg interface, which should not be suspended first.
+ * expicilty add the dependency link
+ */
+ devlink = device_link_add(pm4125->rxdev, pm4125->txdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (!devlink) {
+ dev_err(dev, "Could not devlink TX and RX\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ devlink = device_link_add(dev, pm4125->txdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (!devlink) {
+ dev_err(dev, "Could not devlink PM4125 and TX\n");
+ ret = -EINVAL;
+ goto link_remove_rx_tx;
+ }
+
+ devlink = device_link_add(dev, pm4125->rxdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (!devlink) {
+ dev_err(dev, "Could not devlink PM4125 and RX\n");
+ ret = -EINVAL;
+ goto link_remove_dev_tx;
+ }
+
+ pm4125->regmap = dev_get_regmap(&pm4125->tx_sdw_dev->dev, NULL);
+ if (!pm4125->regmap) {
+ dev_err(dev, "could not get TX device regmap\n");
+ ret = -EINVAL;
+ goto link_remove_dev_rx;
+ }
+
+ ret = pm4125_irq_init(pm4125, dev);
+ if (ret) {
+ dev_err(dev, "IRQ init failed: %d\n", ret);
+ goto link_remove_dev_rx;
+ }
+
+ pm4125->sdw_priv[AIF1_PB]->slave_irq = pm4125->virq;
+ pm4125->sdw_priv[AIF1_CAP]->slave_irq = pm4125->virq;
+
+ ret = pm4125_set_micbias_data(dev, pm4125);
+ if (ret < 0) {
+ dev_err(dev, "Bad micbias pdata\n");
+ goto link_remove_dev_rx;
+ }
+
+ ret = snd_soc_register_component(dev, &soc_codec_dev_pm4125,
+ pm4125_dais, ARRAY_SIZE(pm4125_dais));
+ if (!ret)
+ return ret;
+
+ dev_err(dev, "Codec registration failed\n");
+
+link_remove_dev_rx:
+ device_link_remove(dev, pm4125->rxdev);
+link_remove_dev_tx:
+ device_link_remove(dev, pm4125->txdev);
+link_remove_rx_tx:
+ device_link_remove(pm4125->rxdev, pm4125->txdev);
+error_unbind_all:
+ component_unbind_all(dev, pm4125);
+ return ret;
+}
+
+static void pm4125_unbind(struct device *dev)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
+
+ snd_soc_unregister_component(dev);
+ device_link_remove(dev, pm4125->txdev);
+ device_link_remove(dev, pm4125->rxdev);
+ device_link_remove(pm4125->rxdev, pm4125->txdev);
+ component_unbind_all(dev, pm4125);
+}
+
+static const struct component_master_ops pm4125_comp_ops = {
+ .bind = pm4125_bind,
+ .unbind = pm4125_unbind,
+};
+
+static int pm4125_add_slave_components(struct pm4125_priv *pm4125, struct device *dev,
+ struct component_match **matchptr)
+{
+ struct device_node *np = dev->of_node;
+
+ pm4125->rxnode = of_parse_phandle(np, "qcom,rx-device", 0);
+ if (!pm4125->rxnode)
+ return dev_err_probe(dev, -ENODEV, "Couldn't parse phandle to qcom,rx-device\n");
+ component_match_add_release(dev, matchptr, component_release_of, component_compare_of,
+ pm4125->rxnode);
+
+ pm4125->txnode = of_parse_phandle(np, "qcom,tx-device", 0);
+ if (!pm4125->txnode)
+ return dev_err_probe(dev, -ENODEV, "Couldn't parse phandle to qcom,tx-device\n");
+ component_match_add_release(dev, matchptr, component_release_of, component_compare_of,
+ pm4125->txnode);
+
+ return 0;
+}
+
+static int pm4125_probe(struct platform_device *pdev)
+{
+ struct component_match *match = NULL;
+ struct device *dev = &pdev->dev;
+ struct pm4125_priv *pm4125;
+ struct wcd_mbhc_config *cfg;
+ int ret;
+
+ pm4125 = devm_kzalloc(dev, sizeof(*pm4125), GFP_KERNEL);
+ if (!pm4125)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, pm4125);
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(pm4125_power_supplies),
+ pm4125_power_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
+
+ pm4125->spmi_regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!pm4125->spmi_regmap)
+ return -ENXIO;
+
+ pm4125_reset(pm4125);
+
+ pm4125_dt_parse_micbias_info(dev, pm4125);
+ atomic_set(&pm4125->gloal_mbias_cnt, 0);
+
+ cfg = &pm4125->mbhc_cfg;
+ cfg->mbhc_micbias = MIC_BIAS_2;
+ cfg->anc_micbias = MIC_BIAS_2;
+ cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
+ cfg->num_btn = PM4125_MBHC_MAX_BUTTONS;
+ cfg->micb_mv = pm4125->micb2_mv;
+ cfg->linein_th = 5000;
+ cfg->hs_thr = 1700;
+ cfg->hph_thr = 50;
+
+ wcd_dt_parse_mbhc_data(dev, &pm4125->mbhc_cfg);
+
+ ret = pm4125_add_slave_components(pm4125, dev, &match);
+ if (ret)
+ return ret;
+
+ ret = component_master_add_with_match(dev, &pm4125_comp_ops, match);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+}
+
+static void pm4125_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_master_del(&pdev->dev, &pm4125_comp_ops);
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+}
+
+static const struct of_device_id pm4125_of_match[] = {
+ { .compatible = "qcom,pm4125-codec" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm4125_of_match);
+
+static struct platform_driver pm4125_codec_driver = {
+ .probe = pm4125_probe,
+ .remove = pm4125_remove,
+ .driver = {
+ .name = "pm4125_codec",
+ .of_match_table = pm4125_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+module_platform_driver(pm4125_codec_driver);
+MODULE_DESCRIPTION("PM4125 audio codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pm4125.h b/sound/soc/codecs/pm4125.h
new file mode 100644
index 0000000..3520c71
--- /dev/null
+++ b/sound/soc/codecs/pm4125.h
@@ -0,0 +1,307 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _PM4125_REGISTERS_H
+#define _PM4125_REGISTERS_H
+
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+
+#define PM4125_ANA_BASE_ADDR 0x3000
+#define PM4125_DIG_BASE_ADDR 0x3400
+
+#define PM4125_ANA_MICBIAS_MICB_1_2_EN (PM4125_ANA_BASE_ADDR+0x040)
+#define PM4125_ANA_MICBIAS_MICB1_PULL_UP_MASK BIT(5)
+#define PM4125_ANA_MICBIAS_MICB2_PULL_UP_MASK BIT(1)
+#define PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK BIT(0)
+#define PM4125_ANA_MICBIAS_MICB_PULL_ENABLE 1
+#define PM4125_ANA_MICBIAS_MICB_PULL_DISABLE 0
+#define PM4125_ANA_MICBIAS_MICB_3_EN (PM4125_ANA_BASE_ADDR+0x041)
+#define PM4125_ANA_MICBIAS_LDO_1_SETTING (PM4125_ANA_BASE_ADDR+0x042)
+#define PM4125_ANA_MICBIAS_MICB_OUT_VAL_MASK GENMASK(7, 3)
+#define PM4125_ANA_MICBIAS_LDO_1_CTRL (PM4125_ANA_BASE_ADDR+0x043)
+#define PM4125_ANA_TX_AMIC1 (PM4125_ANA_BASE_ADDR+0x047)
+#define PM4125_ANA_TX_AMIC2 (PM4125_ANA_BASE_ADDR+0x048)
+#define PM4125_ANA_MBHC_MECH (PM4125_ANA_BASE_ADDR+0x05A)
+#define PM4125_ANA_MBHC_ELECT (PM4125_ANA_BASE_ADDR+0x05B)
+#define PM4125_ANA_MBHC_ELECT_BIAS_EN_MASK BIT(0)
+#define PM4125_ANA_MBHC_ELECT_BIAS_ENABLE 1
+#define PM4125_ANA_MBHC_ELECT_BIAS_DISABLE 0
+#define PM4125_ANA_MBHC_ZDET (PM4125_ANA_BASE_ADDR+0x05C)
+#define PM4125_ANA_MBHC_RESULT_1 (PM4125_ANA_BASE_ADDR+0x05D)
+#define PM4125_ANA_MBHC_RESULT_2 (PM4125_ANA_BASE_ADDR+0x05E)
+#define PM4125_ANA_MBHC_RESULT_3 (PM4125_ANA_BASE_ADDR+0x05F)
+#define PM4125_ANA_MBHC_BTN0_ZDET_VREF1 (PM4125_ANA_BASE_ADDR+0x060)
+#define PM4125_ANA_MBHC_BTN0_THRESHOLD_MASK GENMASK(7, 2)
+#define PM4125_ANA_MBHC_BTN1_ZDET_VREF2 (PM4125_ANA_BASE_ADDR+0x061)
+#define PM4125_ANA_MBHC_BTN2_ZDET_VREF3 (PM4125_ANA_BASE_ADDR+0x062)
+#define PM4125_ANA_MBHC_BTN3_ZDET_DBG_400 (PM4125_ANA_BASE_ADDR+0x063)
+#define PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400 (PM4125_ANA_BASE_ADDR+0x064)
+#define PM4125_ANA_MBHC_MICB2_RAMP (PM4125_ANA_BASE_ADDR+0x065)
+#define PM4125_ANA_MBHC_CTL_1 (PM4125_ANA_BASE_ADDR+0x066)
+#define PM4125_ANA_MBHC_CTL_2 (PM4125_ANA_BASE_ADDR+0x067)
+#define PM4125_ANA_MBHC_PLUG_DETECT_CTL (PM4125_ANA_BASE_ADDR+0x068)
+#define PM4125_ANA_MBHC_ZDET_ANA_CTL (PM4125_ANA_BASE_ADDR+0x069)
+#define PM4125_ANA_MBHC_ZDET_RAMP_CTL (PM4125_ANA_BASE_ADDR+0x06A)
+#define PM4125_ANA_MBHC_FSM_STATUS (PM4125_ANA_BASE_ADDR+0x06B)
+#define PM4125_ANA_MBHC_ADC_RESULT (PM4125_ANA_BASE_ADDR+0x06C)
+#define PM4125_ANA_MBHC_CTL_CLK (PM4125_ANA_BASE_ADDR+0x06D)
+#define PM4125_ANA_MBHC_ZDET_CALIB_RESULT (PM4125_ANA_BASE_ADDR+0x072)
+#define PM4125_ANA_NCP_EN (PM4125_ANA_BASE_ADDR+0x077)
+#define PM4125_ANA_NCP_ENABLE_MASK BIT(0)
+#define PM4125_ANA_NCP_ENABLE 1
+#define PM4125_ANA_NCP_DISABLE 0
+#define PM4125_ANA_NCP_VCTRL (PM4125_ANA_BASE_ADDR+0x07C)
+#define PM4125_ANA_HPHPA_CNP_CTL_1 (PM4125_ANA_BASE_ADDR+0x083)
+#define PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK BIT(1)
+#define PM4125_ANA_HPHPA_CNP_CTL_1_EN 1
+#define PM4125_ANA_HPHPA_CNP_CTL_2 (PM4125_ANA_BASE_ADDR+0x084)
+#define PM4125_ANA_HPHPA_CNP_OCP_EN_L_MASK BIT(1)
+#define PM4125_ANA_HPHPA_CNP_OCP_EN_R_MASK BIT(0)
+#define PM4125_ANA_HPHPA_CNP_OCP_ENABLE 1
+#define PM4125_ANA_HPHPA_CNP_OCP_DISABLE 0
+#define PM4125_ANA_HPHPA_PA_STATUS (PM4125_ANA_BASE_ADDR+0x087)
+#define PM4125_ANA_HPHPA_FSM_CLK (PM4125_ANA_BASE_ADDR+0x088)
+#define PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK BIT(7)
+#define PM4125_ANA_HPHPA_FSM_CLK_DIV_ENABLE 1
+#define PM4125_ANA_HPHPA_FSM_CLK_DIV_DISABLE 0
+#define PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK GENMASK(6, 0)
+#define PM4125_ANA_HPHPA_FSM_DIV_RATIO_68 (0x11)
+#define PM4125_ANA_HPHPA_L_GAIN (PM4125_ANA_BASE_ADDR+0x08B)
+#define PM4125_ANA_HPHPA_R_GAIN (PM4125_ANA_BASE_ADDR+0x08C)
+#define PM4125_ANA_HPHPA_SPARE_CTL (PM4125_ANA_BASE_ADDR+0x08E)
+#define PM4125_SWR_HPHPA_HD2 (PM4125_ANA_BASE_ADDR+0x090)
+#define PM4125_SWR_HPHPA_HD2_LEFT_MASK GENMASK(5, 3)
+#define PM4125_SWR_HPHPA_HD2_RIGHT_MASK GENMASK(2, 0)
+#define PM4125_SWR_HPHPA_HD2_ENABLE (BIT(2) | BIT(1) | BIT(0))
+#define PM4125_ANA_SURGE_EN (PM4125_ANA_BASE_ADDR+0x097)
+#define PM4125_ANA_SURGE_PROTECTION_HPHL_MASK BIT(7)
+#define PM4125_ANA_SURGE_PROTECTION_HPHR_MASK BIT(6)
+#define PM4125_ANA_SURGE_PROTECTION_ENABLE 1
+#define PM4125_ANA_SURGE_PROTECTION_DISABLE 0
+#define PM4125_ANA_COMBOPA_CTL (PM4125_ANA_BASE_ADDR+0x09B)
+#define PM4125_ANA_COMBO_PA_SELECT_MASK BIT(6)
+#define PM4125_ANA_COMBO_PA_SELECT_EAR 0
+#define PM4125_ANA_COMBO_PA_SELECT_LO 1
+#define PM4125_ANA_COMBOPA_CTL_4 (PM4125_ANA_BASE_ADDR+0x09F)
+#define PM4125_ANA_COMBOPA_CTL_5 (PM4125_ANA_BASE_ADDR+0x0A0)
+#define PM4125_ANA_RXLDO_CTL (PM4125_ANA_BASE_ADDR+0x0B2)
+#define PM4125_ANA_MBIAS_EN (PM4125_ANA_BASE_ADDR+0x0B4)
+#define PM4125_ANA_MBIAS_EN_GLOBAL_MASK BIT(5)
+#define PM4125_ANA_MBIAS_EN_V2I_MASK BIT(4)
+#define PM4125_ANA_MBIAS_EN_ENABLE 1
+#define PM4125_ANA_MBIAS_EN_DISABLE 0
+
+#define PM4125_DIG_SWR_CHIP_ID0 (PM4125_DIG_BASE_ADDR+0x001)
+#define PM4125_DIG_SWR_CHIP_ID1 (PM4125_DIG_BASE_ADDR+0x002)
+#define PM4125_DIG_SWR_CHIP_ID2 (PM4125_DIG_BASE_ADDR+0x003)
+#define PM4125_DIG_SWR_CHIP_ID3 (PM4125_DIG_BASE_ADDR+0x004)
+#define PM4125_DIG_SWR_SWR_TX_CLK_RATE (PM4125_DIG_BASE_ADDR+0x040)
+#define PM4125_DIG_SWR_CDC_RST_CTL (PM4125_DIG_BASE_ADDR+0x041)
+#define PM4125_DIG_SWR_TOP_CLK_CFG (PM4125_DIG_BASE_ADDR+0x042)
+#define PM4125_DIG_SWR_CDC_RX_CLK_CTL (PM4125_DIG_BASE_ADDR+0x043)
+#define PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK BIT(5)
+#define PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK BIT(4)
+#define PM4125_DIG_SWR_RX1_CLK_EN_MASK BIT(1)
+#define PM4125_DIG_SWR_RX0_CLK_EN_MASK BIT(0)
+#define PM4125_DIG_SWR_RX_CLK_ENABLE 1
+#define PM4125_DIG_SWR_RX_CLK_DISABLE 0
+#define PM4125_DIG_SWR_CDC_TX_CLK_CTL (PM4125_DIG_BASE_ADDR+0x044)
+#define PM4125_DIG_SWR_SWR_RST_EN (PM4125_DIG_BASE_ADDR+0x045)
+#define PM4125_DIG_SWR_CDC_RX_RST (PM4125_DIG_BASE_ADDR+0x047)
+#define PM4125_DIG_SWR_CDC_RX0_CTL (PM4125_DIG_BASE_ADDR+0x048)
+#define PM4125_DIG_SWR_DSM_DITHER_EN_MASK BIT(7)
+#define PM4125_DIG_SWR_DSM_DITHER_DISABLE 0
+#define PM4125_DIG_SWR_DSM_DITHER_ENABLE 1
+#define PM4125_DIG_SWR_CDC_RX1_CTL (PM4125_DIG_BASE_ADDR+0x049)
+#define PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1 (PM4125_DIG_BASE_ADDR+0x04B)
+#define PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK GENMASK(7, 4)
+#define PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK GENMASK(3, 0)
+#define PM4125_DIG_SWR_TXD_MODE_ULPI (0x9)
+#define PM4125_DIG_SWR_TXD_MODE_NORMAL (0x3)
+#define PM4125_DIG_SWR_CDC_COMP_CTL_0 (PM4125_DIG_BASE_ADDR+0x04F)
+#define PM4125_DIG_SWR_COMP_HPHL_EN_MASK BIT(1)
+#define PM4125_DIG_SWR_COMP_HPHR_EN_MASK BIT(0)
+#define PM4125_DIG_SWR_COMP_ENABLE 1
+#define PM4125_DIG_SWR_COMP_DISABLE 0
+#define PM4125_DIG_SWR_CDC_RX_DELAY_CTL (PM4125_DIG_BASE_ADDR+0x052)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_0 (PM4125_DIG_BASE_ADDR+0x053)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_1 (PM4125_DIG_BASE_ADDR+0x054)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_CTL (PM4125_DIG_BASE_ADDR+0x057)
+#define PM4125_DIG_SWR_RX1_EN_MASK BIT(3)
+#define PM4125_DIG_SWR_RX0_EN_MASK BIT(2)
+#define PM4125_DIG_SWR_RX_INPUT_DISABLE 0
+#define PM4125_DIG_SWR_RX_INPUT_ENABLE 1
+#define PM4125_DIG_SWR_CDC_TX0_CTL (PM4125_DIG_BASE_ADDR+0x060)
+#define PM4125_DIG_SWR_CDC_TX1_CTL (PM4125_DIG_BASE_ADDR+0x061)
+#define PM4125_DIG_SWR_CDC_TX_RST (PM4125_DIG_BASE_ADDR+0x063)
+#define PM4125_DIG_SWR_CDC_REQ0_CTL (PM4125_DIG_BASE_ADDR+0x064)
+#define PM4125_DIG_SWR_CDC_REQ1_CTL (PM4125_DIG_BASE_ADDR+0x065)
+#define PM4125_DIG_SWR_CDC_RST (PM4125_DIG_BASE_ADDR+0x067)
+#define PM4125_DIG_SWR_CDC_AMIC_CTL (PM4125_DIG_BASE_ADDR+0x06A)
+#define PM4125_DIG_SWR_AMIC_SELECT_MASK BIT(1)
+#define PM4125_DIG_SWR_AMIC_SELECT_DMIC1 0
+#define PM4125_DIG_SWR_AMIC_SELECT_AMIC3 1
+#define PM4125_DIG_SWR_CDC_DMIC_CTL (PM4125_DIG_BASE_ADDR+0x06B)
+#define PM4125_DIG_SWR_CDC_DMIC1_CTL (PM4125_DIG_BASE_ADDR+0x06C)
+#define PM4125_DIG_SWR_DMIC1_CLK_EN_MASK BIT(3)
+#define PM4125_DIG_SWR_DMIC1_CLK_ENABLE 1
+#define PM4125_DIG_SWR_DMIC1_CLK_DISABLE 0
+#define PM4125_DIG_SWR_CDC_DMIC1_RATE (PM4125_DIG_BASE_ADDR+0x06D)
+#define PM4125_DIG_SWR_PDM_WD_CTL0 (PM4125_DIG_BASE_ADDR+0x070)
+#define PM4125_WDT_ENABLE_MASK GENMASK(1, 0)
+#define PM4125_WDT_ENABLE_RX0_L BIT(0)
+#define PM4125_WDT_ENABLE_RX0_M BIT(1)
+#define PM4125_DIG_SWR_PDM_WD_CTL1 (PM4125_DIG_BASE_ADDR+0x071)
+#define PM4125_WDT_ENABLE_RX1_L BIT(0)
+#define PM4125_WDT_ENABLE_RX1_M BIT(1)
+#define PM4125_DIG_SWR_INTR_MODE (PM4125_DIG_BASE_ADDR+0x080)
+#define PM4125_DIG_SWR_INTR_MASK_0 (PM4125_DIG_BASE_ADDR+0x081)
+#define PM4125_DIG_SWR_INTR_MASK_1 (PM4125_DIG_BASE_ADDR+0x082)
+#define PM4125_DIG_SWR_INTR_MASK_2 (PM4125_DIG_BASE_ADDR+0x083)
+#define PM4125_DIG_SWR_INTR_STATUS_0 (PM4125_DIG_BASE_ADDR+0x084)
+#define PM4125_DIG_SWR_INTR_STATUS_1 (PM4125_DIG_BASE_ADDR+0x085)
+#define PM4125_DIG_SWR_INTR_STATUS_2 (PM4125_DIG_BASE_ADDR+0x086)
+#define PM4125_DIG_SWR_INTR_CLEAR_0 (PM4125_DIG_BASE_ADDR+0x087)
+#define PM4125_DIG_SWR_INTR_CLEAR_1 (PM4125_DIG_BASE_ADDR+0x088)
+#define PM4125_DIG_SWR_INTR_CLEAR_2 (PM4125_DIG_BASE_ADDR+0x089)
+#define PM4125_DIG_SWR_INTR_LEVEL_0 (PM4125_DIG_BASE_ADDR+0x08A)
+#define PM4125_DIG_SWR_INTR_LEVEL_1 (PM4125_DIG_BASE_ADDR+0x08B)
+#define PM4125_DIG_SWR_INTR_LEVEL_2 (PM4125_DIG_BASE_ADDR+0x08C)
+#define PM4125_DIG_SWR_CDC_CONN_RX0_CTL (PM4125_DIG_BASE_ADDR+0x093)
+#define PM4125_DIG_SWR_CDC_CONN_RX1_CTL (PM4125_DIG_BASE_ADDR+0x094)
+#define PM4125_DIG_SWR_LOOP_BACK_MODE (PM4125_DIG_BASE_ADDR+0x097)
+#define PM4125_DIG_SWR_DRIVE_STRENGTH_0 (PM4125_DIG_BASE_ADDR+0x0A0)
+#define PM4125_DIG_SWR_DIG_DEBUG_CTL (PM4125_DIG_BASE_ADDR+0x0AB)
+#define PM4125_DIG_SWR_DIG_DEBUG_EN (PM4125_DIG_BASE_ADDR+0x0AC)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA0 (PM4125_DIG_BASE_ADDR+0x0B0)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA1 (PM4125_DIG_BASE_ADDR+0x0B1)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA2 (PM4125_DIG_BASE_ADDR+0x0B2)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA3 (PM4125_DIG_BASE_ADDR+0x0B3)
+
+#define PM4125_ANALOG_REGISTERS_MAX_SIZE (PM4125_ANA_BASE_ADDR+0x0B5)
+#define PM4125_DIGITAL_REGISTERS_MAX_SIZE (PM4125_DIG_BASE_ADDR+0x0B4)
+#define PM4125_ANALOG_MAX_REGISTER (PM4125_ANALOG_REGISTERS_MAX_SIZE - 1)
+#define PM4125_DIGITAL_MAX_REGISTER (PM4125_DIGITAL_REGISTERS_MAX_SIZE - 1)
+#define PM4125_MAX_REGISTER PM4125_DIGITAL_MAX_REGISTER
+
+#define PM4125_MAX_MICBIAS 3
+#define PM4125_MAX_SWR_CH_IDS 15
+#define PM4125_SWRM_CH_MASK(ch_idx) BIT(ch_idx - 1)
+
+enum pm4125_tx_sdw_ports {
+ PM4125_ADC_1_2_DMIC1L_BCS_PORT = 1,
+ PM4125_DMIC_1L_1R_ADC1_BCS_PORT,
+ PM4125_MAX_TX_SWR_PORTS = PM4125_DMIC_1L_1R_ADC1_BCS_PORT,
+};
+
+enum pm4125_rx_sdw_ports {
+ PM4125_HPH_PORT = 1,
+ PM4125_COMP_PORT,
+ PM4125_MAX_SWR_PORTS = PM4125_COMP_PORT,
+};
+
+struct pm4125_sdw_ch_info {
+ int port_num;
+ unsigned int ch_mask;
+ unsigned int master_ch_mask;
+};
+
+#define WCD_SDW_CH(id, pn, cmask) \
+ [id] = { \
+ .port_num = pn, \
+ .ch_mask = cmask, \
+ .master_ch_mask = cmask, \
+ }
+
+struct pm4125_priv;
+struct pm4125_sdw_priv {
+ struct sdw_slave *sdev;
+ struct sdw_stream_config sconfig;
+ struct sdw_stream_runtime *sruntime;
+ struct sdw_port_config port_config[PM4125_MAX_SWR_PORTS];
+ struct pm4125_sdw_ch_info *ch_info;
+ bool port_enable[PM4125_MAX_SWR_CH_IDS];
+ unsigned int master_channel_map[SDW_MAX_PORTS];
+ int active_ports;
+ int num_ports;
+ bool is_tx;
+ struct pm4125_priv *pm4125;
+ struct irq_domain *slave_irq;
+ struct regmap *regmap;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_PM4125_SDW)
+int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125, struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125, struct snd_soc_dai *dai, void *stream,
+ int direction);
+int pm4125_sdw_hw_params(struct pm4125_sdw_priv *pm4125, struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai);
+
+struct device *pm4125_sdw_device_get(struct device_node *np);
+
+#else
+static inline int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125,
+ struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125,
+ struct snd_soc_dai *dai, void *stream, int direction)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pm4125_sdw_hw_params(struct pm4125_sdw_priv *pm4125,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+enum {
+ /* INTR_CTRL_INT_MASK_0 */
+ PM4125_IRQ_MBHC_BUTTON_PRESS_DET = 0,
+ PM4125_IRQ_MBHC_BUTTON_RELEASE_DET,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_DET,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+ PM4125_IRQ_MBHC_SW_DET,
+ PM4125_IRQ_HPHR_OCP_INT,
+ PM4125_IRQ_HPHR_CNP_INT,
+ PM4125_IRQ_HPHL_OCP_INT,
+
+ /* INTR_CTRL_INT_MASK_1 */
+ PM4125_IRQ_HPHL_CNP_INT,
+ PM4125_IRQ_EAR_CNP_INT,
+ PM4125_IRQ_EAR_SCD_INT,
+ PM4125_IRQ_AUX_CNP_INT,
+ PM4125_IRQ_AUX_SCD_INT,
+ PM4125_IRQ_HPHL_PDM_WD_INT,
+ PM4125_IRQ_HPHR_PDM_WD_INT,
+ PM4125_IRQ_AUX_PDM_WD_INT,
+
+ /* INTR_CTRL_INT_MASK_2 */
+ PM4125_IRQ_LDORT_SCD_INT,
+ PM4125_IRQ_MBHC_MOISTURE_INT,
+ PM4125_IRQ_HPHL_SURGE_DET_INT,
+ PM4125_IRQ_HPHR_SURGE_DET_INT,
+ PM4125_NUM_IRQS,
+};
+
+enum pm4125_tx_sdw_channels {
+ PM4125_ADC1,
+ PM4125_ADC2,
+};
+
+enum pm4125_rx_sdw_channels {
+ PM4125_HPH_L,
+ PM4125_HPH_R,
+ PM4125_COMP_L,
+ PM4125_COMP_R,
+};
+
+#endif /* _PM4125_REGISTERS_H */
diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c
index dcddc28..e3f9b03 100644
--- a/sound/soc/codecs/rt1320-sdw.c
+++ b/sound/soc/codecs/rt1320-sdw.c
@@ -256,6 +256,161 @@ static const struct reg_sequence rt1320_vc_blind_write[] = {
{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
};
+static const struct reg_sequence rt1321_blind_write[] = {
+ { 0x0000c003, 0xf0 },
+ { 0x0000c01b, 0xfc },
+ { 0x0000c5c3, 0xf2 },
+ { 0x0000c5c2, 0x00 },
+ { 0x0000c5c1, 0x10 },
+ { 0x0000c5c0, 0x04 },
+ { 0x0000c5c7, 0x03 },
+ { 0x0000c5c6, 0x10 },
+ { 0x0000c526, 0x47 },
+ { 0x0000c5c4, 0x12 },
+ { 0x0000c5c5, 0x60 },
+ { 0x0000c520, 0x10 },
+ { 0x0000c521, 0x32 },
+ { 0x0000c5c7, 0x00 },
+ { 0x0000c5c8, 0x03 },
+ { 0x0000c5d3, 0x08 },
+ { 0x0000c5d2, 0x0a },
+ { 0x0000c5d1, 0x49 },
+ { 0x0000c5d0, 0x0f },
+ { 0x0000c580, 0x10 },
+ { 0x0000c581, 0x32 },
+ { 0x0000c582, 0x01 },
+ { 0x0000cb00, 0x03 },
+ { 0x0000cb02, 0x52 },
+ { 0x0000cb04, 0x80 },
+ { 0x0000cb0b, 0x01 },
+ { 0x0000c682, 0x60 },
+ { 0x0000c019, 0x10 },
+ { 0x0000c5f0, 0x01 },
+ { 0x0000c5f7, 0x22 },
+ { 0x0000c5f6, 0x22 },
+ { 0x0000c057, 0x51 },
+ { 0x0000c054, 0x55 },
+ { 0x0000c053, 0x55 },
+ { 0x0000c052, 0x55 },
+ { 0x0000c051, 0x01 },
+ { 0x0000c050, 0x15 },
+ { 0x0000c060, 0x99 },
+ { 0x0000c030, 0x55 },
+ { 0x0000c061, 0x55 },
+ { 0x0000c063, 0x55 },
+ { 0x0000c065, 0xa5 },
+ { 0x0000c06b, 0x0a },
+ { 0x0000ca05, 0xd6 },
+ { 0x0000ca07, 0x07 },
+ { 0x0000ca25, 0xd6 },
+ { 0x0000ca27, 0x07 },
+ { 0x0000cd00, 0x05 },
+ { 0x0000c604, 0x40 },
+ { 0x0000c609, 0x40 },
+ { 0x0000c046, 0xf7 },
+ { 0x0000c045, 0xff },
+ { 0x0000c044, 0xff },
+ { 0x0000c043, 0xff },
+ { 0x0000c042, 0xff },
+ { 0x0000c041, 0xff },
+ { 0x0000c040, 0xff },
+ { 0x0000c049, 0xff },
+ { 0x0000c028, 0x3f },
+ { 0x0000c020, 0x3f },
+ { 0x0000c032, 0x13 },
+ { 0x0000c033, 0x01 },
+ { 0x0000cc10, 0x01 },
+ { 0x0000dc20, 0x03 },
+ { 0x0000de03, 0x05 },
+ { 0x0000dc00, 0x00 },
+ { 0x0000c700, 0xf0 },
+ { 0x0000c701, 0x13 },
+ { 0x0000c900, 0xc3 },
+ { 0x0000c570, 0x08 },
+ { 0x0000c086, 0x02 },
+ { 0x0000c085, 0x7f },
+ { 0x0000c084, 0x00 },
+ { 0x0000c081, 0xff },
+ { 0x0000f084, 0x0f },
+ { 0x0000f083, 0xff },
+ { 0x0000f082, 0xff },
+ { 0x0000f081, 0xff },
+ { 0x0000f080, 0xff },
+ { 0x20003003, 0x3f },
+ { 0x20005818, 0x81 },
+ { 0x20009018, 0x81 },
+ { 0x2000301c, 0x81 },
+ { 0x0000c003, 0xc0 },
+ { 0x0000c047, 0x80 },
+ { 0x0000d541, 0x80 },
+ { 0x0000d487, 0x0b },
+ { 0x0000d487, 0x3b },
+ { 0x0000d486, 0xc3 },
+ { 0x0000d470, 0x89 },
+ { 0x0000d471, 0x3a },
+ { 0x0000d472, 0x1d },
+ { 0x0000d478, 0xff },
+ { 0x0000d479, 0x20 },
+ { 0x0000d47a, 0x10 },
+ { 0x0000d73c, 0xb7 },
+ { 0x0000d73d, 0xd7 },
+ { 0x0000d73e, 0x00 },
+ { 0x0000d73f, 0x10 },
+ { 0x3fc2dfc3, 0x00 },
+ { 0x3fc2dfc2, 0x00 },
+ { 0x3fc2dfc1, 0x00 },
+ { 0x3fc2dfc0, 0x07 },
+ { 0x3fc2dfc7, 0x00 },
+ { 0x3fc2dfc6, 0x00 },
+ { 0x3fc2dfc5, 0x00 },
+ { 0x3fc2dfc4, 0x01 },
+ { 0x3fc2df83, 0x00 },
+ { 0x3fc2df82, 0x00 },
+ { 0x3fc2df81, 0x00 },
+ { 0x3fc2df80, 0x00 },
+ { 0x0000d541, 0x40 },
+ { 0x0000d486, 0x43 },
+ { 0x1000db00, 0x03 },
+ { 0x1000db01, 0x00 },
+ { 0x1000db02, 0x10 },
+ { 0x1000db03, 0x00 },
+ { 0x1000db04, 0x00 },
+ { 0x1000db05, 0x45 },
+ { 0x1000db06, 0x12 },
+ { 0x1000db07, 0x09 },
+ { 0x1000db08, 0x00 },
+ { 0x1000db09, 0x00 },
+ { 0x1000db0a, 0x00 },
+ { 0x1000db0b, 0x13 },
+ { 0x1000db0c, 0x09 },
+ { 0x1000db0d, 0x00 },
+ { 0x1000db0e, 0x00 },
+ { 0x1000db0f, 0x00 },
+ { 0x0000d540, 0x21 },
+ { 0x41000189, 0x00 },
+ { 0x4100018a, 0x00 },
+ { 0x41001988, 0x00 },
+ { 0x41081400, 0x09 },
+ { 0x40801508, 0x03 },
+ { 0x40801588, 0x03 },
+ { 0x40801809, 0x00 },
+ { 0x4080180a, 0x00 },
+ { 0x4080180b, 0x00 },
+ { 0x4080180c, 0x00 },
+ { 0x40801b09, 0x00 },
+ { 0x40801b0a, 0x00 },
+ { 0x40801b0b, 0x00 },
+ { 0x40801b0c, 0x00 },
+ { 0x0000d714, 0x17 },
+ { 0x20009012, 0x00 },
+ { 0x0000dd0b, 0x0d },
+ { 0x0000dd0a, 0xff },
+ { 0x0000dd09, 0x0d },
+ { 0x0000dd08, 0xff },
+ { 0x0000d172, 0x2a },
+ { 0x41001988, 0x03 },
+};
+
static const struct reg_default rt1320_reg_defaults[] = {
{ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
{ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
@@ -340,10 +495,19 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg)
case 0xf717 ... 0xf719:
case 0xf720 ... 0xf723:
case 0x1000cd91 ... 0x1000cd96:
+ case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER:
case 0x1000f008:
case 0x1000f021:
+ case 0x2000300f:
+ case 0x2000301c:
+ case 0x2000900f:
+ case 0x20009018:
+ case 0x3fc29d80 ... 0x3fc29d83:
case 0x3fe2e000 ... 0x3fe2e003:
case 0x3fc2ab80 ... 0x3fc2abd4:
+ case 0x3fc2bfc0 ... 0x3fc2bfc8:
+ case 0x3fc2d300 ... 0x3fc2d354:
+ case 0x3fc2dfc0 ... 0x3fc2dfc8:
/* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */
case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01):
@@ -394,6 +558,7 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
case 0xc560:
case 0xc5b5 ... 0xc5b7:
case 0xc5fc ... 0xc5ff:
+ case 0xc680 ... 0xc683:
case 0xc820:
case 0xc900:
case 0xc920:
@@ -412,7 +577,7 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
case 0xd4e5 ... 0xd4e6:
case 0xd4e8 ... 0xd4ff:
case 0xd530:
- case 0xd540:
+ case 0xd540 ... 0xd541:
case 0xd543:
case 0xdb58 ... 0xdb5f:
case 0xdb60 ... 0xdb63:
@@ -429,13 +594,20 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
case 0xf01e:
case 0xf717 ... 0xf719:
case 0xf720 ... 0xf723:
- case 0x10000000 ... 0x10007fff:
+ case 0x10000000 ... 0x10008fff:
case 0x1000c000 ... 0x1000dfff:
case 0x1000f008:
case 0x1000f021:
+ case 0x2000300f:
+ case 0x2000301c:
+ case 0x2000900f:
+ case 0x20009018:
case 0x3fc2ab80 ... 0x3fc2abd4:
+ case 0x3fc2b780:
case 0x3fc2bf80 ... 0x3fc2bf83:
- case 0x3fc2bfc0 ... 0x3fc2bfc7:
+ case 0x3fc2bfc0 ... 0x3fc2bfc8:
+ case 0x3fc2d300 ... 0x3fc2d354:
+ case 0x3fc2dfc0 ... 0x3fc2dfc8:
case 0x3fe2e000 ... 0x3fe2e003:
case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0):
@@ -560,7 +732,7 @@ static int rt1320_read_prop(struct sdw_slave *slave)
static int rt1320_pde_transition_delay(struct rt1320_sdw_priv *rt1320, unsigned char func,
unsigned char entity, unsigned char ps)
{
- unsigned int delay = 1000, val;
+ unsigned int delay = 2000, val;
pm_runtime_mark_last_busy(&rt1320->sdw_slave->dev);
@@ -591,24 +763,44 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320)
struct sdw_slave *slave = rt1320->sdw_slave;
const struct firmware *patch;
const char *filename;
- unsigned int addr, val;
+ unsigned int addr, val, min_addr, max_addr;
const unsigned char *ptr;
int ret, i;
- if (rt1320->version_id <= RT1320_VB)
- filename = RT1320_VAB_MCU_PATCH;
- else
- filename = RT1320_VC_MCU_PATCH;
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (rt1320->version_id <= RT1320_VB)
+ filename = RT1320_VAB_MCU_PATCH;
+ else
+ filename = RT1320_VC_MCU_PATCH;
+ min_addr = 0x10007000;
+ max_addr = 0x10007fff;
+ break;
+ case RT1321_DEV_ID:
+ filename = RT1321_VA_MCU_PATCH;
+ min_addr = 0x10008000;
+ max_addr = 0x10008fff;
+ break;
+ default:
+ dev_err(&slave->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return;
+ }
/* load the patch code here */
ret = request_firmware(&patch, filename, &slave->dev);
if (ret) {
dev_err(&slave->dev, "%s: Failed to load %s firmware", __func__, filename);
regmap_write(rt1320->regmap, 0xc598, 0x00);
- regmap_write(rt1320->regmap, 0x10007000, 0x67);
- regmap_write(rt1320->regmap, 0x10007001, 0x80);
- regmap_write(rt1320->regmap, 0x10007002, 0x00);
- regmap_write(rt1320->regmap, 0x10007003, 0x00);
+ regmap_write(rt1320->regmap, min_addr, 0x67);
+ regmap_write(rt1320->regmap, min_addr + 0x1, 0x80);
+ regmap_write(rt1320->regmap, min_addr + 0x2, 0x00);
+ regmap_write(rt1320->regmap, min_addr + 0x3, 0x00);
+ if (rt1320->dev_id == RT1321_DEV_ID) {
+ regmap_write(rt1320->regmap, 0xd73c, 0x67);
+ regmap_write(rt1320->regmap, 0xd73d, 0x80);
+ regmap_write(rt1320->regmap, 0xd73e, 0x00);
+ regmap_write(rt1320->regmap, 0xd73f, 0x00);
+ }
} else {
ptr = (const unsigned char *)patch->data;
if ((patch->size % 8) == 0) {
@@ -618,7 +810,7 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320)
val = (ptr[i + 4] & 0xff) | (ptr[i + 5] & 0xff) << 8 |
(ptr[i + 6] & 0xff) << 16 | (ptr[i + 7] & 0xff) << 24;
- if (addr > 0x10007fff || addr < 0x10007000) {
+ if (addr > max_addr || addr < min_addr) {
dev_err(&slave->dev, "%s: the address 0x%x is wrong", __func__, addr);
goto _exit_;
}
@@ -688,6 +880,28 @@ static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320)
}
}
+static void rt1321_preset(struct rt1320_sdw_priv *rt1320)
+{
+ unsigned int i, reg, val, delay;
+
+ for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) {
+ reg = rt1321_blind_write[i].reg;
+ val = rt1321_blind_write[i].def;
+ delay = rt1321_blind_write[i].delay_us;
+
+ if (reg == 0x3fc2dfc3)
+ rt1320_load_mcu_patch(rt1320);
+
+ regmap_write(rt1320->regmap, reg, val);
+
+ if (delay)
+ usleep_range(delay, delay + 1000);
+
+ if (reg == SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0))
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, val);
+ }
+}
+
static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
{
struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev);
@@ -714,6 +928,9 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
if (rt1320->version_id < 0) {
regmap_read(rt1320->regmap, RT1320_DEV_VERSION_ID_1, &val);
rt1320->version_id = val;
+ regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val);
+ regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp);
+ rt1320->dev_id = (val << 8) | tmp;
}
regmap_read(rt1320->regmap,
@@ -722,16 +939,25 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
/* initialization write */
if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) {
- if (rt1320->version_id < RT1320_VC)
- rt1320_vab_preset(rt1320);
- else
- rt1320_vc_preset(rt1320);
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (rt1320->version_id < RT1320_VC)
+ rt1320_vab_preset(rt1320);
+ else
+ rt1320_vc_preset(rt1320);
+ break;
+ case RT1321_DEV_ID:
+ rt1321_preset(rt1320);
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ }
regmap_write(rt1320->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0),
FUNCTION_NEEDS_INITIALIZATION);
}
- if (!rt1320->first_hw_init && rt1320->version_id == RT1320_VA) {
+ if (!rt1320->first_hw_init && rt1320->version_id == RT1320_VA && rt1320->dev_id == RT1320_DEV_ID) {
regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0);
regmap_read(rt1320->regmap, RT1320_HIFI_VER_0, &val);
@@ -751,7 +977,7 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 3);
}
- dev_dbg(dev, "%s version_id=%d\n", __func__, rt1320->version_id);
+ dev_dbg(dev, "%s version_id=%d, dev_id=0x%x\n", __func__, rt1320->version_id, rt1320->dev_id);
if (rt1320->first_hw_init) {
regcache_cache_bypass(rt1320->regmap, false);
@@ -894,12 +1120,20 @@ static int rt1320_set_gain_put(struct snd_kcontrol *kcontrol,
/* check all channels */
for (i = 0; i < p->count; i++) {
- if (i < 2) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2) {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, ®value[i]);
+ } else {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i - 2, ®value[i]);
+ }
+ break;
+ case RT1321_DEV_ID:
reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
regmap_read(rt1320->mbq_regmap, reg_base + i, ®value[i]);
- } else {
- reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
- regmap_read(rt1320->mbq_regmap, reg_base + i - 2, ®value[i]);
+ break;
}
gain_val[i] = ucontrol->value.integer.value[i];
@@ -916,12 +1150,20 @@ static int rt1320_set_gain_put(struct snd_kcontrol *kcontrol,
return 0;
for (i = 0; i < p->count; i++) {
- if (i < 2) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2) {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ err = regmap_write(rt1320->mbq_regmap, reg_base + i, gain_val[i]);
+ } else {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ err = regmap_write(rt1320->mbq_regmap, reg_base + i - 2, gain_val[i]);
+ }
+ break;
+ case RT1321_DEV_ID:
reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
err = regmap_write(rt1320->mbq_regmap, reg_base + i, gain_val[i]);
- } else {
- reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
- err = regmap_write(rt1320->mbq_regmap, reg_base + i - 2, gain_val[i]);
+ break;
}
if (err < 0)
@@ -966,12 +1208,20 @@ static int rt1320_set_gain_get(struct snd_kcontrol *kcontrol,
/* check all channels */
for (i = 0; i < p->count; i++) {
- if (i < 2) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2) {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, ®value);
+ } else {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i - 2, ®value);
+ }
+ break;
+ case RT1321_DEV_ID:
reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
regmap_read(rt1320->mbq_regmap, reg_base + i, ®value);
- } else {
- reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
- regmap_read(rt1320->mbq_regmap, reg_base + i - 2, ®value);
+ break;
}
ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
@@ -989,14 +1239,26 @@ static int rt1320_set_fu_capture_ctl(struct rt1320_sdw_priv *rt1320)
for (i = 0; i < ARRAY_SIZE(rt1320->fu_mixer_mute); i++) {
ch_mute = (rt1320->fu_dapm_mute || rt1320->fu_mixer_mute[i]) ? 0x01 : 0x00;
- if (i < 2)
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2)
+ err = regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113,
+ RT1320_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ else
+ err = regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14,
+ RT1320_SDCA_CTL_FU_MUTE, CH_01) + i - 2, ch_mute);
+ break;
+ case RT1321_DEV_ID:
err = regmap_write(rt1320->regmap,
SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113,
RT1320_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
- else
- err = regmap_write(rt1320->regmap,
- SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14,
- RT1320_SDCA_CTL_FU_MUTE, CH_01) + i - 2, ch_mute);
+ break;
+ default:
+ dev_err(&rt1320->sdw_slave->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
if (err < 0)
return err;
}
@@ -1217,10 +1479,20 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream,
if (dai->id == RT1320_AIF1)
port_config.num = 4;
else if (dai->id == RT1320_AIF2) {
- dmic_port_config[0].ch_mask = BIT(0) | BIT(1);
- dmic_port_config[0].num = 8;
- dmic_port_config[1].ch_mask = BIT(0) | BIT(1);
- dmic_port_config[1].num = 10;
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ dmic_port_config[0].ch_mask = BIT(0) | BIT(1);
+ dmic_port_config[0].num = 8;
+ dmic_port_config[1].ch_mask = BIT(0) | BIT(1);
+ dmic_port_config[1].num = 10;
+ break;
+ case RT1321_DEV_ID:
+ dmic_port_config[0].ch_mask = BIT(0) | BIT(1);
+ dmic_port_config[0].num = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
} else
return -EINVAL;
}
@@ -1228,10 +1500,21 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream,
if (dai->id == RT1320_AIF1)
retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
&port_config, 1, sdw_stream);
- else if (dai->id == RT1320_AIF2)
- retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+ else if (dai->id == RT1320_AIF2) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
dmic_port_config, 2, sdw_stream);
- else
+ break;
+ case RT1321_DEV_ID:
+ retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+ dmic_port_config, 1, sdw_stream);
+ break;
+ default:
+ dev_err(dai->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
+ } else
return -EINVAL;
if (retval) {
dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
@@ -1273,9 +1556,11 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream,
regmap_write(rt1320->regmap,
SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
sampling_rate);
- regmap_write(rt1320->regmap,
- SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
- sampling_rate);
+
+ if (rt1320->dev_id == RT1320_DEV_ID)
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
}
return 0;
@@ -1469,6 +1754,7 @@ static int rt1320_sdw_remove(struct sdw_slave *slave)
static const struct sdw_device_id rt1320_id[] = {
SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x0, 0),
SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1321, 0x3, 0x1, 0),
{},
};
MODULE_DEVICE_TABLE(sdw, rt1320_id);
diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h
index 23b321a..a6d90e2 100644
--- a/sound/soc/codecs/rt1320-sdw.h
+++ b/sound/soc/codecs/rt1320-sdw.h
@@ -14,8 +14,16 @@
#include <linux/soundwire/sdw_registers.h>
#include <sound/soc.h>
+#define RT1320_DEV_ID 0x6981
+#define RT1321_DEV_ID 0x7045
+
/* imp-defined registers */
#define RT1320_DEV_VERSION_ID_1 0xc404
+#define RT1320_DEV_ID_1 0xc405
+#define RT1320_DEV_ID_0 0xc406
+
+#define RT1321_PATCH_MAIN_VER 0x1000cffe
+#define RT1321_PATCH_BETA_VER 0x1000cfff
#define RT1320_KR0_STATUS_CNT 0x1000f008
#define RT1320_KR0_INT_READY 0x1000f021
@@ -86,6 +94,7 @@ enum rt1320_version_id {
#define RT1320_VER_B_ID 0x07392238
#define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin"
#define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin"
+#define RT1321_VA_MCU_PATCH "realtek/rt1320/rt1321-patch-code-va.bin"
struct rt1320_sdw_priv {
struct snd_soc_component *component;
@@ -96,6 +105,7 @@ struct rt1320_sdw_priv {
bool hw_init;
bool first_hw_init;
int version_id;
+ unsigned int dev_id;
bool fu_dapm_mute;
bool fu_mixer_mute[4];
};
diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c
index 582b47d..4d8a12b 100644
--- a/sound/soc/codecs/rt721-sdca-sdw.c
+++ b/sound/soc/codecs/rt721-sdca-sdw.c
@@ -63,15 +63,14 @@ static bool rt721_sdca_volatile_register(struct device *dev, unsigned int reg)
static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case 0x0900007:
+ case 0x0900004 ... 0x0900009:
case 0x0a00005:
case 0x0c00005:
case 0x0d00014:
case 0x0310100:
- case 0x2000001:
- case 0x2000002:
- case 0x2000003:
+ case 0x2000000 ... 0x2000003:
case 0x2000013:
+ case 0x200002c:
case 0x200003c:
case 0x2000046:
case 0x5810000:
@@ -134,6 +133,8 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re
{
switch (reg) {
case 0x0310100:
+ case 0x0900005:
+ case 0x0900009:
case 0x0a00005:
case 0x0c00005:
case 0x0d00014:
@@ -141,6 +142,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re
case 0x200000d:
case 0x2000019:
case 0x2000020:
+ case 0x200002c:
case 0x2000030:
case 0x2000046:
case 0x2000067:
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
index c9c1e60..8baf562 100644
--- a/sound/soc/codecs/tas2781-fmwlib.c
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -180,6 +180,16 @@ static struct tasdevice_config_info *tasdevice_add_config(
dev_err(tas_priv->dev, "add conf: Out of boundary\n");
goto out;
}
+ /* If in the RCA bin file are several profiles with the
+ * keyword "init", init_profile_id only store the last
+ * init profile id.
+ */
+ if (strnstr(&config_data[config_offset], "init", 64)) {
+ tas_priv->rcabin.init_profile_id =
+ tas_priv->rcabin.ncfgs - 1;
+ dev_dbg(tas_priv->dev, "%s: init profile id = %d\n",
+ __func__, tas_priv->rcabin.init_profile_id);
+ }
config_offset += 64;
}
@@ -283,6 +293,8 @@ int tasdevice_rca_parser(void *context, const struct firmware *fmw)
int i;
rca = &(tas_priv->rcabin);
+ /* Initialize to none */
+ rca->init_profile_id = -1;
fw_hdr = &(rca->fw_hdr);
if (!fmw || !fmw->data) {
dev_err(tas_priv->dev, "Failed to read %s\n",
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index 0e09d79..ea3cdb8 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -1641,6 +1641,12 @@ static void tasdevice_fw_ready(const struct firmware *fmw,
tasdevice_prmg_load(tas_priv, 0);
tas_priv->cur_prog = 0;
+ /* Init common setting for different audio profiles */
+ if (tas_priv->rcabin.init_profile_id >= 0)
+ tasdevice_select_cfg_blk(tas_priv,
+ tas_priv->rcabin.init_profile_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
if (tas_priv->name_prefix)
acoustic_debugfs_node = devm_kasprintf(tas_priv->dev,
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 7399080..715a07a 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -1277,8 +1277,8 @@ static int aic32x4_setup_regulators(struct device *dev,
/* Check if the regulator requirements are fulfilled */
if (IS_ERR(aic32x4->supply_iov)) {
- dev_err(dev, "Missing supply 'iov'\n");
- return PTR_ERR(aic32x4->supply_iov);
+ return dev_err_probe(dev, PTR_ERR(aic32x4->supply_iov),
+ "Missing supply 'iov'\n");
}
if (IS_ERR(aic32x4->supply_ldo)) {
@@ -1286,12 +1286,12 @@ static int aic32x4_setup_regulators(struct device *dev,
return -EPROBE_DEFER;
if (IS_ERR(aic32x4->supply_dv)) {
- dev_err(dev, "Missing supply 'dv' or 'ldoin'\n");
- return PTR_ERR(aic32x4->supply_dv);
+ return dev_err_probe(dev, PTR_ERR(aic32x4->supply_dv),
+ "Missing supply 'dv' or 'ldoin'\n");
}
if (IS_ERR(aic32x4->supply_av)) {
- dev_err(dev, "Missing supply 'av' or 'ldoin'\n");
- return PTR_ERR(aic32x4->supply_av);
+ return dev_err_probe(dev, PTR_ERR(aic32x4->supply_av),
+ "Missing supply 'av' or 'ldoin'\n");
}
} else {
if (PTR_ERR(aic32x4->supply_dv) == -EPROBE_DEFER)
@@ -1383,10 +1383,8 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap,
}
ret = aic32x4_setup_regulators(dev, aic32x4);
- if (ret) {
- dev_err(dev, "Failed to setup regulators\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to setup regulators\n");
if (aic32x4->rstn_gpio) {
ndelay(10);
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 423b926..c495be1 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -14,7 +14,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -24,7 +24,6 @@
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/tlv320dac33-plat.h>
#include "tlv320dac33.h"
/*
@@ -80,7 +79,7 @@ struct tlv320dac33_priv {
struct snd_soc_component *component;
struct regulator_bulk_data supplies[DAC33_NUM_SUPPLIES];
struct snd_pcm_substream *substream;
- int power_gpio;
+ struct gpio_desc *reset_gpiod;
int chip_power;
int irq;
unsigned int refclk;
@@ -383,14 +382,26 @@ static int dac33_hard_power(struct snd_soc_component *component, int power)
goto exit;
}
- if (dac33->power_gpio >= 0)
- gpio_set_value(dac33->power_gpio, 1);
+ if (dac33->reset_gpiod) {
+ ret = gpiod_set_value(dac33->reset_gpiod, 1);
+ if (ret < 0) {
+ dev_err(&dac33->i2c->dev,
+ "Failed to set reset GPIO: %d\n", ret);
+ goto exit;
+ }
+ }
dac33->chip_power = 1;
} else {
dac33_soft_power(component, 0);
- if (dac33->power_gpio >= 0)
- gpio_set_value(dac33->power_gpio, 0);
+ if (dac33->reset_gpiod) {
+ ret = gpiod_set_value(dac33->reset_gpiod, 0);
+ if (ret < 0) {
+ dev_err(&dac33->i2c->dev,
+ "Failed to set reset GPIO: %d\n", ret);
+ goto exit;
+ }
+ }
ret = regulator_bulk_disable(ARRAY_SIZE(dac33->supplies),
dac33->supplies);
@@ -1462,16 +1473,9 @@ static struct snd_soc_dai_driver dac33_dai = {
static int dac33_i2c_probe(struct i2c_client *client)
{
- struct tlv320dac33_platform_data *pdata;
struct tlv320dac33_priv *dac33;
int ret, i;
- if (client->dev.platform_data == NULL) {
- dev_err(&client->dev, "Platform data not set\n");
- return -ENODEV;
- }
- pdata = client->dev.platform_data;
-
dac33 = devm_kzalloc(&client->dev, sizeof(struct tlv320dac33_priv),
GFP_KERNEL);
if (dac33 == NULL)
@@ -1488,26 +1492,22 @@ static int dac33_i2c_probe(struct i2c_client *client)
i2c_set_clientdata(client, dac33);
- dac33->power_gpio = pdata->power_gpio;
- dac33->burst_bclkdiv = pdata->burst_bclkdiv;
- dac33->keep_bclk = pdata->keep_bclk;
- dac33->mode1_latency = pdata->mode1_latency;
+ if (!dac33->burst_bclkdiv)
+ dac33->burst_bclkdiv = 8;
if (!dac33->mode1_latency)
dac33->mode1_latency = 10000; /* 10ms */
dac33->irq = client->irq;
/* Disable FIFO use by default */
dac33->fifo_mode = DAC33_FIFO_BYPASS;
- /* Check if the reset GPIO number is valid and request it */
- if (dac33->power_gpio >= 0) {
- ret = gpio_request(dac33->power_gpio, "tlv320dac33 reset");
- if (ret < 0) {
- dev_err(&client->dev,
- "Failed to request reset GPIO (%d)\n",
- dac33->power_gpio);
- goto err_gpio;
- }
- gpio_direction_output(dac33->power_gpio, 0);
+ /* request optional reset GPIO */
+ dac33->reset_gpiod =
+ devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(dac33->reset_gpiod)) {
+ ret = PTR_ERR(dac33->reset_gpiod);
+ dev_err_probe(&client->dev, ret,
+ "Failed to get reset GPIO\n");
+ goto err;
}
for (i = 0; i < ARRAY_SIZE(dac33->supplies); i++)
@@ -1518,19 +1518,17 @@ static int dac33_i2c_probe(struct i2c_client *client)
if (ret != 0) {
dev_err(&client->dev, "Failed to request supplies: %d\n", ret);
- goto err_get;
+ goto err;
}
ret = devm_snd_soc_register_component(&client->dev,
&soc_component_dev_tlv320dac33, &dac33_dai, 1);
if (ret < 0)
- goto err_get;
+ goto err;
return ret;
-err_get:
- if (dac33->power_gpio >= 0)
- gpio_free(dac33->power_gpio);
-err_gpio:
+
+err:
return ret;
}
@@ -1540,9 +1538,6 @@ static void dac33_i2c_remove(struct i2c_client *client)
if (unlikely(dac33->chip_power))
dac33_hard_power(dac33->component, 0);
-
- if (dac33->power_gpio >= 0)
- gpio_free(dac33->power_gpio);
}
static const struct i2c_device_id tlv320dac33_i2c_id[] = {
diff --git a/sound/soc/codecs/wcd-common.c b/sound/soc/codecs/wcd-common.c
new file mode 100644
index 0000000..9bbfda8
--- /dev/null
+++ b/sound/soc/codecs/wcd-common.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2025, Qualcomm Technologies, Inc. and/or its subsidiaries.
+
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/printk.h>
+#include <linux/component.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/regmap.h>
+
+#include "wcd-common.h"
+
+#define WCD_MIN_MICBIAS_MV 1000
+#define WCD_DEF_MICBIAS_MV 1800
+#define WCD_MAX_MICBIAS_MV 2850
+
+#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m))
+
+int wcd_get_micb_vout_ctl_val(struct device *dev, u32 micb_mv)
+{
+ /* min micbias voltage is 1V and maximum is 2.85V */
+ if (micb_mv < WCD_MIN_MICBIAS_MV || micb_mv > WCD_MAX_MICBIAS_MV) {
+ dev_err(dev, "Unsupported micbias voltage (%u mV)\n", micb_mv);
+ return -EINVAL;
+ }
+
+ return (micb_mv - WCD_MIN_MICBIAS_MV) / 50;
+}
+EXPORT_SYMBOL_GPL(wcd_get_micb_vout_ctl_val);
+
+static int wcd_get_micbias_val(struct device *dev, int micb_num, u32 *micb_mv)
+{
+ char micbias[64];
+ int mv;
+
+ sprintf(micbias, "qcom,micbias%d-microvolt", micb_num);
+
+ if (of_property_read_u32(dev->of_node, micbias, &mv)) {
+ dev_err(dev, "%s value not found, using default\n", micbias);
+ mv = WCD_DEF_MICBIAS_MV;
+ } else {
+ /* convert it to milli volts */
+ mv = mv/1000;
+ }
+ if (micb_mv)
+ *micb_mv = mv;
+
+ mv = wcd_get_micb_vout_ctl_val(dev, mv);
+ if (mv < 0) {
+ dev_err(dev, "Unsupported %s voltage (%d mV), falling back to default (%d mV)\n",
+ micbias, mv, WCD_DEF_MICBIAS_MV);
+ return wcd_get_micb_vout_ctl_val(dev, WCD_DEF_MICBIAS_MV);
+ }
+
+ return mv;
+}
+
+int wcd_dt_parse_micbias_info(struct wcd_common *common)
+{
+ int i;
+
+ for (i = 0; i < common->max_bias; i++) {
+ common->micb_vout[i] = wcd_get_micbias_val(common->dev, i + 1, &common->micb_mv[i]);
+ if (common->micb_vout[i] < 0)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_dt_parse_micbias_info);
+
+static int wcd_sdw_component_bind(struct device *dev, struct device *master, void *data)
+{
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static void wcd_sdw_component_unbind(struct device *dev, struct device *master, void *data)
+{
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+}
+
+const struct component_ops wcd_sdw_component_ops = {
+ .bind = wcd_sdw_component_bind,
+ .unbind = wcd_sdw_component_unbind,
+};
+EXPORT_SYMBOL_GPL(wcd_sdw_component_ops);
+
+int wcd_update_status(struct sdw_slave *slave, enum sdw_slave_status status)
+{
+ struct regmap *regmap = dev_get_regmap(&slave->dev, NULL);
+
+ if (regmap && status == SDW_SLAVE_ATTACHED) {
+ /* Write out any cached changes that happened between probe and attach */
+ regcache_cache_only(regmap, false);
+ return regcache_sync(regmap);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_update_status);
+
+int wcd_bus_config(struct sdw_slave *slave, struct sdw_bus_params *params)
+{
+ sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank), 0x01);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_bus_config);
+
+int wcd_interrupt_callback(struct sdw_slave *slave, struct irq_domain *slave_irq,
+ unsigned int wcd_intr_status0, unsigned int wcd_intr_status1,
+ unsigned int wcd_intr_status2)
+{
+ struct regmap *regmap = dev_get_regmap(&slave->dev, NULL);
+ u32 sts1, sts2, sts3;
+
+ do {
+ handle_nested_irq(irq_find_mapping(slave_irq, 0));
+ regmap_read(regmap, wcd_intr_status0, &sts1);
+ regmap_read(regmap, wcd_intr_status1, &sts2);
+ regmap_read(regmap, wcd_intr_status2, &sts3);
+
+ } while (sts1 || sts2 || sts3);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wcd_interrupt_callback);
+
+MODULE_DESCRIPTION("Common Qualcomm WCD Codec helpers driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd-common.h b/sound/soc/codecs/wcd-common.h
new file mode 100644
index 0000000..d5c156e6
--- /dev/null
+++ b/sound/soc/codecs/wcd-common.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025, Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __WCD_COMMON_H__
+#define __WCD_COMMON_H__
+
+struct device;
+struct sdw_slave;
+struct sdw_bus_params;
+struct irq_domain;
+enum sdw_slave_status;
+
+#define WCD_MAX_MICBIAS 4
+
+struct wcd_sdw_ch_info {
+ int port_num;
+ unsigned int ch_mask;
+ unsigned int master_ch_mask;
+};
+
+#define WCD_SDW_CH(id, pn, cmask) \
+ [id] = { \
+ .port_num = pn, \
+ .ch_mask = cmask, \
+ .master_ch_mask = cmask, \
+ }
+
+struct wcd_common {
+ struct device *dev;
+ int max_bias;
+ u32 micb_mv[WCD_MAX_MICBIAS];
+ u32 micb_vout[WCD_MAX_MICBIAS];
+};
+
+extern const struct component_ops wcd_sdw_component_ops;
+int wcd_get_micb_vout_ctl_val(struct device *dev, u32 micb_mv);
+int wcd_dt_parse_micbias_info(struct wcd_common *common);
+int wcd_update_status(struct sdw_slave *slave, enum sdw_slave_status status);
+int wcd_bus_config(struct sdw_slave *slave, struct sdw_bus_params *params);
+int wcd_interrupt_callback(struct sdw_slave *slave, struct irq_domain *slave_irq,
+ unsigned int wcd_intr_status0, unsigned int wcd_intr_status1,
+ unsigned int wcd_intr_status2);
+
+#endif /* __WCD_COMMON_H__ */
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index 1bb7e1d..d10b457 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -21,6 +21,7 @@
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include "wcd-clsh-v2.h"
+#include "wcd-common.h"
#include "wcd-mbhc-v2.h"
#include <dt-bindings/sound/qcom,wcd934x.h>
@@ -116,9 +117,6 @@
#define WCD934X_DEC_PWR_LVL_DF 0x00
#define WCD934X_DEC_PWR_LVL_HYBRID WCD934X_DEC_PWR_LVL_DF
-#define WCD934X_DEF_MICBIAS_MV 1800
-#define WCD934X_MAX_MICBIAS_MV 2850
-
#define WCD_IIR_FILTER_SIZE (sizeof(u32) * BAND_MAX)
#define WCD_IIR_FILTER_CTL(xname, iidx, bidx) \
@@ -530,6 +528,7 @@ struct wcd934x_codec {
struct slim_device *sdev;
struct slim_device *sidev;
struct wcd_clsh_ctrl *clsh_ctrl;
+ struct wcd_common common;
struct snd_soc_component *component;
struct wcd934x_slim_ch rx_chs[WCD934X_RX_MAX];
struct wcd934x_slim_ch tx_chs[WCD934X_TX_MAX];
@@ -555,7 +554,6 @@ struct wcd934x_codec {
struct mutex micb_lock;
u32 micb_ref[WCD934X_MAX_MICBIAS];
u32 pullup_ref[WCD934X_MAX_MICBIAS];
- u32 micb2_mv;
};
#define to_wcd934x_codec(_hw) container_of(_hw, struct wcd934x_codec, hw)
@@ -2168,55 +2166,24 @@ static struct clk *wcd934x_register_mclk_output(struct wcd934x_codec *wcd)
return NULL;
}
-static int wcd934x_get_micbias_val(struct device *dev, const char *micbias,
- u32 *micb_mv)
-{
- int mv;
-
- if (of_property_read_u32(dev->parent->of_node, micbias, &mv)) {
- dev_err(dev, "%s value not found, using default\n", micbias);
- mv = WCD934X_DEF_MICBIAS_MV;
- } else {
- /* convert it to milli volts */
- mv = mv/1000;
- }
-
- if (mv < 1000 || mv > 2850) {
- dev_err(dev, "%s value not in valid range, using default\n",
- micbias);
- mv = WCD934X_DEF_MICBIAS_MV;
- }
-
- if (micb_mv)
- *micb_mv = mv;
-
- return (mv - 1000) / 50;
-}
-
static int wcd934x_init_dmic(struct snd_soc_component *comp)
{
- int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4;
struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
u32 def_dmic_rate, dmic_clk_drv;
+ int ret;
- vout_ctl_1 = wcd934x_get_micbias_val(comp->dev,
- "qcom,micbias1-microvolt", NULL);
- vout_ctl_2 = wcd934x_get_micbias_val(comp->dev,
- "qcom,micbias2-microvolt",
- &wcd->micb2_mv);
- vout_ctl_3 = wcd934x_get_micbias_val(comp->dev,
- "qcom,micbias3-microvolt", NULL);
- vout_ctl_4 = wcd934x_get_micbias_val(comp->dev,
- "qcom,micbias4-microvolt", NULL);
+ ret = wcd_dt_parse_mbhc_data(comp->dev, &wcd->mbhc_cfg);
+ if (ret)
+ return ret;
snd_soc_component_update_bits(comp, WCD934X_ANA_MICB1,
- WCD934X_MICB_VAL_MASK, vout_ctl_1);
+ WCD934X_MICB_VAL_MASK, wcd->common.micb_vout[0]);
snd_soc_component_update_bits(comp, WCD934X_ANA_MICB2,
- WCD934X_MICB_VAL_MASK, vout_ctl_2);
+ WCD934X_MICB_VAL_MASK, wcd->common.micb_vout[1]);
snd_soc_component_update_bits(comp, WCD934X_ANA_MICB3,
- WCD934X_MICB_VAL_MASK, vout_ctl_3);
+ WCD934X_MICB_VAL_MASK, wcd->common.micb_vout[2]);
snd_soc_component_update_bits(comp, WCD934X_ANA_MICB4,
- WCD934X_MICB_VAL_MASK, vout_ctl_4);
+ WCD934X_MICB_VAL_MASK, wcd->common.micb_vout[3]);
if (wcd->rate == WCD934X_MCLK_CLK_9P6MHZ)
def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
@@ -2517,15 +2484,6 @@ static void wcd934x_mbhc_micb_ramp_control(struct snd_soc_component *component,
}
}
-static int wcd934x_get_micb_vout_ctl_val(u32 micb_mv)
-{
- /* min micbias voltage is 1V and maximum is 2.85V */
- if (micb_mv < 1000 || micb_mv > 2850)
- return -EINVAL;
-
- return (micb_mv - 1000) / 50;
-}
-
static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
int req_volt, int micb_num)
{
@@ -2562,7 +2520,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
cur_vout_ctl = snd_soc_component_read_field(component, micb_reg,
WCD934X_MICB_VAL_MASK);
- req_vout_ctl = wcd934x_get_micb_vout_ctl_val(req_volt);
+ req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt);
if (req_vout_ctl < 0) {
ret = -EINVAL;
goto exit;
@@ -2610,10 +2568,10 @@ static int wcd934x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon
* voltage needed to detect threshold microphone, then do
* not change the micbias, just return.
*/
- if (wcd934x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV)
+ if (wcd934x->common.micb_mv[1] >= WCD_MBHC_THR_HS_MICB_MV)
return 0;
- micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd934x->micb2_mv;
+ micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd934x->common.micb_mv[1];
rc = wcd934x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
@@ -3036,7 +2994,7 @@ static void wcd934x_mbhc_deinit(struct snd_soc_component *component)
static int wcd934x_comp_probe(struct snd_soc_component *component)
{
struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
- int i;
+ int i, ret;
snd_soc_component_init_regmap(component, wcd->regmap);
wcd->component = component;
@@ -3054,7 +3012,12 @@ static int wcd934x_comp_probe(struct snd_soc_component *component)
for (i = 0; i < NUM_CODEC_DAIS; i++)
INIT_LIST_HEAD(&wcd->dai[i].slim_ch_list);
- wcd934x_init_dmic(component);
+
+ ret = wcd934x_init_dmic(component);
+ if (ret) {
+ dev_err(component->dev, "Failed to Initialize micbias\n");
+ return ret;
+ }
if (wcd934x_mbhc_init(component))
dev_err(component->dev, "Failed to Initialize MBHC\n");
@@ -5860,14 +5823,13 @@ static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd)
cfg->anc_micbias = MIC_BIAS_2;
cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
cfg->num_btn = WCD934X_MBHC_MAX_BUTTONS;
- cfg->micb_mv = wcd->micb2_mv;
+ cfg->micb_mv = wcd->common.micb_mv[1];
cfg->linein_th = 5000;
cfg->hs_thr = 1700;
cfg->hph_thr = 50;
wcd_dt_parse_mbhc_data(dev, cfg);
-
return 0;
}
@@ -5888,6 +5850,8 @@ static int wcd934x_codec_probe(struct platform_device *pdev)
wcd->sdev = to_slim_device(data->dev);
mutex_init(&wcd->sysclk_mutex);
mutex_init(&wcd->micb_lock);
+ wcd->common.dev = dev->parent;
+ wcd->common.max_bias = 4;
ret = wcd934x_codec_parse_data(wcd);
if (ret)
diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c
index 1bfe738..1878d67 100644
--- a/sound/soc/codecs/wcd937x-sdw.c
+++ b/sound/soc/codecs/wcd937x-sdw.c
@@ -19,7 +19,7 @@
#include <sound/soc.h>
#include "wcd937x.h"
-static struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = {
+static struct wcd_sdw_ch_info wcd937x_sdw_rx_ch_info[] = {
WCD_SDW_CH(WCD937X_HPH_L, WCD937X_HPH_PORT, BIT(0)),
WCD_SDW_CH(WCD937X_HPH_R, WCD937X_HPH_PORT, BIT(1)),
WCD_SDW_CH(WCD937X_CLSH, WCD937X_CLSH_PORT, BIT(0)),
@@ -30,7 +30,7 @@ static struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = {
WCD_SDW_CH(WCD937X_DSD_R, WCD937X_DSD_PORT, BIT(1)),
};
-static struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = {
+static struct wcd_sdw_ch_info wcd937x_sdw_tx_ch_info[] = {
WCD_SDW_CH(WCD937X_ADC1, WCD937X_ADC_1_PORT, BIT(0)),
WCD_SDW_CH(WCD937X_ADC2, WCD937X_ADC_2_3_PORT, BIT(0)),
WCD_SDW_CH(WCD937X_ADC3, WCD937X_ADC_2_3_PORT, BIT(0)),
@@ -78,12 +78,6 @@ static struct sdw_dpn_prop wcd937x_dpn_prop[WCD937X_MAX_SWR_PORTS] = {
}
};
-struct device *wcd937x_sdw_device_get(struct device_node *np)
-{
- return bus_find_device_by_of_node(&sdw_bus_type, np);
-}
-EXPORT_SYMBOL_GPL(wcd937x_sdw_device_get);
-
int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
@@ -118,19 +112,6 @@ int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd,
}
EXPORT_SYMBOL_GPL(wcd937x_sdw_hw_params);
-static int wcd9370_update_status(struct sdw_slave *slave, enum sdw_slave_status status)
-{
- struct wcd937x_sdw_priv *wcd = dev_get_drvdata(&slave->dev);
-
- if (wcd->regmap && status == SDW_SLAVE_ATTACHED) {
- /* Write out any cached changes that happened between probe and attach */
- regcache_cache_only(wcd->regmap, false);
- return regcache_sync(wcd->regmap);
- }
-
- return 0;
-}
-
/*
* Handle Soundwire out-of-band interrupt event by triggering
* the first irq of the slave_irq irq domain, which then will
@@ -141,18 +122,9 @@ static int wcd9370_interrupt_callback(struct sdw_slave *slave,
struct sdw_slave_intr_status *status)
{
struct wcd937x_sdw_priv *wcd = dev_get_drvdata(&slave->dev);
- struct irq_domain *slave_irq = wcd->slave_irq;
- u32 sts1, sts2, sts3;
- do {
- handle_nested_irq(irq_find_mapping(slave_irq, 0));
- regmap_read(wcd->regmap, WCD937X_DIGITAL_INTR_STATUS_0, &sts1);
- regmap_read(wcd->regmap, WCD937X_DIGITAL_INTR_STATUS_1, &sts2);
- regmap_read(wcd->regmap, WCD937X_DIGITAL_INTR_STATUS_2, &sts3);
-
- } while (sts1 || sts2 || sts3);
-
- return IRQ_HANDLED;
+ return wcd_interrupt_callback(slave, wcd->slave_irq, WCD937X_DIGITAL_INTR_STATUS_0,
+ WCD937X_DIGITAL_INTR_STATUS_1, WCD937X_DIGITAL_INTR_STATUS_2);
}
static const struct reg_default wcd937x_defaults[] = {
@@ -985,35 +957,10 @@ static const struct regmap_config wcd937x_regmap_config = {
};
static const struct sdw_slave_ops wcd9370_slave_ops = {
- .update_status = wcd9370_update_status,
+ .update_status = wcd_update_status,
.interrupt_callback = wcd9370_interrupt_callback,
};
-static int wcd937x_sdw_component_bind(struct device *dev,
- struct device *master, void *data)
-{
- pm_runtime_set_autosuspend_delay(dev, 3000);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_mark_last_busy(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
-
- return 0;
-}
-
-static void wcd937x_sdw_component_unbind(struct device *dev,
- struct device *master, void *data)
-{
- pm_runtime_disable(dev);
- pm_runtime_set_suspended(dev);
- pm_runtime_dont_use_autosuspend(dev);
-}
-
-static const struct component_ops wcd937x_sdw_component_ops = {
- .bind = wcd937x_sdw_component_bind,
- .unbind = wcd937x_sdw_component_unbind,
-};
-
static int wcd9370_probe(struct sdw_slave *pdev,
const struct sdw_device_id *id)
{
@@ -1099,7 +1046,7 @@ static int wcd9370_probe(struct sdw_slave *pdev,
}
- ret = component_add(dev, &wcd937x_sdw_component_ops);
+ ret = component_add(dev, &wcd_sdw_component_ops);
if (ret)
return ret;
@@ -1113,7 +1060,7 @@ static int wcd9370_remove(struct sdw_slave *pdev)
{
struct device *dev = &pdev->dev;
- component_del(dev, &wcd937x_sdw_component_ops);
+ component_del(dev, &wcd_sdw_component_ops);
return 0;
}
diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c
index 3b0a8cc..421ec7a 100644
--- a/sound/soc/codecs/wcd937x.c
+++ b/sound/soc/codecs/wcd937x.c
@@ -21,6 +21,7 @@
#include <sound/tlv.h>
#include "wcd-clsh-v2.h"
+#include "wcd-common.h"
#include "wcd-mbhc-v2.h"
#include "wcd937x.h"
@@ -85,6 +86,7 @@ struct wcd937x_priv {
struct wcd_mbhc_config mbhc_cfg;
struct wcd_mbhc_intr intr_ids;
struct wcd_clsh_ctrl *clsh_info;
+ struct wcd_common common;
struct irq_domain *virq;
struct regmap_irq_chip_data *irq_chip;
struct snd_soc_jack *jack;
@@ -93,9 +95,6 @@ struct wcd937x_priv {
s32 pullup_ref[WCD937X_MAX_MICBIAS];
u32 hph_mode;
int ear_rx_path;
- u32 micb1_mv;
- u32 micb2_mv;
- u32 micb3_mv;
int hphr_pdm_wd_int;
int hphl_pdm_wd_int;
int aux_pdm_wd_int;
@@ -872,15 +871,6 @@ static int wcd937x_enable_rx3(struct snd_soc_dapm_widget *w,
return 0;
}
-static int wcd937x_get_micb_vout_ctl_val(u32 micb_mv)
-{
- if (micb_mv < 1000 || micb_mv > 2850) {
- pr_err("Unsupported micbias voltage (%u mV)\n", micb_mv);
- return -EINVAL;
- }
-
- return (micb_mv - 1000) / 50;
-}
static int wcd937x_tx_swr_ctrl(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
@@ -1193,7 +1183,7 @@ static int wcd937x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
static int wcd937x_connect_port(struct wcd937x_sdw_priv *wcd, u8 port_idx, u8 ch_id, bool enable)
{
struct sdw_port_config *port_config = &wcd->port_config[port_idx - 1];
- const struct wcd937x_sdw_ch_info *ch_info = &wcd->ch_info[ch_id];
+ const struct wcd_sdw_ch_info *ch_info = &wcd->ch_info[ch_id];
u8 port_num = ch_info->port_num;
u8 ch_mask = ch_info->ch_mask;
u8 mstr_port_num, mstr_ch_mask;
@@ -1481,7 +1471,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
cur_vout_ctl = snd_soc_component_read_field(component, micb_reg,
WCD937X_MICB_VOUT_MASK);
- req_vout_ctl = wcd937x_get_micb_vout_ctl_val(req_volt);
+ req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt);
if (req_vout_ctl < 0) {
ret = -EINVAL;
goto exit;
@@ -1529,10 +1519,10 @@ static int wcd937x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon
* voltage needed to detect threshold microphone, then do
* not change the micbias, just return.
*/
- if (wcd937x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV)
+ if (wcd937x->common.micb_mv[2] >= WCD_MBHC_THR_HS_MICB_MV)
return 0;
- micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd937x->micb2_mv;
+ micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd937x->common.micb_mv[2];
return wcd937x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
}
@@ -2046,9 +2036,9 @@ static const struct snd_kcontrol_new wcd937x_snd_controls[] = {
SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
wcd937x_rx_hph_mode_get, wcd937x_rx_hph_mode_put),
- SOC_SINGLE_EXT("HPHL_COMP Switch", SND_SOC_NOPM, 0, 1, 0,
+ SOC_SINGLE_EXT("HPHL_COMP Switch", WCD937X_COMP_L, 0, 1, 0,
wcd937x_get_compander, wcd937x_set_compander),
- SOC_SINGLE_EXT("HPHR_COMP Switch", SND_SOC_NOPM, 1, 1, 0,
+ SOC_SINGLE_EXT("HPHR_COMP Switch", WCD937X_COMP_R, 1, 1, 0,
wcd937x_get_compander, wcd937x_set_compander),
SOC_SINGLE_TLV("HPHL Volume", WCD937X_HPH_L_EN, 0, 20, 1, line_gain),
@@ -2436,22 +2426,14 @@ static const struct snd_soc_dapm_route wcd9375_audio_map[] = {
{ "DMIC6_MIXER", "Switch", "DMIC6" },
};
-static int wcd937x_set_micbias_data(struct wcd937x_priv *wcd937x)
+static void wcd937x_set_micbias_data(struct device *dev, struct wcd937x_priv *wcd937x)
{
- int vout_ctl[3];
-
- /* Set micbias voltage */
- vout_ctl[0] = wcd937x_get_micb_vout_ctl_val(wcd937x->micb1_mv);
- vout_ctl[1] = wcd937x_get_micb_vout_ctl_val(wcd937x->micb2_mv);
- vout_ctl[2] = wcd937x_get_micb_vout_ctl_val(wcd937x->micb3_mv);
- if ((vout_ctl[0] | vout_ctl[1] | vout_ctl[2]) < 0)
- return -EINVAL;
-
- regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB1, WCD937X_ANA_MICB_VOUT, vout_ctl[0]);
- regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB2, WCD937X_ANA_MICB_VOUT, vout_ctl[1]);
- regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB3, WCD937X_ANA_MICB_VOUT, vout_ctl[2]);
-
- return 0;
+ regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB1, WCD937X_ANA_MICB_VOUT,
+ wcd937x->common.micb_vout[0]);
+ regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB2, WCD937X_ANA_MICB_VOUT,
+ wcd937x->common.micb_vout[1]);
+ regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB3, WCD937X_ANA_MICB_VOUT,
+ wcd937x->common.micb_vout[2]);
}
static irqreturn_t wcd937x_wd_handle_irq(int irq, void *data)
@@ -2630,31 +2612,6 @@ static const struct snd_soc_component_driver soc_codec_dev_wcd937x = {
.endianness = 1,
};
-static void wcd937x_dt_parse_micbias_info(struct device *dev, struct wcd937x_priv *wcd)
-{
- struct device_node *np = dev->of_node;
- u32 prop_val = 0;
- int ret = 0;
-
- ret = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val);
- if (!ret)
- wcd->micb1_mv = prop_val / 1000;
- else
- dev_warn(dev, "Micbias1 DT property not found\n");
-
- ret = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val);
- if (!ret)
- wcd->micb2_mv = prop_val / 1000;
- else
- dev_warn(dev, "Micbias2 DT property not found\n");
-
- ret = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val);
- if (!ret)
- wcd->micb3_mv = prop_val / 1000;
- else
- dev_warn(dev, "Micbias3 DT property not found\n");
-}
-
static bool wcd937x_swap_gnd_mic(struct snd_soc_component *component)
{
int value;
@@ -2788,7 +2745,7 @@ static int wcd937x_bind(struct device *dev)
return ret;
}
- wcd937x->rxdev = wcd937x_sdw_device_get(wcd937x->rxnode);
+ wcd937x->rxdev = of_sdw_find_device_by_node(wcd937x->rxnode);
if (!wcd937x->rxdev) {
dev_err(dev, "could not find slave with matching of node\n");
return -EINVAL;
@@ -2797,7 +2754,7 @@ static int wcd937x_bind(struct device *dev)
wcd937x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd937x->rxdev);
wcd937x->sdw_priv[AIF1_PB]->wcd937x = wcd937x;
- wcd937x->txdev = wcd937x_sdw_device_get(wcd937x->txnode);
+ wcd937x->txdev = of_sdw_find_device_by_node(wcd937x->txnode);
if (!wcd937x->txdev) {
dev_err(dev, "could not find txslave with matching of node\n");
return -EINVAL;
@@ -2833,7 +2790,7 @@ static int wcd937x_bind(struct device *dev)
return -EINVAL;
}
- wcd937x->regmap = dev_get_regmap(&wcd937x->tx_sdw_dev->dev, NULL);
+ wcd937x->regmap = wcd937x->sdw_priv[AIF1_CAP]->regmap;
if (!wcd937x->regmap) {
dev_err(dev, "could not get TX device regmap\n");
return -EINVAL;
@@ -2848,11 +2805,7 @@ static int wcd937x_bind(struct device *dev)
wcd937x->sdw_priv[AIF1_PB]->slave_irq = wcd937x->virq;
wcd937x->sdw_priv[AIF1_CAP]->slave_irq = wcd937x->virq;
- ret = wcd937x_set_micbias_data(wcd937x);
- if (ret < 0) {
- dev_err(dev, "Bad micbias pdata\n");
- return ret;
- }
+ wcd937x_set_micbias_data(dev, wcd937x);
ret = snd_soc_register_component(dev, &soc_codec_dev_wcd937x,
wcd937x_dais, ARRAY_SIZE(wcd937x_dais));
@@ -2920,6 +2873,8 @@ static int wcd937x_probe(struct platform_device *pdev)
dev_set_drvdata(dev, wcd937x);
mutex_init(&wcd937x->micb_lock);
+ wcd937x->common.dev = dev;
+ wcd937x->common.max_bias = 3;
wcd937x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(wcd937x->reset_gpio))
@@ -2939,13 +2894,15 @@ static int wcd937x_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
- wcd937x_dt_parse_micbias_info(dev, wcd937x);
+ ret = wcd_dt_parse_micbias_info(&wcd937x->common);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get micbias\n");
cfg->mbhc_micbias = MIC_BIAS_2;
cfg->anc_micbias = MIC_BIAS_2;
cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
cfg->num_btn = WCD937X_MBHC_MAX_BUTTONS;
- cfg->micb_mv = wcd937x->micb2_mv;
+ cfg->micb_mv = wcd937x->common.micb_mv[2];
cfg->linein_th = 5000;
cfg->hs_thr = 1700;
cfg->hph_thr = 50;
diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h
index 3ab21bb..3d0ba3cc 100644
--- a/sound/soc/codecs/wcd937x.h
+++ b/sound/soc/codecs/wcd937x.h
@@ -7,6 +7,7 @@
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
+#include "wcd-common.h"
#define WCD937X_BASE_ADDRESS 0x3000
#define WCD937X_ANA_BIAS 0x3001
@@ -507,26 +508,13 @@ enum wcd937x_rx_sdw_ports {
WCD937X_MAX_SWR_PORTS = WCD937X_DSD_PORT,
};
-struct wcd937x_sdw_ch_info {
- int port_num;
- unsigned int ch_mask;
- unsigned int master_ch_mask;
-};
-
-#define WCD_SDW_CH(id, pn, cmask) \
- [id] = { \
- .port_num = pn, \
- .ch_mask = cmask, \
- .master_ch_mask = cmask, \
- }
-
struct wcd937x_priv;
struct wcd937x_sdw_priv {
struct sdw_slave *sdev;
struct sdw_stream_config sconfig;
struct sdw_stream_runtime *sruntime;
struct sdw_port_config port_config[WCD937X_MAX_SWR_PORTS];
- struct wcd937x_sdw_ch_info *ch_info;
+ struct wcd_sdw_ch_info *ch_info;
bool port_enable[WCD937X_MAX_SWR_CH_IDS];
unsigned int master_channel_map[SDW_MAX_PORTS];
int active_ports;
@@ -549,24 +537,22 @@ int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai);
-struct device *wcd937x_sdw_device_get(struct device_node *np);
-
#else
-int wcd937x_sdw_free(struct wcd937x_sdw_priv *wcd,
+static inline int wcd937x_sdw_free(struct wcd937x_sdw_priv *wcd,
struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
return -EOPNOTSUPP;
}
-int wcd937x_sdw_set_sdw_stream(struct wcd937x_sdw_priv *wcd,
+static inline int wcd937x_sdw_set_sdw_stream(struct wcd937x_sdw_priv *wcd,
struct snd_soc_dai *dai,
void *stream, int direction)
{
return -EOPNOTSUPP;
}
-int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd,
+static inline int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c
index e822cc14..add907c 100644
--- a/sound/soc/codecs/wcd938x-sdw.c
+++ b/sound/soc/codecs/wcd938x-sdw.c
@@ -18,10 +18,9 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include "wcd938x.h"
+#include "wcd-common.h"
-#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m))
-
-static const struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = {
+static const struct wcd_sdw_ch_info wcd938x_sdw_rx_ch_info[] = {
WCD_SDW_CH(WCD938X_HPH_L, WCD938X_HPH_PORT, BIT(0)),
WCD_SDW_CH(WCD938X_HPH_R, WCD938X_HPH_PORT, BIT(1)),
WCD_SDW_CH(WCD938X_CLSH, WCD938X_CLSH_PORT, BIT(0)),
@@ -32,7 +31,7 @@ static const struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = {
WCD_SDW_CH(WCD938X_DSD_R, WCD938X_DSD_PORT, BIT(1)),
};
-static const struct wcd938x_sdw_ch_info wcd938x_sdw_tx_ch_info[] = {
+static const struct wcd_sdw_ch_info wcd938x_sdw_tx_ch_info[] = {
WCD_SDW_CH(WCD938X_ADC1, WCD938X_ADC_1_2_PORT, BIT(0)),
WCD_SDW_CH(WCD938X_ADC2, WCD938X_ADC_1_2_PORT, BIT(1)),
WCD_SDW_CH(WCD938X_ADC3, WCD938X_ADC_3_4_PORT, BIT(0)),
@@ -82,23 +81,6 @@ static struct sdw_dpn_prop wcd938x_dpn_prop[WCD938X_MAX_SWR_PORTS] = {
}
};
-struct device *wcd938x_sdw_device_get(struct device_node *np)
-{
- return bus_find_device_by_of_node(&sdw_bus_type, np);
-
-}
-EXPORT_SYMBOL_GPL(wcd938x_sdw_device_get);
-
-int wcd938x_swr_get_current_bank(struct sdw_slave *sdev)
-{
- int bank;
-
- bank = sdw_read(sdev, SDW_SCP_CTRL);
-
- return ((bank & 0x40) ? 1 : 0);
-}
-EXPORT_SYMBOL_GPL(wcd938x_swr_get_current_bank);
-
int wcd938x_sdw_hw_params(struct wcd938x_sdw_priv *wcd,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
@@ -158,44 +140,13 @@ int wcd938x_sdw_set_sdw_stream(struct wcd938x_sdw_priv *wcd,
}
EXPORT_SYMBOL_GPL(wcd938x_sdw_set_sdw_stream);
-static int wcd9380_update_status(struct sdw_slave *slave,
- enum sdw_slave_status status)
-{
- struct wcd938x_sdw_priv *wcd = dev_get_drvdata(&slave->dev);
-
- if (wcd->regmap && (status == SDW_SLAVE_ATTACHED)) {
- /* Write out any cached changes that happened between probe and attach */
- regcache_cache_only(wcd->regmap, false);
- return regcache_sync(wcd->regmap);
- }
-
- return 0;
-}
-
-static int wcd9380_bus_config(struct sdw_slave *slave,
- struct sdw_bus_params *params)
-{
- sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank), 0x01);
-
- return 0;
-}
-
static int wcd9380_interrupt_callback(struct sdw_slave *slave,
struct sdw_slave_intr_status *status)
{
struct wcd938x_sdw_priv *wcd = dev_get_drvdata(&slave->dev);
- struct irq_domain *slave_irq = wcd->slave_irq;
- u32 sts1, sts2, sts3;
- do {
- handle_nested_irq(irq_find_mapping(slave_irq, 0));
- regmap_read(wcd->regmap, WCD938X_DIGITAL_INTR_STATUS_0, &sts1);
- regmap_read(wcd->regmap, WCD938X_DIGITAL_INTR_STATUS_1, &sts2);
- regmap_read(wcd->regmap, WCD938X_DIGITAL_INTR_STATUS_2, &sts3);
-
- } while (sts1 || sts2 || sts3);
-
- return IRQ_HANDLED;
+ return wcd_interrupt_callback(slave, wcd->slave_irq, WCD938X_DIGITAL_INTR_STATUS_0,
+ WCD938X_DIGITAL_INTR_STATUS_1, WCD938X_DIGITAL_INTR_STATUS_2);
}
static const struct reg_default wcd938x_defaults[] = {
@@ -1193,25 +1144,9 @@ static const struct regmap_config wcd938x_regmap_config = {
};
static const struct sdw_slave_ops wcd9380_slave_ops = {
- .update_status = wcd9380_update_status,
+ .update_status = wcd_update_status,
.interrupt_callback = wcd9380_interrupt_callback,
- .bus_config = wcd9380_bus_config,
-};
-
-static int wcd938x_sdw_component_bind(struct device *dev,
- struct device *master, void *data)
-{
- return 0;
-}
-
-static void wcd938x_sdw_component_unbind(struct device *dev,
- struct device *master, void *data)
-{
-}
-
-static const struct component_ops wcd938x_sdw_component_ops = {
- .bind = wcd938x_sdw_component_bind,
- .unbind = wcd938x_sdw_component_unbind,
+ .bus_config = wcd_bus_config,
};
static int wcd9380_probe(struct sdw_slave *pdev,
@@ -1278,7 +1213,7 @@ static int wcd9380_probe(struct sdw_slave *pdev,
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
- ret = component_add(dev, &wcd938x_sdw_component_ops);
+ ret = component_add(dev, &wcd_sdw_component_ops);
if (ret)
goto err_disable_rpm;
@@ -1296,7 +1231,7 @@ static int wcd9380_remove(struct sdw_slave *pdev)
{
struct device *dev = &pdev->dev;
- component_del(dev, &wcd938x_sdw_component_ops);
+ component_del(dev, &wcd_sdw_component_ops);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c
index 711f373..e1a4783 100644
--- a/sound/soc/codecs/wcd938x.c
+++ b/sound/soc/codecs/wcd938x.c
@@ -22,6 +22,7 @@
#include <linux/regulator/consumer.h>
#include "wcd-clsh-v2.h"
+#include "wcd-common.h"
#include "wcd-mbhc-v2.h"
#include "wcd938x.h"
@@ -155,6 +156,7 @@ struct wcd938x_priv {
struct wcd_mbhc_config mbhc_cfg;
struct wcd_mbhc_intr intr_ids;
struct wcd_clsh_ctrl *clsh_info;
+ struct wcd_common common;
struct irq_domain *virq;
struct regmap_irq_chip_data *irq_chip;
struct snd_soc_jack *jack;
@@ -169,10 +171,6 @@ struct wcd938x_priv {
struct gpio_desc *us_euro_gpio;
struct mux_control *us_euro_mux;
unsigned int mux_state;
- u32 micb1_mv;
- u32 micb2_mv;
- u32 micb3_mv;
- u32 micb4_mv;
int hphr_pdm_wd_int;
int hphl_pdm_wd_int;
int aux_pdm_wd_int;
@@ -396,7 +394,7 @@ static int wcd938x_io_init(struct wcd938x_priv *wcd938x)
}
-static int wcd938x_sdw_connect_port(const struct wcd938x_sdw_ch_info *ch_info,
+static int wcd938x_sdw_connect_port(const struct wcd_sdw_ch_info *ch_info,
struct sdw_port_config *port_config,
u8 enable)
{
@@ -1094,8 +1092,7 @@ static int wcd938x_tx_swr_ctrl(struct snd_soc_dapm_widget *w,
int bank;
int rate;
- bank = (wcd938x_swr_get_current_bank(wcd938x->sdw_priv[AIF1_CAP]->sdev)) ? 0 : 1;
- bank = bank ? 0 : 1;
+ bank = sdw_slave_get_current_bank(wcd938x->sdw_priv[AIF1_CAP]->sdev);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -1975,15 +1972,6 @@ static void wcd938x_mbhc_micb_ramp_control(struct snd_soc_component *component,
}
}
-static int wcd938x_get_micb_vout_ctl_val(u32 micb_mv)
-{
- /* min micbias voltage is 1V and maximum is 2.85V */
- if (micb_mv < 1000 || micb_mv > 2850)
- return -EINVAL;
-
- return (micb_mv - 1000) / 50;
-}
-
static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
int req_volt, int micb_num)
{
@@ -2020,7 +2008,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
cur_vout_ctl = snd_soc_component_read_field(component, micb_reg,
WCD938X_MICB_VOUT_MASK);
- req_vout_ctl = wcd938x_get_micb_vout_ctl_val(req_volt);
+ req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt);
if (req_vout_ctl < 0) {
ret = -EINVAL;
goto exit;
@@ -2068,10 +2056,10 @@ static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon
* voltage needed to detect threshold microphone, then do
* not change the micbias, just return.
*/
- if (wcd938x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV)
+ if (wcd938x->common.micb_mv[2] >= WCD_MBHC_THR_HS_MICB_MV)
return 0;
- micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd938x->micb2_mv;
+ micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd938x->common.micb_mv[2];
return wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
}
@@ -2976,28 +2964,16 @@ static const struct snd_soc_dapm_route wcd938x_audio_map[] = {
{"EAR", NULL, "EAR PGA"},
};
-static int wcd938x_set_micbias_data(struct wcd938x_priv *wcd938x)
+static void wcd938x_set_micbias_data(struct device *dev, struct wcd938x_priv *wcd938x)
{
- int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4;
-
- /* set micbias voltage */
- vout_ctl_1 = wcd938x_get_micb_vout_ctl_val(wcd938x->micb1_mv);
- vout_ctl_2 = wcd938x_get_micb_vout_ctl_val(wcd938x->micb2_mv);
- vout_ctl_3 = wcd938x_get_micb_vout_ctl_val(wcd938x->micb3_mv);
- vout_ctl_4 = wcd938x_get_micb_vout_ctl_val(wcd938x->micb4_mv);
- if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || vout_ctl_3 < 0 || vout_ctl_4 < 0)
- return -EINVAL;
-
regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB1,
- WCD938X_MICB_VOUT_MASK, vout_ctl_1);
+ WCD938X_MICB_VOUT_MASK, wcd938x->common.micb_vout[0]);
regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB2,
- WCD938X_MICB_VOUT_MASK, vout_ctl_2);
+ WCD938X_MICB_VOUT_MASK, wcd938x->common.micb_vout[1]);
regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB3,
- WCD938X_MICB_VOUT_MASK, vout_ctl_3);
+ WCD938X_MICB_VOUT_MASK, wcd938x->common.micb_vout[2]);
regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB4,
- WCD938X_MICB_VOUT_MASK, vout_ctl_4);
-
- return 0;
+ WCD938X_MICB_VOUT_MASK, wcd938x->common.micb_vout[3]);
}
static irqreturn_t wcd938x_wd_handle_irq(int irq, void *data)
@@ -3201,37 +3177,6 @@ static const struct snd_soc_component_driver soc_codec_dev_wcd938x = {
.endianness = 1,
};
-static void wcd938x_dt_parse_micbias_info(struct device *dev, struct wcd938x_priv *wcd)
-{
- struct device_node *np = dev->of_node;
- u32 prop_val = 0;
- int rc = 0;
-
- rc = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val);
- if (!rc)
- wcd->micb1_mv = prop_val/1000;
- else
- dev_info(dev, "%s: Micbias1 DT property not found\n", __func__);
-
- rc = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val);
- if (!rc)
- wcd->micb2_mv = prop_val/1000;
- else
- dev_info(dev, "%s: Micbias2 DT property not found\n", __func__);
-
- rc = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val);
- if (!rc)
- wcd->micb3_mv = prop_val/1000;
- else
- dev_info(dev, "%s: Micbias3 DT property not found\n", __func__);
-
- rc = of_property_read_u32(np, "qcom,micbias4-microvolt", &prop_val);
- if (!rc)
- wcd->micb4_mv = prop_val/1000;
- else
- dev_info(dev, "%s: Micbias4 DT property not found\n", __func__);
-}
-
static bool wcd938x_swap_gnd_mic(struct snd_soc_component *component)
{
struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
@@ -3296,13 +3241,15 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device
if (ret)
return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
- wcd938x_dt_parse_micbias_info(dev, wcd938x);
+ ret = wcd_dt_parse_micbias_info(&wcd938x->common);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
cfg->mbhc_micbias = MIC_BIAS_2;
cfg->anc_micbias = MIC_BIAS_2;
cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
cfg->num_btn = WCD938X_MBHC_MAX_BUTTONS;
- cfg->micb_mv = wcd938x->micb2_mv;
+ cfg->micb_mv = wcd938x->common.micb_mv[2];
cfg->linein_th = 5000;
cfg->hs_thr = 1700;
cfg->hph_thr = 50;
@@ -3400,7 +3347,7 @@ static int wcd938x_bind(struct device *dev)
return ret;
}
- wcd938x->rxdev = wcd938x_sdw_device_get(wcd938x->rxnode);
+ wcd938x->rxdev = of_sdw_find_device_by_node(wcd938x->rxnode);
if (!wcd938x->rxdev) {
dev_err(dev, "could not find slave with matching of node\n");
ret = -EINVAL;
@@ -3409,7 +3356,7 @@ static int wcd938x_bind(struct device *dev)
wcd938x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd938x->rxdev);
wcd938x->sdw_priv[AIF1_PB]->wcd938x = wcd938x;
- wcd938x->txdev = wcd938x_sdw_device_get(wcd938x->txnode);
+ wcd938x->txdev = of_sdw_find_device_by_node(wcd938x->txnode);
if (!wcd938x->txdev) {
dev_err(dev, "could not find txslave with matching of node\n");
ret = -EINVAL;
@@ -3442,7 +3389,7 @@ static int wcd938x_bind(struct device *dev)
goto err_remove_tx_link;
}
- wcd938x->regmap = dev_get_regmap(&wcd938x->tx_sdw_dev->dev, NULL);
+ wcd938x->regmap = wcd938x->sdw_priv[AIF1_CAP]->regmap;
if (!wcd938x->regmap) {
dev_err(dev, "could not get TX device regmap\n");
ret = -EINVAL;
@@ -3458,11 +3405,7 @@ static int wcd938x_bind(struct device *dev)
wcd938x->sdw_priv[AIF1_PB]->slave_irq = wcd938x->virq;
wcd938x->sdw_priv[AIF1_CAP]->slave_irq = wcd938x->virq;
- ret = wcd938x_set_micbias_data(wcd938x);
- if (ret < 0) {
- dev_err(dev, "%s: bad micbias pdata\n", __func__);
- goto err_remove_rx_link;
- }
+ wcd938x_set_micbias_data(dev, wcd938x);
ret = snd_soc_register_component(dev, &soc_codec_dev_wcd938x,
wcd938x_dais, ARRAY_SIZE(wcd938x_dais));
@@ -3551,6 +3494,8 @@ static int wcd938x_probe(struct platform_device *pdev)
dev_set_drvdata(dev, wcd938x);
mutex_init(&wcd938x->micb_lock);
+ wcd938x->common.dev = dev;
+ wcd938x->common.max_bias = 4;
ret = wcd938x_populate_dt_data(wcd938x, dev);
if (ret)
diff --git a/sound/soc/codecs/wcd938x.h b/sound/soc/codecs/wcd938x.h
index fb6a0e4..c186104 100644
--- a/sound/soc/codecs/wcd938x.h
+++ b/sound/soc/codecs/wcd938x.h
@@ -587,17 +587,6 @@
#define WCD938X_MAX_SWR_CH_IDS 15
-struct wcd938x_sdw_ch_info {
- int port_num;
- unsigned int ch_mask;
-};
-
-#define WCD_SDW_CH(id, pn, cmask) \
- [id] = { \
- .port_num = pn, \
- .ch_mask = cmask, \
- }
-
enum wcd938x_tx_sdw_ports {
WCD938X_ADC_1_2_PORT = 1,
WCD938X_ADC_3_4_PORT,
@@ -649,7 +638,7 @@ struct wcd938x_sdw_priv {
struct sdw_stream_config sconfig;
struct sdw_stream_runtime *sruntime;
struct sdw_port_config port_config[WCD938X_MAX_SWR_PORTS];
- const struct wcd938x_sdw_ch_info *ch_info;
+ const struct wcd_sdw_ch_info *ch_info;
bool port_enable[WCD938X_MAX_SWR_CH_IDS];
int active_ports;
bool is_tx;
@@ -669,10 +658,6 @@ int wcd938x_sdw_hw_params(struct wcd938x_sdw_priv *wcd,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai);
-
-struct device *wcd938x_sdw_device_get(struct device_node *np);
-int wcd938x_swr_get_current_bank(struct sdw_slave *sdev);
-
#else
static inline int wcd938x_sdw_free(struct wcd938x_sdw_priv *wcd,
@@ -697,14 +682,5 @@ static inline int wcd938x_sdw_hw_params(struct wcd938x_sdw_priv *wcd,
return -EOPNOTSUPP;
}
-static inline struct device *wcd938x_sdw_device_get(struct device_node *np)
-{
- return NULL;
-}
-
-static inline int wcd938x_swr_get_current_bank(struct sdw_slave *sdev)
-{
- return 0;
-}
#endif /* CONFIG_SND_SOC_WCD938X_SDW */
#endif /* __WCD938X_H__ */
diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c
index f7a9323..d369100 100644
--- a/sound/soc/codecs/wcd939x-sdw.c
+++ b/sound/soc/codecs/wcd939x-sdw.c
@@ -20,10 +20,9 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include "wcd939x.h"
+#include "wcd-common.h"
-#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m))
-
-static const struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = {
+static const struct wcd_sdw_ch_info wcd939x_sdw_rx_ch_info[] = {
WCD_SDW_CH(WCD939X_HPH_L, WCD939X_HPH_PORT, BIT(0)),
WCD_SDW_CH(WCD939X_HPH_R, WCD939X_HPH_PORT, BIT(1)),
WCD_SDW_CH(WCD939X_CLSH, WCD939X_CLSH_PORT, BIT(0)),
@@ -36,7 +35,7 @@ static const struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = {
WCD_SDW_CH(WCD939X_HIFI_PCM_R, WCD939X_HIFI_PCM_PORT, BIT(1)),
};
-static const struct wcd939x_sdw_ch_info wcd939x_sdw_tx_ch_info[] = {
+static const struct wcd_sdw_ch_info wcd939x_sdw_tx_ch_info[] = {
WCD_SDW_CH(WCD939X_ADC1, WCD939X_ADC_1_4_PORT, BIT(0)),
WCD_SDW_CH(WCD939X_ADC2, WCD939X_ADC_1_4_PORT, BIT(1)),
WCD_SDW_CH(WCD939X_ADC3, WCD939X_ADC_1_4_PORT, BIT(2)),
@@ -128,19 +127,6 @@ static struct sdw_dpn_prop wcd939x_tx_dpn_prop[WCD939X_MAX_TX_SWR_PORTS] = {
}
};
-struct device *wcd939x_sdw_device_get(struct device_node *np)
-{
- return bus_find_device_by_of_node(&sdw_bus_type, np);
-}
-EXPORT_SYMBOL_GPL(wcd939x_sdw_device_get);
-
-unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev)
-{
- return FIELD_GET(SDW_SCP_STAT_CURR_BANK,
- sdw_read(sdev, SDW_SCP_CTRL));
-}
-EXPORT_SYMBOL_GPL(wcd939x_swr_get_current_bank);
-
int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
@@ -199,38 +185,6 @@ int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd,
}
EXPORT_SYMBOL_GPL(wcd939x_sdw_set_sdw_stream);
-struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd)
-{
- if (wcd->regmap)
- return wcd->regmap;
-
- return ERR_PTR(-EINVAL);
-}
-EXPORT_SYMBOL_GPL(wcd939x_swr_get_regmap);
-
-static int wcd9390_update_status(struct sdw_slave *slave,
- enum sdw_slave_status status)
-{
- struct wcd939x_sdw_priv *wcd = dev_get_drvdata(&slave->dev);
-
- if (wcd->regmap && status == SDW_SLAVE_ATTACHED) {
- /* Write out any cached changes that happened between probe and attach */
- regcache_cache_only(wcd->regmap, false);
- return regcache_sync(wcd->regmap);
- }
-
- return 0;
-}
-
-static int wcd9390_bus_config(struct sdw_slave *slave,
- struct sdw_bus_params *params)
-{
- sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank),
- 0x01);
-
- return 0;
-}
-
/*
* Handle Soundwire out-of-band interrupt event by triggering
* the first irq of the slave_irq irq domain, which then will
@@ -241,18 +195,9 @@ static int wcd9390_interrupt_callback(struct sdw_slave *slave,
struct sdw_slave_intr_status *status)
{
struct wcd939x_sdw_priv *wcd = dev_get_drvdata(&slave->dev);
- struct irq_domain *slave_irq = wcd->slave_irq;
- u32 sts1, sts2, sts3;
- do {
- handle_nested_irq(irq_find_mapping(slave_irq, 0));
- regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_0, &sts1);
- regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_1, &sts2);
- regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_2, &sts3);
-
- } while (sts1 || sts2 || sts3);
-
- return IRQ_HANDLED;
+ return wcd_interrupt_callback(slave, wcd->slave_irq, WCD939X_DIGITAL_INTR_STATUS_0,
+ WCD939X_DIGITAL_INTR_STATUS_1, WCD939X_DIGITAL_INTR_STATUS_2);
}
static const struct reg_default wcd939x_defaults[] = {
@@ -1385,34 +1330,9 @@ static const struct regmap_config wcd939x_regmap_config = {
};
static const struct sdw_slave_ops wcd9390_slave_ops = {
- .update_status = wcd9390_update_status,
+ .update_status = wcd_update_status,
.interrupt_callback = wcd9390_interrupt_callback,
- .bus_config = wcd9390_bus_config,
-};
-
-static int wcd939x_sdw_component_bind(struct device *dev, struct device *master,
- void *data)
-{
- pm_runtime_set_autosuspend_delay(dev, 3000);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_mark_last_busy(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
-
- return 0;
-}
-
-static void wcd939x_sdw_component_unbind(struct device *dev,
- struct device *master, void *data)
-{
- pm_runtime_disable(dev);
- pm_runtime_set_suspended(dev);
- pm_runtime_dont_use_autosuspend(dev);
-}
-
-static const struct component_ops wcd939x_sdw_component_ops = {
- .bind = wcd939x_sdw_component_bind,
- .unbind = wcd939x_sdw_component_unbind,
+ .bus_config = wcd_bus_config,
};
static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
@@ -1478,7 +1398,7 @@ static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
regcache_cache_only(wcd->regmap, true);
}
- ret = component_add(dev, &wcd939x_sdw_component_ops);
+ ret = component_add(dev, &wcd_sdw_component_ops);
if (ret)
return ret;
@@ -1493,7 +1413,7 @@ static int wcd9390_remove(struct sdw_slave *pdev)
struct device *dev = &pdev->dev;
struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev);
- component_del(dev, &wcd939x_sdw_component_ops);
+ component_del(dev, &wcd_sdw_component_ops);
if (wcd->regmap)
regmap_exit(wcd->regmap);
diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c
index 64f082e..e74e6f01 100644
--- a/sound/soc/codecs/wcd939x.c
+++ b/sound/soc/codecs/wcd939x.c
@@ -28,6 +28,7 @@
#include <linux/usb/typec_altmode.h>
#include "wcd-clsh-v2.h"
+#include "wcd-common.h"
#include "wcd-mbhc-v2.h"
#include "wcd939x.h"
@@ -191,6 +192,7 @@ struct wcd939x_priv {
struct wcd_mbhc_config mbhc_cfg;
struct wcd_mbhc_intr intr_ids;
struct wcd_clsh_ctrl *clsh_info;
+ struct wcd_common common;
struct irq_domain *virq;
struct regmap_irq_chip_data *irq_chip;
struct snd_soc_jack *jack;
@@ -201,10 +203,6 @@ struct wcd939x_priv {
u32 tx_mode[TX_ADC_MAX];
int variant;
struct gpio_desc *reset_gpio;
- u32 micb1_mv;
- u32 micb2_mv;
- u32 micb3_mv;
- u32 micb4_mv;
int hphr_pdm_wd_int;
int hphl_pdm_wd_int;
int ear_pdm_wd_int;
@@ -415,7 +413,7 @@ static int wcd939x_io_init(struct snd_soc_component *component)
return 0;
}
-static int wcd939x_sdw_connect_port(const struct wcd939x_sdw_ch_info *ch_info,
+static int wcd939x_sdw_connect_port(const struct wcd_sdw_ch_info *ch_info,
struct sdw_port_config *port_config,
u8 enable)
{
@@ -1017,7 +1015,7 @@ static int wcd939x_tx_swr_ctrl(struct snd_soc_dapm_widget *w,
int bank;
int rate;
- bank = wcd939x_swr_get_current_bank(wcd939x->sdw_priv[AIF1_CAP]->sdev);
+ bank = sdw_slave_get_current_bank(wcd939x->sdw_priv[AIF1_CAP]->sdev);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -1919,17 +1917,6 @@ static void wcd939x_mbhc_micb_ramp_control(struct snd_soc_component *component,
}
}
-static int wcd939x_get_micb_vout_ctl_val(u32 micb_mv)
-{
- /* min micbias voltage is 1V and maximum is 2.85V */
- if (micb_mv < 1000 || micb_mv > 2850) {
- pr_err("%s: unsupported micbias voltage\n", __func__);
- return -EINVAL;
- }
-
- return (micb_mv - 1000) / 50;
-}
-
static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
int req_volt, int micb_num)
{
@@ -1969,7 +1956,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
cur_vout_ctl = snd_soc_component_read_field(component, micb_reg,
WCD939X_MICB_VOUT_CTL);
- req_vout_ctl = wcd939x_get_micb_vout_ctl_val(req_volt);
+ req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt);
if (req_vout_ctl < 0) {
ret = req_vout_ctl;
goto exit;
@@ -2021,10 +2008,10 @@ static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon
* voltage needed to detect threshold microphone, then do
* not change the micbias, just return.
*/
- if (wcd939x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV)
+ if (wcd939x->common.micb_mv[1] >= WCD_MBHC_THR_HS_MICB_MV)
return 0;
- micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd939x->micb2_mv;
+ micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd939x->common.micb_mv[1];
return wcd939x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
}
@@ -2895,28 +2882,16 @@ static const struct snd_soc_dapm_route wcd939x_audio_map[] = {
{"EAR", NULL, "EAR PGA"},
};
-static int wcd939x_set_micbias_data(struct wcd939x_priv *wcd939x)
+static void wcd939x_set_micbias_data(struct device *dev, struct wcd939x_priv *wcd939x)
{
- int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4;
-
- /* set micbias voltage */
- vout_ctl_1 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb1_mv);
- vout_ctl_2 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb2_mv);
- vout_ctl_3 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb3_mv);
- vout_ctl_4 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb4_mv);
- if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || vout_ctl_3 < 0 || vout_ctl_4 < 0)
- return -EINVAL;
-
regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB1,
- WCD939X_MICB_VOUT_CTL, vout_ctl_1);
+ WCD939X_MICB_VOUT_CTL, wcd939x->common.micb_vout[0]);
regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB2,
- WCD939X_MICB_VOUT_CTL, vout_ctl_2);
+ WCD939X_MICB_VOUT_CTL, wcd939x->common.micb_vout[1]);
regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB3,
- WCD939X_MICB_VOUT_CTL, vout_ctl_3);
+ WCD939X_MICB_VOUT_CTL, wcd939x->common.micb_vout[2]);
regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB4,
- WCD939X_MICB_VOUT_CTL, vout_ctl_4);
-
- return 0;
+ WCD939X_MICB_VOUT_CTL, wcd939x->common.micb_vout[3]);
}
static irqreturn_t wcd939x_wd_handle_irq(int irq, void *data)
@@ -3186,37 +3161,6 @@ static int wcd939x_typec_mux_set(struct typec_mux_dev *mux,
}
#endif /* CONFIG_TYPEC */
-static void wcd939x_dt_parse_micbias_info(struct device *dev, struct wcd939x_priv *wcd)
-{
- struct device_node *np = dev->of_node;
- u32 prop_val = 0;
- int rc = 0;
-
- rc = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val);
- if (!rc)
- wcd->micb1_mv = prop_val / 1000;
- else
- dev_info(dev, "%s: Micbias1 DT property not found\n", __func__);
-
- rc = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val);
- if (!rc)
- wcd->micb2_mv = prop_val / 1000;
- else
- dev_info(dev, "%s: Micbias2 DT property not found\n", __func__);
-
- rc = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val);
- if (!rc)
- wcd->micb3_mv = prop_val / 1000;
- else
- dev_info(dev, "%s: Micbias3 DT property not found\n", __func__);
-
- rc = of_property_read_u32(np, "qcom,micbias4-microvolt", &prop_val);
- if (!rc)
- wcd->micb4_mv = prop_val / 1000;
- else
- dev_info(dev, "%s: Micbias4 DT property not found\n", __func__);
-}
-
#if IS_ENABLED(CONFIG_TYPEC)
static bool wcd939x_swap_gnd_mic(struct snd_soc_component *component)
{
@@ -3252,13 +3196,15 @@ static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device
if (ret)
return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
- wcd939x_dt_parse_micbias_info(dev, wcd939x);
+ ret = wcd_dt_parse_micbias_info(&wcd939x->common);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get micbias\n");
cfg->mbhc_micbias = MIC_BIAS_2;
cfg->anc_micbias = MIC_BIAS_2;
cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
cfg->num_btn = WCD939X_MBHC_MAX_BUTTONS;
- cfg->micb_mv = wcd939x->micb2_mv;
+ cfg->micb_mv = wcd939x->common.micb_mv[1];
cfg->linein_th = 5000;
cfg->hs_thr = 1700;
cfg->hph_thr = 50;
@@ -3383,7 +3329,7 @@ static int wcd939x_bind(struct device *dev)
goto err_put_typec_switch;
}
- wcd939x->rxdev = wcd939x_sdw_device_get(wcd939x->rxnode);
+ wcd939x->rxdev = of_sdw_find_device_by_node(wcd939x->rxnode);
if (!wcd939x->rxdev) {
dev_err(dev, "could not find slave with matching of node\n");
ret = -EINVAL;
@@ -3392,7 +3338,7 @@ static int wcd939x_bind(struct device *dev)
wcd939x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd939x->rxdev);
wcd939x->sdw_priv[AIF1_PB]->wcd939x = wcd939x;
- wcd939x->txdev = wcd939x_sdw_device_get(wcd939x->txnode);
+ wcd939x->txdev = of_sdw_find_device_by_node(wcd939x->txnode);
if (!wcd939x->txdev) {
dev_err(dev, "could not find txslave with matching of node\n");
ret = -EINVAL;
@@ -3428,10 +3374,10 @@ static int wcd939x_bind(struct device *dev)
}
/* Get regmap from TX SoundWire device */
- wcd939x->regmap = wcd939x_swr_get_regmap(wcd939x->sdw_priv[AIF1_CAP]);
- if (IS_ERR(wcd939x->regmap)) {
+ wcd939x->regmap = wcd939x->sdw_priv[AIF1_CAP]->regmap;
+ if (!wcd939x->regmap) {
dev_err(dev, "could not get TX device regmap\n");
- ret = PTR_ERR(wcd939x->regmap);
+ ret = -ENODEV;
goto err_remove_rx_link;
}
@@ -3444,11 +3390,7 @@ static int wcd939x_bind(struct device *dev)
wcd939x->sdw_priv[AIF1_PB]->slave_irq = wcd939x->virq;
wcd939x->sdw_priv[AIF1_CAP]->slave_irq = wcd939x->virq;
- ret = wcd939x_set_micbias_data(wcd939x);
- if (ret < 0) {
- dev_err(dev, "%s: bad micbias pdata\n", __func__);
- goto err_remove_rx_link;
- }
+ wcd939x_set_micbias_data(dev, wcd939x);
/* Check WCD9395 version */
regmap_read(wcd939x->regmap, WCD939X_DIGITAL_CHIP_ID1, &id1);
@@ -3613,6 +3555,8 @@ static int wcd939x_probe(struct platform_device *pdev)
dev_set_drvdata(dev, wcd939x);
mutex_init(&wcd939x->micb_lock);
+ wcd939x->common.dev = dev;
+ wcd939x->common.max_bias = 4;
ret = wcd939x_populate_dt_data(wcd939x, dev);
if (ret) {
diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h
index 3204fb1..6bd2366 100644
--- a/sound/soc/codecs/wcd939x.h
+++ b/sound/soc/codecs/wcd939x.h
@@ -844,17 +844,6 @@
#define WCD939X_MAX_SWR_CH_IDS (15)
-struct wcd939x_sdw_ch_info {
- int port_num;
- unsigned int ch_mask;
-};
-
-#define WCD_SDW_CH(id, pn, cmask) \
- [id] = { \
- .port_num = pn, \
- .ch_mask = cmask, \
- }
-
enum wcd939x_tx_sdw_ports {
WCD939X_ADC_1_4_PORT = 1,
WCD939X_ADC_DMIC_1_2_PORT,
@@ -909,7 +898,7 @@ struct wcd939x_sdw_priv {
struct sdw_stream_config sconfig;
struct sdw_stream_runtime *sruntime;
struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS];
- const struct wcd939x_sdw_ch_info *ch_info;
+ const struct wcd_sdw_ch_info *ch_info;
bool port_enable[WCD939X_MAX_SWR_CH_IDS];
int active_ports;
bool is_tx;
@@ -929,11 +918,6 @@ int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai);
-
-struct device *wcd939x_sdw_device_get(struct device_node *np);
-unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev);
-
-struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd);
#else
static inline int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd,
@@ -958,20 +942,6 @@ static inline int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd,
return -EOPNOTSUPP;
}
-static inline struct device *wcd939x_sdw_device_get(struct device_node *np)
-{
- return NULL;
-}
-
-static inline unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev)
-{
- return 0;
-}
-
-struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd)
-{
- return PTR_ERR(-EINVAL);
-}
#endif /* CONFIG_SND_SOC_WCD939X_SDW */
#endif /* __WCD939X_H__ */
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
deleted file mode 100644
index 737ca82..0000000
--- a/sound/soc/codecs/wl1273.c
+++ /dev/null
@@ -1,500 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * ALSA SoC WL1273 codec driver
- *
- * Author: Matti Aaltonen, <matti.j.aaltonen@nokia.com>
- *
- * Copyright: (C) 2010, 2011 Nokia Corporation
- */
-
-#include <linux/mfd/wl1273-core.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/initval.h>
-
-#include "wl1273.h"
-
-enum wl1273_mode { WL1273_MODE_BT, WL1273_MODE_FM_RX, WL1273_MODE_FM_TX };
-
-/* codec private data */
-struct wl1273_priv {
- enum wl1273_mode mode;
- struct wl1273_core *core;
- unsigned int channels;
-};
-
-static int snd_wl1273_fm_set_i2s_mode(struct wl1273_core *core,
- int rate, int width)
-{
- struct device *dev = &core->client->dev;
- int r = 0;
- u16 mode;
-
- dev_dbg(dev, "rate: %d\n", rate);
- dev_dbg(dev, "width: %d\n", width);
-
- mutex_lock(&core->lock);
-
- mode = core->i2s_mode & ~WL1273_IS2_WIDTH & ~WL1273_IS2_RATE;
-
- switch (rate) {
- case 48000:
- mode |= WL1273_IS2_RATE_48K;
- break;
- case 44100:
- mode |= WL1273_IS2_RATE_44_1K;
- break;
- case 32000:
- mode |= WL1273_IS2_RATE_32K;
- break;
- case 22050:
- mode |= WL1273_IS2_RATE_22_05K;
- break;
- case 16000:
- mode |= WL1273_IS2_RATE_16K;
- break;
- case 12000:
- mode |= WL1273_IS2_RATE_12K;
- break;
- case 11025:
- mode |= WL1273_IS2_RATE_11_025;
- break;
- case 8000:
- mode |= WL1273_IS2_RATE_8K;
- break;
- default:
- dev_err(dev, "Sampling rate: %d not supported\n", rate);
- r = -EINVAL;
- goto out;
- }
-
- switch (width) {
- case 16:
- mode |= WL1273_IS2_WIDTH_32;
- break;
- case 20:
- mode |= WL1273_IS2_WIDTH_40;
- break;
- case 24:
- mode |= WL1273_IS2_WIDTH_48;
- break;
- case 25:
- mode |= WL1273_IS2_WIDTH_50;
- break;
- case 30:
- mode |= WL1273_IS2_WIDTH_60;
- break;
- case 32:
- mode |= WL1273_IS2_WIDTH_64;
- break;
- case 40:
- mode |= WL1273_IS2_WIDTH_80;
- break;
- case 48:
- mode |= WL1273_IS2_WIDTH_96;
- break;
- case 64:
- mode |= WL1273_IS2_WIDTH_128;
- break;
- default:
- dev_err(dev, "Data width: %d not supported\n", width);
- r = -EINVAL;
- goto out;
- }
-
- dev_dbg(dev, "WL1273_I2S_DEF_MODE: 0x%04x\n", WL1273_I2S_DEF_MODE);
- dev_dbg(dev, "core->i2s_mode: 0x%04x\n", core->i2s_mode);
- dev_dbg(dev, "mode: 0x%04x\n", mode);
-
- if (core->i2s_mode != mode) {
- r = core->write(core, WL1273_I2S_MODE_CONFIG_SET, mode);
- if (r)
- goto out;
-
- core->i2s_mode = mode;
- r = core->write(core, WL1273_AUDIO_ENABLE,
- WL1273_AUDIO_ENABLE_I2S);
- if (r)
- goto out;
- }
-out:
- mutex_unlock(&core->lock);
-
- return r;
-}
-
-static int snd_wl1273_fm_set_channel_number(struct wl1273_core *core,
- int channel_number)
-{
- struct device *dev = &core->client->dev;
- int r = 0;
-
- dev_dbg(dev, "%s\n", __func__);
-
- mutex_lock(&core->lock);
-
- if (core->channel_number == channel_number)
- goto out;
-
- if (channel_number == 1 && core->mode == WL1273_MODE_RX)
- r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO);
- else if (channel_number == 1 && core->mode == WL1273_MODE_TX)
- r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO);
- else if (channel_number == 2 && core->mode == WL1273_MODE_RX)
- r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO);
- else if (channel_number == 2 && core->mode == WL1273_MODE_TX)
- r = core->write(core, WL1273_MONO_SET, WL1273_TX_STEREO);
- else
- r = -EINVAL;
-out:
- mutex_unlock(&core->lock);
-
- return r;
-}
-
-static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
- struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-
- ucontrol->value.enumerated.item[0] = wl1273->mode;
-
- return 0;
-}
-
-/*
- * TODO: Implement the audio routing in the driver. Now this control
- * only indicates the setting that has been done elsewhere (in the user
- * space).
- */
-static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" };
-
-static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
- struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-
- if (wl1273->mode == ucontrol->value.enumerated.item[0])
- return 0;
-
- /* Do not allow changes while stream is running */
- if (snd_soc_component_active(component))
- return -EPERM;
-
- if (ucontrol->value.enumerated.item[0] >= ARRAY_SIZE(wl1273_audio_route))
- return -EINVAL;
-
- wl1273->mode = ucontrol->value.enumerated.item[0];
-
- return 1;
-}
-
-static SOC_ENUM_SINGLE_EXT_DECL(wl1273_enum, wl1273_audio_route);
-
-static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
- struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-
- dev_dbg(component->dev, "%s: enter.\n", __func__);
-
- ucontrol->value.enumerated.item[0] = wl1273->core->audio_mode;
-
- return 0;
-}
-
-static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
- struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
- int val, r = 0;
-
- dev_dbg(component->dev, "%s: enter.\n", __func__);
-
- val = ucontrol->value.enumerated.item[0];
- if (wl1273->core->audio_mode == val)
- return 0;
-
- r = wl1273->core->set_audio(wl1273->core, val);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static const char * const wl1273_audio_strings[] = { "Digital", "Analog" };
-
-static SOC_ENUM_SINGLE_EXT_DECL(wl1273_audio_enum, wl1273_audio_strings);
-
-static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
- struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-
- dev_dbg(component->dev, "%s: enter.\n", __func__);
-
- ucontrol->value.integer.value[0] = wl1273->core->volume;
-
- return 0;
-}
-
-static int snd_wl1273_fm_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
- struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
- int r;
-
- dev_dbg(component->dev, "%s: enter.\n", __func__);
-
- r = wl1273->core->set_volume(wl1273->core,
- ucontrol->value.integer.value[0]);
- if (r)
- return r;
-
- return 1;
-}
-
-static const struct snd_kcontrol_new wl1273_controls[] = {
- SOC_ENUM_EXT("Codec Mode", wl1273_enum,
- snd_wl1273_get_audio_route, snd_wl1273_set_audio_route),
- SOC_ENUM_EXT("Audio Switch", wl1273_audio_enum,
- snd_wl1273_fm_audio_get, snd_wl1273_fm_audio_put),
- SOC_SINGLE_EXT("Volume", 0, 0, WL1273_MAX_VOLUME, 0,
- snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put),
-};
-
-static const struct snd_soc_dapm_widget wl1273_dapm_widgets[] = {
- SND_SOC_DAPM_INPUT("RX"),
-
- SND_SOC_DAPM_OUTPUT("TX"),
-};
-
-static const struct snd_soc_dapm_route wl1273_dapm_routes[] = {
- { "Capture", NULL, "RX" },
-
- { "TX", NULL, "Playback" },
-};
-
-static int wl1273_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_component *component = dai->component;
- struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-
- switch (wl1273->mode) {
- case WL1273_MODE_BT:
- snd_pcm_hw_constraint_single(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE, 8000);
- snd_pcm_hw_constraint_single(substream->runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS, 1);
- break;
- case WL1273_MODE_FM_RX:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- pr_err("Cannot play in RX mode.\n");
- return -EINVAL;
- }
- break;
- case WL1273_MODE_FM_TX:
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- pr_err("Cannot capture in TX mode.\n");
- return -EINVAL;
- }
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int wl1273_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(dai->component);
- struct wl1273_core *core = wl1273->core;
- unsigned int rate, width, r;
-
- if (params_width(params) != 16) {
- dev_err(dai->dev, "%d bits/sample not supported\n",
- params_width(params));
- return -EINVAL;
- }
-
- rate = params_rate(params);
- width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
-
- if (wl1273->mode == WL1273_MODE_BT) {
- if (rate != 8000) {
- pr_err("Rate %d not supported.\n", params_rate(params));
- return -EINVAL;
- }
-
- if (params_channels(params) != 1) {
- pr_err("Only mono supported.\n");
- return -EINVAL;
- }
-
- return 0;
- }
-
- if (wl1273->mode == WL1273_MODE_FM_TX &&
- substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- pr_err("Only playback supported with TX.\n");
- return -EINVAL;
- }
-
- if (wl1273->mode == WL1273_MODE_FM_RX &&
- substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- pr_err("Only capture supported with RX.\n");
- return -EINVAL;
- }
-
- if (wl1273->mode != WL1273_MODE_FM_RX &&
- wl1273->mode != WL1273_MODE_FM_TX) {
- pr_err("Unexpected mode: %d.\n", wl1273->mode);
- return -EINVAL;
- }
-
- r = snd_wl1273_fm_set_i2s_mode(core, rate, width);
- if (r)
- return r;
-
- wl1273->channels = params_channels(params);
- r = snd_wl1273_fm_set_channel_number(core, wl1273->channels);
- if (r)
- return r;
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops wl1273_dai_ops = {
- .startup = wl1273_startup,
- .hw_params = wl1273_hw_params,
-};
-
-static struct snd_soc_dai_driver wl1273_dai = {
- .name = "wl1273-fm",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE},
- .capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE},
- .ops = &wl1273_dai_ops,
-};
-
-/* Audio interface format for the soc_card driver */
-int wl1273_get_format(struct snd_soc_component *component, unsigned int *fmt)
-{
- struct wl1273_priv *wl1273;
-
- if (component == NULL || fmt == NULL)
- return -EINVAL;
-
- wl1273 = snd_soc_component_get_drvdata(component);
-
- switch (wl1273->mode) {
- case WL1273_MODE_FM_RX:
- case WL1273_MODE_FM_TX:
- *fmt = SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBP_CFP;
-
- break;
- case WL1273_MODE_BT:
- *fmt = SND_SOC_DAIFMT_DSP_A |
- SND_SOC_DAIFMT_IB_NF |
- SND_SOC_DAIFMT_CBP_CFP;
-
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(wl1273_get_format);
-
-static int wl1273_probe(struct snd_soc_component *component)
-{
- struct wl1273_core **core = component->dev->platform_data;
- struct wl1273_priv *wl1273;
-
- dev_dbg(component->dev, "%s.\n", __func__);
-
- if (!core) {
- dev_err(component->dev, "Platform data is missing.\n");
- return -EINVAL;
- }
-
- wl1273 = kzalloc(sizeof(struct wl1273_priv), GFP_KERNEL);
- if (!wl1273)
- return -ENOMEM;
-
- wl1273->mode = WL1273_MODE_BT;
- wl1273->core = *core;
-
- snd_soc_component_set_drvdata(component, wl1273);
-
- return 0;
-}
-
-static void wl1273_remove(struct snd_soc_component *component)
-{
- struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-
- dev_dbg(component->dev, "%s\n", __func__);
- kfree(wl1273);
-}
-
-static const struct snd_soc_component_driver soc_component_dev_wl1273 = {
- .probe = wl1273_probe,
- .remove = wl1273_remove,
- .controls = wl1273_controls,
- .num_controls = ARRAY_SIZE(wl1273_controls),
- .dapm_widgets = wl1273_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets),
- .dapm_routes = wl1273_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes),
- .idle_bias_on = 1,
- .use_pmdown_time = 1,
- .endianness = 1,
-};
-
-static int wl1273_platform_probe(struct platform_device *pdev)
-{
- return devm_snd_soc_register_component(&pdev->dev,
- &soc_component_dev_wl1273,
- &wl1273_dai, 1);
-}
-
-MODULE_ALIAS("platform:wl1273-codec");
-
-static struct platform_driver wl1273_platform_driver = {
- .driver = {
- .name = "wl1273-codec",
- },
- .probe = wl1273_platform_probe,
-};
-
-module_platform_driver(wl1273_platform_driver);
-
-MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>");
-MODULE_DESCRIPTION("ASoC WL1273 codec driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wl1273.h b/sound/soc/codecs/wl1273.h
deleted file mode 100644
index 66c312f..0000000
--- a/sound/soc/codecs/wl1273.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * sound/soc/codec/wl1273.h
- *
- * ALSA SoC WL1273 codec driver
- *
- * Copyright (C) Nokia Corporation
- * Author: Matti Aaltonen <matti.j.aaltonen@nokia.com>
- */
-
-#ifndef __WL1273_CODEC_H__
-#define __WL1273_CODEC_H__
-
-int wl1273_get_format(struct snd_soc_component *component, unsigned int *fmt);
-
-#endif /* End of __WL1273_CODEC_H__ */
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 9be4f6c..75d923c 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -1536,7 +1536,7 @@ static int wm8993_probe(struct snd_soc_component *component)
* VMID as an output and can disable it.
*/
if (wm8993->pdata.lineout1_diff && wm8993->pdata.lineout2_diff)
- dapm->idle_bias_off = 1;
+ dapm->idle_bias = false;
return 0;
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 240ec1be..128c3a5 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -4182,8 +4182,8 @@ static int wm8994_component_probe(struct snd_soc_component *component)
wm8994->micdet_irq = control->pdata.micdet_irq;
- /* By default use idle_bias_off, will override for WM8994 */
- dapm->idle_bias_off = 1;
+ /* By default use idle_bias false, will override for WM8994 */
+ dapm->idle_bias = false;
/* Set revision-specific configuration */
switch (control->type) {
@@ -4191,7 +4191,7 @@ static int wm8994_component_probe(struct snd_soc_component *component)
/* Single ended line outputs should have VMID on. */
if (!control->pdata.lineout1_diff ||
!control->pdata.lineout2_diff)
- dapm->idle_bias_off = 0;
+ dapm->idle_bias = true;
switch (control->revision) {
case 2:
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index bc584b1..b28398a 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -106,33 +106,33 @@ struct wm8994_priv {
int vss_ena[3];
int enh_eq_ena[3];
- /* Platform dependant DRC configuration */
+ /* Platform dependent DRC configuration */
const char **drc_texts;
int drc_cfg[WM8994_NUM_DRC];
struct soc_enum drc_enum;
- /* Platform dependant ReTune mobile configuration */
+ /* Platform dependent ReTune mobile configuration */
int num_retune_mobile_texts;
const char **retune_mobile_texts;
int retune_mobile_cfg[WM8994_NUM_EQ];
struct soc_enum retune_mobile_enum;
- /* Platform dependant MBC configuration */
+ /* Platform dependent MBC configuration */
int mbc_cfg;
const char **mbc_texts;
struct soc_enum mbc_enum;
- /* Platform dependant VSS configuration */
+ /* Platform dependent VSS configuration */
int vss_cfg;
const char **vss_texts;
struct soc_enum vss_enum;
- /* Platform dependant VSS HPF configuration */
+ /* Platform dependent VSS HPF configuration */
int vss_hpf_cfg;
const char **vss_hpf_texts;
struct soc_enum vss_hpf_enum;
- /* Platform dependant enhanced EQ configuration */
+ /* Platform dependent enhanced EQ configuration */
int enh_eq_cfg;
const char **enh_eq_texts;
struct soc_enum enh_eq_enum;
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index 459b399..ee20407 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -77,7 +77,7 @@ struct wm8996_priv {
int rx_rate[WM8996_AIFS];
int bclk_rate[WM8996_AIFS];
- /* Platform dependant ReTune mobile configuration */
+ /* Platform dependent ReTune mobile configuration */
int num_retune_mobile_texts;
const char **retune_mobile_texts;
int retune_mobile_cfg[2];
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 8a1d5cc..82c7633 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -1043,7 +1043,7 @@ int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- queue_work(system_unbound_wq, &dsp->boot_work);
+ queue_work(system_dfl_wq, &dsp->boot_work);
break;
case SND_SOC_DAPM_PRE_PMD:
wm_adsp_power_down(dsp);
diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c
index 188363b..ca4520a 100644
--- a/sound/soc/codecs/wsa883x.c
+++ b/sound/soc/codecs/wsa883x.c
@@ -14,6 +14,7 @@
#include <linux/printk.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
@@ -468,6 +469,7 @@ struct wsa883x_priv {
struct sdw_stream_runtime *sruntime;
struct sdw_port_config port_config[WSA883X_MAX_SWR_PORTS];
struct gpio_desc *sd_n;
+ struct reset_control *sd_reset;
bool port_prepared[WSA883X_MAX_SWR_PORTS];
bool port_enable[WSA883X_MAX_SWR_PORTS];
int active_ports;
@@ -1546,6 +1548,46 @@ static const struct hwmon_chip_info wsa883x_hwmon_chip_info = {
.info = wsa883x_hwmon_info,
};
+static void wsa883x_reset_assert(void *data)
+{
+ struct wsa883x_priv *wsa883x = data;
+
+ if (wsa883x->sd_reset)
+ reset_control_assert(wsa883x->sd_reset);
+ else
+ gpiod_direction_output(wsa883x->sd_n, 1);
+}
+
+static void wsa883x_reset_deassert(struct wsa883x_priv *wsa883x)
+{
+ if (wsa883x->sd_reset)
+ reset_control_deassert(wsa883x->sd_reset);
+ else
+ gpiod_direction_output(wsa883x->sd_n, 0);
+}
+
+static int wsa883x_get_reset(struct device *dev, struct wsa883x_priv *wsa883x)
+{
+ wsa883x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL);
+ if (IS_ERR(wsa883x->sd_reset))
+ return dev_err_probe(dev, PTR_ERR(wsa883x->sd_reset),
+ "Failed to get reset\n");
+ /*
+ * if sd_reset: NULL, so use the backwards compatible way for powerdown-gpios,
+ * which does not handle sharing GPIO properly.
+ */
+ if (!wsa883x->sd_reset) {
+ wsa883x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
+ GPIOD_FLAGS_BIT_NONEXCLUSIVE |
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(wsa883x->sd_n))
+ return dev_err_probe(dev, PTR_ERR(wsa883x->sd_n),
+ "Shutdown Control GPIO not found\n");
+ }
+
+ return 0;
+}
+
static int wsa883x_probe(struct sdw_slave *pdev,
const struct sdw_device_id *id)
{
@@ -1566,13 +1608,9 @@ static int wsa883x_probe(struct sdw_slave *pdev,
if (ret)
return dev_err_probe(dev, ret, "Failed to enable vdd regulator\n");
- wsa883x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
- GPIOD_FLAGS_BIT_NONEXCLUSIVE | GPIOD_OUT_HIGH);
- if (IS_ERR(wsa883x->sd_n)) {
- ret = dev_err_probe(dev, PTR_ERR(wsa883x->sd_n),
- "Shutdown Control GPIO not found\n");
+ ret = wsa883x_get_reset(dev, wsa883x);
+ if (ret)
goto err;
- }
dev_set_drvdata(dev, wsa883x);
wsa883x->slave = pdev;
@@ -1595,11 +1633,14 @@ static int wsa883x_probe(struct sdw_slave *pdev,
pdev->prop.simple_clk_stop_capable = true;
pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop;
pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
- gpiod_direction_output(wsa883x->sd_n, 0);
+
+ wsa883x_reset_deassert(wsa883x);
+ ret = devm_add_action_or_reset(dev, wsa883x_reset_assert, wsa883x);
+ if (ret)
+ return ret;
wsa883x->regmap = devm_regmap_init_sdw(pdev, &wsa883x_regmap_config);
if (IS_ERR(wsa883x->regmap)) {
- gpiod_direction_output(wsa883x->sd_n, 1);
ret = dev_err_probe(dev, PTR_ERR(wsa883x->regmap),
"regmap_init failed\n");
goto err;
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index d0367b2..757e7868 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -1244,7 +1244,6 @@ static struct regmap_config fsl_sai_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .fast_io = true,
.max_register = FSL_SAI_RMR,
.reg_defaults = fsl_sai_reg_defaults_ofs0,
@@ -1346,7 +1345,7 @@ static int fsl_sai_read_dlcfg(struct fsl_sai *sai)
num_cfg = elems / 3;
/* Add one more for default value */
- cfg = devm_kzalloc(&pdev->dev, (num_cfg + 1) * sizeof(*cfg), GFP_KERNEL);
+ cfg = devm_kcalloc(&pdev->dev, num_cfg + 1, sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
index cc2918e..f8335a0 100644
--- a/sound/soc/fsl/imx-audmux.c
+++ b/sound/soc/fsl/imx-audmux.c
@@ -305,7 +305,7 @@ static int imx_audmux_probe(struct platform_device *pdev)
return -EINVAL;
}
- regcache = devm_kzalloc(&pdev->dev, sizeof(u32) * reg_max, GFP_KERNEL);
+ regcache = devm_kcalloc(&pdev->dev, reg_max, sizeof(u32), GFP_KERNEL);
if (!regcache)
return -ENOMEM;
diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c
index fe47b43..1115189 100644
--- a/sound/soc/fsl/imx-hdmi.c
+++ b/sound/soc/fsl/imx-hdmi.c
@@ -101,7 +101,6 @@ static int imx_hdmi_probe(struct platform_device *pdev)
bool hdmi_out = of_property_read_bool(np, "hdmi-out");
bool hdmi_in = of_property_read_bool(np, "hdmi-in");
struct snd_soc_dai_link_component *dlc;
- struct platform_device *cpu_pdev;
struct device_node *cpu_np;
struct imx_hdmi_data *data;
int ret;
@@ -117,17 +116,9 @@ static int imx_hdmi_probe(struct platform_device *pdev)
goto fail;
}
- cpu_pdev = of_find_device_by_node(cpu_np);
- if (!cpu_pdev) {
- dev_err(&pdev->dev, "failed to find SAI platform device\n");
- ret = -EINVAL;
- goto fail;
- }
-
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
- put_device(&cpu_pdev->dev);
goto fail;
}
@@ -140,15 +131,13 @@ static int imx_hdmi_probe(struct platform_device *pdev)
data->dai.name = "i.MX HDMI";
data->dai.stream_name = "i.MX HDMI";
- data->dai.cpus->dai_name = dev_name(&cpu_pdev->dev);
+ data->dai.cpus->of_node = cpu_np;
data->dai.platforms->of_node = cpu_np;
data->dai.ops = &imx_hdmi_ops;
data->dai.playback_only = true;
data->dai.capture_only = false;
data->dai.init = imx_hdmi_init;
- put_device(&cpu_pdev->dev);
-
if (of_node_name_eq(cpu_np, "sai")) {
data->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1;
data->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c
index 89b9959..2e49066 100644
--- a/sound/soc/generic/test-component.c
+++ b/sound/soc/generic/test-component.c
@@ -547,8 +547,8 @@ static int test_driver_probe(struct platform_device *pdev)
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
cdriv = devm_kzalloc(dev, sizeof(*cdriv), GFP_KERNEL);
- ddriv = devm_kzalloc(dev, sizeof(*ddriv) * num, GFP_KERNEL);
- dname = devm_kzalloc(dev, sizeof(*dname) * num, GFP_KERNEL);
+ ddriv = devm_kcalloc(dev, num, sizeof(*ddriv), GFP_KERNEL);
+ dname = devm_kcalloc(dev, num, sizeof(*dname), GFP_KERNEL);
if (!priv || !cdriv || !ddriv || !dname || !adata)
return -EINVAL;
diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c
index e0357d2..3c47c8de 100644
--- a/sound/soc/intel/atom/sst/sst.c
+++ b/sound/soc/intel/atom/sst/sst.c
@@ -64,7 +64,7 @@ static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
header.p.header_high.part.done = 0;
sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
- /* write 1 to clear status register */;
+ /* write 1 to clear status register */
isr.part.done_interrupt = 1;
sst_shim_write64(drv->shim, SST_ISRX, isr.full);
spin_unlock(&drv->ipc_spin_lock);
diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c
index 3dccf0a..b922eea 100644
--- a/sound/soc/intel/avs/apl.c
+++ b/sound/soc/intel/avs/apl.c
@@ -10,6 +10,7 @@
#include <linux/slab.h>
#include <sound/hdaudio_ext.h>
#include "avs.h"
+#include "debug.h"
#include "messages.h"
#include "path.h"
#include "registers.h"
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 4c096af..0f8ddd0 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -22,7 +22,6 @@
struct avs_dev;
struct avs_tplg;
struct avs_tplg_library;
-struct avs_soc_component;
struct avs_ipc_msg;
#ifdef CONFIG_ACPI
@@ -348,91 +347,18 @@ struct avs_soc_component {
extern const struct snd_soc_dai_ops avs_dai_fe_ops;
-int avs_soc_component_register(struct device *dev, const char *name,
- struct snd_soc_component_driver *drv,
- struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais);
-int avs_dmic_platform_register(struct avs_dev *adev, const char *name);
-int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
- unsigned long *tdms);
-int avs_hda_platform_register(struct avs_dev *adev, const char *name);
+int avs_register_dmic_component(struct avs_dev *adev, const char *name);
+int avs_register_i2s_component(struct avs_dev *adev, const char *name, unsigned long port_mask,
+ unsigned long *tdms);
+int avs_register_hda_component(struct avs_dev *adev, const char *name);
+int avs_register_component(struct device *dev, const char *name,
+ struct snd_soc_component_driver *drv,
+ struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais);
int avs_register_all_boards(struct avs_dev *adev);
void avs_unregister_all_boards(struct avs_dev *adev);
-/* Firmware tracing helpers */
-
-#define avs_log_buffer_size(adev) \
- ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
-
-#define avs_log_buffer_addr(adev, core) \
-({ \
- s32 __offset = avs_dsp_op(adev, log_buffer_offset, core); \
- (__offset < 0) ? NULL : \
- (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \
-})
-
-static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg)
-{
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&adev->trace_lock, flags);
- ret = avs_dsp_op(adev, log_buffer_status, msg);
- spin_unlock_irqrestore(&adev->trace_lock, flags);
-
- return ret;
-}
-
-struct avs_apl_log_buffer_layout {
- u32 read_ptr;
- u32 write_ptr;
- u8 buffer[];
-} __packed;
-static_assert(sizeof(struct avs_apl_log_buffer_layout) == 8);
-
-#define avs_apl_log_payload_size(adev) \
- (avs_log_buffer_size(adev) - sizeof(struct avs_apl_log_buffer_layout))
-
-#define avs_apl_log_payload_addr(addr) \
- (addr + sizeof(struct avs_apl_log_buffer_layout))
-
-#ifdef CONFIG_DEBUG_FS
-#define AVS_SET_ENABLE_LOGS_OP(name) \
- .enable_logs = avs_##name##_enable_logs
-
-bool avs_logging_fw(struct avs_dev *adev);
-void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len);
-void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len);
-
-int avs_probe_platform_register(struct avs_dev *adev, const char *name);
-
-void avs_debugfs_init(struct avs_dev *adev);
-void avs_debugfs_exit(struct avs_dev *adev);
-#else
-#define AVS_SET_ENABLE_LOGS_OP(name)
-
-static inline bool avs_logging_fw(struct avs_dev *adev)
-{
- return false;
-}
-
-static inline void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
-{
-}
-
-static inline void
-avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len)
-{
-}
-
-static inline int avs_probe_platform_register(struct avs_dev *adev, const char *name)
-{
- return 0;
-}
-
-static inline void avs_debugfs_init(struct avs_dev *adev) { }
-static inline void avs_debugfs_exit(struct avs_dev *adev) { }
-#endif
+int avs_parse_sched_cfg(struct avs_dev *adev, const char *buf, size_t len);
/* Filesystems integration */
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
index fb49167..52e6266 100644
--- a/sound/soc/intel/avs/board_selection.c
+++ b/sound/soc/intel/avs/board_selection.c
@@ -17,6 +17,8 @@
#include <sound/soc-acpi.h>
#include <sound/soc-component.h>
#include "avs.h"
+#include "debug.h"
+#include "pcm.h"
#include "utils.h"
static char *i2s_test;
@@ -56,19 +58,13 @@ static const struct dmi_system_id kblr_dmi_table[] = {
static struct snd_soc_acpi_mach *dmi_match_quirk(void *arg)
{
struct snd_soc_acpi_mach *mach = arg;
- const struct dmi_system_id *dmi_id;
struct dmi_system_id *dmi_table;
- if (mach->quirk_data == NULL)
- return mach;
-
dmi_table = (struct dmi_system_id *)mach->quirk_data;
- dmi_id = dmi_first_match(dmi_table);
- if (!dmi_id)
- return NULL;
-
- return mach;
+ if (!dmi_table || dmi_first_match(dmi_table))
+ return mach;
+ return NULL;
}
#define AVS_SSP(x) (BIT(x))
@@ -368,10 +364,10 @@ struct avs_acpi_boards {
/* supported I2S boards per platform */
static const struct avs_acpi_boards i2s_boards[] = {
- AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines),
- AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines),
- AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines),
- AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines),
+ AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines),
AVS_MACH_ENTRY(HDA_CNL_LP, avs_cnl_i2s_machines),
AVS_MACH_ENTRY(HDA_CNL_H, avs_cnl_i2s_machines),
AVS_MACH_ENTRY(HDA_CML_LP, avs_cnl_i2s_machines),
@@ -386,185 +382,122 @@ static const struct avs_acpi_boards i2s_boards[] = {
{ },
};
-static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
+static struct snd_soc_acpi_mach *avs_get_i2s_machines(struct avs_dev *adev)
{
int id, i;
id = adev->base.pci->device;
for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
if (i2s_boards[i].id == id)
- return &i2s_boards[i];
+ return i2s_boards[i].machs;
return NULL;
}
-/* platform devices owned by AVS audio are removed with this hook */
-static void board_pdev_unregister(void *data)
+/* Platform devices spawned by AVS driver are removed with this hook. */
+static void avs_unregister_board(void *pdev)
{
- platform_device_unregister(data);
+ platform_device_unregister(pdev);
+}
+
+static struct platform_device *avs_register_board(struct avs_dev *adev, const char *name,
+ const void *data, size_t size)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ pdev = platform_device_register_data(NULL, name, PLATFORM_DEVID_AUTO, data, size);
+ if (IS_ERR(pdev))
+ return pdev;
+
+ ret = devm_add_action_or_reset(adev->dev, avs_unregister_board, pdev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return pdev;
+}
+
+static struct platform_device *avs_register_board_pdata(struct avs_dev *adev, const char *name,
+ struct snd_soc_acpi_mach *mach,
+ struct hda_codec *codec,
+ unsigned long *tdms, char *codec_name)
+{
+ struct avs_mach_pdata *pdata;
+
+ pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->codec = codec;
+ pdata->tdms = tdms;
+ pdata->codec_name = codec_name;
+ pdata->obsolete_card_names = obsolete_card_names;
+ mach->pdata = pdata;
+
+ return avs_register_board(adev, name, mach, sizeof(*mach));
}
static int __maybe_unused avs_register_probe_board(struct avs_dev *adev)
{
- struct platform_device *board;
- struct snd_soc_acpi_mach mach = {{0}};
- int ret;
+ struct platform_device *pdev;
- ret = avs_probe_platform_register(adev, "probe-platform");
- if (ret < 0)
- return ret;
+ pdev = avs_register_board(adev, "avs_probe_mb", NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
- mach.mach_params.platform = "probe-platform";
-
- board = platform_device_register_data(NULL, "avs_probe_mb", PLATFORM_DEVID_NONE,
- (const void *)&mach, sizeof(mach));
- if (IS_ERR(board)) {
- dev_err(adev->dev, "probe board register failed\n");
- return PTR_ERR(board);
- }
-
- ret = devm_add_action(adev->dev, board_pdev_unregister, board);
- if (ret < 0) {
- platform_device_unregister(board);
- return ret;
- }
- return 0;
+ return avs_register_probe_component(adev, dev_name(&pdev->dev));
}
static int avs_register_dmic_board(struct avs_dev *adev)
{
- struct platform_device *codec, *board;
- struct snd_soc_acpi_mach mach = {{0}};
- struct avs_mach_pdata *pdata;
- int ret;
+ static struct snd_soc_acpi_mach mach = {
+ .tplg_filename = "dmic-tplg.bin",
+ };
+ struct platform_device *pdev;
+ char *codec_name;
if (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, -1, -1)) {
dev_dbg(adev->dev, "no DMIC endpoints present\n");
return 0;
}
- codec = platform_device_register_simple("dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
- if (IS_ERR(codec)) {
- dev_err(adev->dev, "dmic codec register failed\n");
- return PTR_ERR(codec);
- }
+ /* DMIC present in Intel PCH is enumerated statically. */
+ pdev = avs_register_board(adev, "dmic-codec", NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
- ret = devm_add_action(adev->dev, board_pdev_unregister, codec);
- if (ret < 0) {
- platform_device_unregister(codec);
- return ret;
- }
-
- ret = avs_dmic_platform_register(adev, "dmic-platform");
- if (ret < 0)
- return ret;
-
- pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
- pdata->obsolete_card_names = obsolete_card_names;
- mach.pdata = pdata;
- mach.tplg_filename = "dmic-tplg.bin";
- mach.mach_params.platform = "dmic-platform";
-
- board = platform_device_register_data(NULL, "avs_dmic", PLATFORM_DEVID_NONE,
- (const void *)&mach, sizeof(mach));
- if (IS_ERR(board)) {
- dev_err(adev->dev, "dmic board register failed\n");
- return PTR_ERR(board);
- }
-
- ret = devm_add_action(adev->dev, board_pdev_unregister, board);
- if (ret < 0) {
- platform_device_unregister(board);
- return ret;
- }
-
- return 0;
-}
-
-static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
-{
- struct platform_device *board;
- struct avs_mach_pdata *pdata;
- int num_ssps;
- char *name;
- int ret;
- int uid;
-
- num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
- if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
- dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
- num_ssps, mach->drv_name,
- (unsigned long)__fls(mach->mach_params.i2s_link_mask));
- return -ENODEV;
- }
-
- pdata = mach->pdata;
- if (!pdata)
- pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
- pdata->obsolete_card_names = obsolete_card_names;
- mach->pdata = pdata;
-
- uid = mach->mach_params.i2s_link_mask;
- if (avs_mach_singular_ssp(mach))
- uid = (uid << AVS_CHANNELS_MAX) + avs_mach_ssp_tdm(mach, avs_mach_ssp_port(mach));
-
- name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name, uid);
- if (!name)
+ codec_name = devm_kstrdup(adev->dev, dev_name(&pdev->dev), GFP_KERNEL);
+ if (!codec_name)
return -ENOMEM;
- ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, pdata->tdms);
- if (ret < 0)
- return ret;
+ pdev = avs_register_board_pdata(adev, "avs_dmic", &mach, NULL, NULL, codec_name);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
- mach->mach_params.platform = name;
-
- board = platform_device_register_data(NULL, mach->drv_name, uid,
- (const void *)mach, sizeof(*mach));
- if (IS_ERR(board)) {
- dev_err(adev->dev, "ssp board register failed\n");
- return PTR_ERR(board);
- }
-
- ret = devm_add_action(adev->dev, board_pdev_unregister, board);
- if (ret < 0) {
- platform_device_unregister(board);
- return ret;
- }
-
- return 0;
+ return avs_register_dmic_component(adev, dev_name(&pdev->dev));
}
static int avs_register_i2s_test_board(struct avs_dev *adev, int ssp_port, int tdm_slot)
{
- struct snd_soc_acpi_mach *mach;
- int tdm_mask = BIT(tdm_slot);
- unsigned long *tdm_cfg;
- char *tplg_name;
- int ret;
+ struct snd_soc_acpi_mach mach = {{0}};
+ struct platform_device *pdev;
+ unsigned long *tdms;
- mach = devm_kzalloc(adev->dev, sizeof(*mach), GFP_KERNEL);
- tdm_cfg = devm_kcalloc(adev->dev, ssp_port + 1, sizeof(unsigned long), GFP_KERNEL);
- tplg_name = devm_kasprintf(adev->dev, GFP_KERNEL, AVS_STRING_FMT("i2s", "-test-tplg.bin",
- ssp_port, tdm_slot));
- if (!mach || !tdm_cfg || !tplg_name)
+ tdms = devm_kcalloc(adev->dev, ssp_port + 1, sizeof(*tdms), GFP_KERNEL);
+ mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL,
+ AVS_STRING_FMT("i2s", "-test-tplg.bin",
+ ssp_port, tdm_slot));
+ if (!tdms || !mach.tplg_filename)
return -ENOMEM;
- mach->drv_name = "avs_i2s_test";
- mach->mach_params.i2s_link_mask = AVS_SSP(ssp_port);
- tdm_cfg[ssp_port] = tdm_mask;
- mach->pdata = tdm_cfg;
- mach->tplg_filename = tplg_name;
+ tdms[ssp_port] = BIT(tdm_slot);
+ mach.drv_name = "avs_i2s_test";
+ mach.mach_params.i2s_link_mask = AVS_SSP(ssp_port);
- ret = avs_register_i2s_board(adev, mach);
- if (ret < 0) {
- dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret);
- return ret;
- }
+ pdev = avs_register_board_pdata(adev, mach.drv_name, &mach, NULL, tdms, NULL);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
- return 0;
+ return avs_register_i2s_component(adev, dev_name(&pdev->dev), AVS_SSP(ssp_port), tdms);
}
static int avs_register_i2s_test_boards(struct avs_dev *adev)
@@ -574,6 +507,9 @@ static int avs_register_i2s_test_boards(struct avs_dev *adev)
unsigned long tdm_slots;
u32 *array, num_elems;
+ if (!i2s_test)
+ return 0;
+
ret = parse_int_array(i2s_test, strlen(i2s_test), (int **)&array);
if (ret) {
dev_err(adev->dev, "failed to parse i2s_test parameter\n");
@@ -599,9 +535,26 @@ static int avs_register_i2s_test_boards(struct avs_dev *adev)
return 0;
}
+static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
+{
+ u32 i2s_mask = mach->mach_params.i2s_link_mask;
+ struct platform_device *pdev;
+ unsigned long *tdms = NULL;
+
+ if (mach->pdata)
+ tdms = ((struct avs_mach_pdata *)mach->pdata)->tdms;
+
+ pdev = avs_register_board_pdata(adev, mach->drv_name, mach, NULL, tdms, NULL);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ return avs_register_i2s_component(adev, dev_name(&pdev->dev), i2s_mask, tdms);
+}
+
static int avs_register_i2s_boards(struct avs_dev *adev)
{
- const struct avs_acpi_boards *boards;
+ int num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+ struct snd_soc_acpi_mach *machs;
struct snd_soc_acpi_mach *mach;
int ret;
@@ -610,19 +563,22 @@ static int avs_register_i2s_boards(struct avs_dev *adev)
return 0;
}
- if (i2s_test)
- return avs_register_i2s_test_boards(adev);
-
- boards = avs_get_i2s_boards(adev);
- if (!boards) {
+ machs = avs_get_i2s_machines(adev);
+ if (!machs) {
dev_dbg(adev->dev, "no I2S endpoints supported\n");
return 0;
}
- for (mach = boards->machs; mach->id[0]; mach++) {
+ for (mach = machs; mach->id[0]; mach++) {
if (!acpi_dev_present(mach->id, mach->uid, -1))
continue;
+ if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
+ dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
+ num_ssps, mach->drv_name,
+ (unsigned long)__fls(mach->mach_params.i2s_link_mask));
+ continue;
+ }
if (mach->machine_quirk)
if (!mach->machine_quirk(mach))
continue;
@@ -637,49 +593,20 @@ static int avs_register_i2s_boards(struct avs_dev *adev)
static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
{
- struct snd_soc_acpi_mach mach = {{0}};
- struct platform_device *board;
- struct avs_mach_pdata *pdata;
struct hdac_device *hdev = &codec->core;
- char *pname;
- int ret, id;
+ struct snd_soc_acpi_mach mach = {{0}};
+ struct platform_device *pdev;
- pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
- if (!pname)
- return -ENOMEM;
-
- pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
- pdata->obsolete_card_names = obsolete_card_names;
- pdata->codec = codec;
-
- ret = avs_hda_platform_register(adev, pname);
- if (ret < 0)
- return ret;
-
- mach.pdata = pdata;
- mach.mach_params.platform = pname;
mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
hdev->vendor_id);
if (!mach.tplg_filename)
return -ENOMEM;
- id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
- board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
- sizeof(mach));
- if (IS_ERR(board)) {
- dev_err(adev->dev, "hda board register failed\n");
- return PTR_ERR(board);
- }
+ pdev = avs_register_board_pdata(adev, "avs_hdaudio", &mach, codec, NULL, NULL);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
- ret = devm_add_action(adev->dev, board_pdev_unregister, board);
- if (ret < 0) {
- platform_device_unregister(board);
- return ret;
- }
-
- return 0;
+ return avs_register_hda_component(adev, dev_name(&pdev->dev));
}
static int avs_register_hda_boards(struct avs_dev *adev)
@@ -722,6 +649,10 @@ int avs_register_all_boards(struct avs_dev *adev)
dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
ret);
+ ret = avs_register_i2s_test_boards(adev);
+ if (ret)
+ dev_dbg(adev->dev, "enumerate I2S TEST endpoints failed: %d\n", ret);
+
ret = avs_register_i2s_boards(adev);
if (ret < 0)
dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c
index 3ef0db2..6782dc7 100644
--- a/sound/soc/intel/avs/boards/da7219.c
+++ b/sound/soc/intel/avs/boards/da7219.c
@@ -165,8 +165,8 @@ avs_da7219_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_param
return 0;
}
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -176,8 +176,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
@@ -193,6 +191,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -218,18 +217,16 @@ static int avs_da7219_probe(struct platform_device *pdev)
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -259,10 +256,6 @@ static int avs_da7219_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c
index a1448a9..bf6f580 100644
--- a/sound/soc/intel/avs/boards/dmic.c
+++ b/sound/soc/intel/avs/boards/dmic.c
@@ -14,30 +14,6 @@
SND_SOC_DAILINK_DEF(dmic_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC Pin")));
SND_SOC_DAILINK_DEF(dmic_wov_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC WoV Pin")));
-SND_SOC_DAILINK_DEF(dmic_codec, DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
-/* Name overridden on probe */
-SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("")));
-
-static struct snd_soc_dai_link card_dai_links[] = {
- /* Back ends */
- {
- .name = "DMIC",
- .id = 0,
- .capture_only = 1,
- .nonatomic = 1,
- .no_pcm = 1,
- SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform),
- },
- {
- .name = "DMIC WoV",
- .id = 1,
- .capture_only = 1,
- .nonatomic = 1,
- .no_pcm = 1,
- .ignore_suspend = 1,
- SND_SOC_DAILINK_REG(dmic_wov_pin, dmic_codec, platform),
- },
-};
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
@@ -47,12 +23,56 @@ static const struct snd_soc_dapm_route card_routes[] = {
{"DMic", NULL, "SoC DMIC"},
};
+static int avs_create_dai_links(struct device *dev, const char *codec_name,
+ struct snd_soc_dai_link **links, int *num_links)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+ const int num_dl = 2;
+
+ dl = devm_kcalloc(dev, num_dl, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->codecs)
+ return -ENOMEM;
+
+ dl->codecs->name = devm_kstrdup(dev, codec_name, GFP_KERNEL);
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, "dmic-hifi");
+ if (!dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl[0].num_cpus = 1;
+ dl[0].num_codecs = 1;
+ dl[0].platforms = platform;
+ dl[0].num_platforms = 1;
+ dl[0].nonatomic = 1;
+ dl[0].no_pcm = 1;
+ dl[0].capture_only = 1;
+ memcpy(&dl[1], &dl[0], sizeof(*dl));
+
+ dl[0].name = "DMIC";
+ dl[0].cpus = dmic_pin;
+ dl[0].id = 0;
+ dl[1].name = "DMIC WoV";
+ dl[1].cpus = dmic_wov_pin;
+ dl[1].id = 1;
+ dl[1].ignore_suspend = 1;
+
+ *links = dl;
+ *num_links = num_dl;
+ return 0;
+}
+
static int avs_dmic_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
- struct device *dev = &pdev->dev;
int ret;
mach = dev_get_platdata(dev);
@@ -62,6 +82,10 @@ static int avs_dmic_probe(struct platform_device *pdev)
if (!card)
return -ENOMEM;
+ ret = avs_create_dai_links(dev, pdata->codec_name, &card->dai_link, &card->num_links);
+ if (ret)
+ return ret;
+
if (pdata->obsolete_card_names) {
card->name = "avs_dmic";
} else {
@@ -70,18 +94,12 @@ static int avs_dmic_probe(struct platform_device *pdev)
}
card->dev = dev;
card->owner = THIS_MODULE;
- card->dai_link = card_dai_links;
- card->num_links = ARRAY_SIZE(card_dai_links);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_routes;
card->num_dapm_routes = ARRAY_SIZE(card_routes);
card->fully_routed = true;
- ret = snd_soc_fixup_dai_links_platform_name(card, mach->mach_params.platform);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c
index 1955f2d..eb2b408 100644
--- a/sound/soc/intel/avs/boards/es8336.c
+++ b/sound/soc/intel/avs/boards/es8336.c
@@ -132,7 +132,7 @@ static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime)
snd_jack_set_key(data->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_soc_component_set_jack(component, &data->jack, NULL);
- card->dapm.idle_bias_off = true;
+ card->dapm.idle_bias = false;
return 0;
}
@@ -195,8 +195,9 @@ static int avs_es8336_be_fixup(struct snd_soc_pcm_runtime *runtime,
return 0;
}
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -206,8 +207,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -222,6 +221,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -263,18 +263,16 @@ static int avs_es8336_probe(struct platform_device *pdev)
struct avs_card_drvdata *data;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -306,10 +304,6 @@ static int avs_es8336_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, data);
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c
index 19b2255..aec769e 100644
--- a/sound/soc/intel/avs/boards/hdaudio.c
+++ b/sound/soc/intel/avs/boards/hdaudio.c
@@ -16,7 +16,7 @@
#include "../utils.h"
static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int pcm_count,
- const char *platform_name, struct snd_soc_dai_link **links)
+ struct snd_soc_dai_link **links)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -29,7 +29,7 @@ static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
+ platform->name = dev_name(dev);
pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
@@ -142,7 +142,7 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
list_for_each_entry(pcm, &codec->pcm_list_head, list)
pcm_count++;
- ret = avs_create_dai_links(card->dev, codec, pcm_count, mach->mach_params.platform, &links);
+ ret = avs_create_dai_links(card->dev, codec, pcm_count, &links);
if (ret < 0) {
dev_err(card->dev, "create links failed: %d\n", ret);
return ret;
@@ -197,7 +197,7 @@ static int avs_hdaudio_probe(struct platform_device *pdev)
if (!binder->codecs->name)
return -ENOMEM;
- binder->platforms->name = mach->mach_params.platform;
+ binder->platforms->name = dev_name(dev);
binder->num_platforms = 1;
binder->codecs->dai_name = "codec-probing-DAI";
binder->num_codecs = 1;
@@ -207,7 +207,10 @@ static int avs_hdaudio_probe(struct platform_device *pdev)
return -ENOMEM;
if (pdata->obsolete_card_names) {
- card->name = binder->codecs->name;
+ card->name = devm_kasprintf(dev, GFP_KERNEL, "hdaudioB%dD%d", codec->bus->core.idx,
+ codec->core.addr);
+ if (!card->name)
+ return -ENOMEM;
} else {
card->driver_name = "avs_hdaudio";
if (hda_codec_is_display(codec))
diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c
index f7b6d77..9a6b89f 100644
--- a/sound/soc/intel/avs/boards/i2s_test.c
+++ b/sound/soc/intel/avs/boards/i2s_test.c
@@ -14,8 +14,8 @@
#include <sound/soc-dapm.h>
#include "../utils.h"
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -25,8 +25,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -39,6 +37,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -59,11 +58,9 @@ static int avs_i2s_test_probe(struct platform_device *pdev)
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
if (!avs_mach_singular_ssp(mach)) {
@@ -94,7 +91,7 @@ static int avs_i2s_test_probe(struct platform_device *pdev)
if (!card->name)
return -ENOMEM;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d\n", ret);
return ret;
@@ -106,10 +103,6 @@ static int avs_i2s_test_probe(struct platform_device *pdev)
card->num_links = 1;
card->fully_routed = true;
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c
index 72053f8..e9a8780 100644
--- a/sound/soc/intel/avs/boards/max98357a.c
+++ b/sound/soc/intel/avs/boards/max98357a.c
@@ -46,8 +46,8 @@ avs_max98357a_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_pa
return 0;
}
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -57,8 +57,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -73,6 +71,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -96,18 +95,16 @@ static int avs_max98357a_probe(struct platform_device *pdev)
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -135,10 +132,6 @@ static int avs_max98357a_probe(struct platform_device *pdev)
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c
index cdba1c3..8b45b64 100644
--- a/sound/soc/intel/avs/boards/max98373.c
+++ b/sound/soc/intel/avs/boards/max98373.c
@@ -95,8 +95,8 @@ static const struct snd_soc_ops avs_max98373_ops = {
.hw_params = avs_max98373_hw_params,
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -106,8 +106,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -125,6 +123,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
!dl->codecs[1].name || !dl->codecs[1].dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 2;
dl->platforms = platform;
@@ -149,18 +148,16 @@ static int avs_max98373_probe(struct platform_device *pdev)
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -190,10 +187,6 @@ static int avs_max98373_probe(struct platform_device *pdev)
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c
index a68e227..db07312 100644
--- a/sound/soc/intel/avs/boards/max98927.c
+++ b/sound/soc/intel/avs/boards/max98927.c
@@ -92,8 +92,8 @@ static const struct snd_soc_ops avs_max98927_ops = {
.hw_params = avs_max98927_hw_params,
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -103,8 +103,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -122,6 +120,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
!dl->codecs[1].name || !dl->codecs[1].dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 2;
dl->platforms = platform;
@@ -146,18 +145,16 @@ static int avs_max98927_probe(struct platform_device *pdev)
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -187,10 +184,6 @@ static int avs_max98927_probe(struct platform_device *pdev)
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c
index 3fb1a5d..9ca400a 100644
--- a/sound/soc/intel/avs/boards/nau8825.c
+++ b/sound/soc/intel/avs/boards/nau8825.c
@@ -172,8 +172,8 @@ static const struct snd_soc_ops avs_nau8825_ops = {
.trigger = avs_nau8825_trigger,
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -183,8 +183,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -199,6 +197,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -250,18 +249,16 @@ static int avs_nau8825_probe(struct platform_device *pdev)
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -293,10 +290,6 @@ static int avs_nau8825_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c
index 06c1f19f..73884f8 100644
--- a/sound/soc/intel/avs/boards/probe.c
+++ b/sound/soc/intel/avs/boards/probe.c
@@ -9,45 +9,54 @@
#include <linux/device.h>
#include <linux/module.h>
#include <sound/soc.h>
-#include <sound/soc-acpi.h>
-SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
-SND_SOC_DAILINK_DEF(probe_cp, DAILINK_COMP_ARRAY(COMP_CPU("Probe Extraction CPU DAI")));
-SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("probe-platform")));
+static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **links, int *num_links)
+{
+ struct snd_soc_dai_link *dl;
-static struct snd_soc_dai_link probe_mb_dai_links[] = {
- {
- .name = "Compress Probe Capture",
- .nonatomic = 1,
- SND_SOC_DAILINK_REG(probe_cp, dummy, platform),
- },
-};
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ if (!dl)
+ return -ENOMEM;
+
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->platforms = devm_kzalloc(dev, sizeof(*dl->platforms), GFP_KERNEL);
+ if (!dl->cpus || !dl->platforms)
+ return -ENOMEM;
+
+ dl->name = "Compress Probe Capture";
+ dl->cpus->dai_name = "Probe Extraction CPU DAI";
+ dl->num_cpus = 1;
+ dl->codecs = &snd_soc_dummy_dlc;
+ dl->num_codecs = 1;
+ dl->platforms->name = dev_name(dev);
+ dl->num_platforms = 1;
+ dl->nonatomic = 1;
+
+ *links = dl;
+ *num_links = 1;
+ return 0;
+}
static int avs_probe_mb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
int ret;
- mach = dev_get_platdata(dev);
-
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
+ ret = avs_create_dai_links(dev, &card->dai_link, &card->num_links);
+ if (ret)
+ return ret;
+
card->driver_name = "avs_probe_mb";
card->long_name = card->name = "AVS PROBE";
card->dev = dev;
card->owner = THIS_MODULE;
- card->dai_link = probe_mb_dai_links;
- card->num_links = ARRAY_SIZE(probe_mb_dai_links);
card->fully_routed = true;
- ret = snd_soc_fixup_dai_links_platform_name(card, mach->mach_params.platform);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c
index ec53829..4055ecc 100644
--- a/sound/soc/intel/avs/boards/rt274.c
+++ b/sound/soc/intel/avs/boards/rt274.c
@@ -117,7 +117,7 @@ static int avs_rt274_codec_init(struct snd_soc_pcm_runtime *runtime)
return ret;
}
- card->dapm.idle_bias_off = true;
+ card->dapm.idle_bias = false;
return 0;
}
@@ -147,8 +147,8 @@ static int avs_rt274_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pc
return 0;
}
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -158,8 +158,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -174,6 +172,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -214,18 +213,16 @@ static int avs_rt274_probe(struct platform_device *pdev)
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -257,10 +254,6 @@ static int avs_rt274_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c
index 2566e97..4c9ac54 100644
--- a/sound/soc/intel/avs/boards/rt286.c
+++ b/sound/soc/intel/avs/boards/rt286.c
@@ -115,8 +115,8 @@ static const struct snd_soc_ops avs_rt286_ops = {
.hw_params = avs_rt286_hw_params,
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -126,8 +126,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -142,6 +140,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -183,18 +182,16 @@ static int avs_rt286_probe(struct platform_device *pdev)
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
@@ -227,10 +224,6 @@ static int avs_rt286_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c
index 7be34c8..2d7a774 100644
--- a/sound/soc/intel/avs/boards/rt298.c
+++ b/sound/soc/intel/avs/boards/rt298.c
@@ -132,8 +132,8 @@ static const struct snd_soc_ops avs_rt298_ops = {
.hw_params = avs_rt298_hw_params,
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -143,8 +143,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -159,6 +157,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -203,18 +202,16 @@ static int avs_rt298_probe(struct platform_device *pdev)
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -246,10 +243,6 @@ static int avs_rt298_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c
index 45f091f..00b99e36 100644
--- a/sound/soc/intel/avs/boards/rt5514.c
+++ b/sound/soc/intel/avs/boards/rt5514.c
@@ -84,8 +84,8 @@ static const struct snd_soc_ops avs_rt5514_ops = {
.hw_params = avs_rt5514_hw_params,
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -95,8 +95,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -111,6 +109,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -136,18 +135,16 @@ static int avs_rt5514_probe(struct platform_device *pdev)
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -173,10 +170,6 @@ static int avs_rt5514_probe(struct platform_device *pdev)
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c
index 706b84f..97d1fa9 100644
--- a/sound/soc/intel/avs/boards/rt5640.c
+++ b/sound/soc/intel/avs/boards/rt5640.c
@@ -67,7 +67,7 @@ static int avs_rt5640_codec_init(struct snd_soc_pcm_runtime *runtime)
return ret;
snd_soc_component_set_jack(codec_dai->component, jack, NULL);
- card->dapm.idle_bias_off = true;
+ card->dapm.idle_bias = false;
return 0;
}
diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c
index 5164880..68fea32 100644
--- a/sound/soc/intel/avs/boards/rt5663.c
+++ b/sound/soc/intel/avs/boards/rt5663.c
@@ -134,8 +134,8 @@ static const struct snd_soc_ops avs_rt5663_ops = {
};
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -145,8 +145,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -161,6 +159,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -202,18 +201,16 @@ static int avs_rt5663_probe(struct platform_device *pdev)
struct snd_soc_card *card;
struct rt5663_private *priv;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -245,10 +242,6 @@ static int avs_rt5663_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, priv);
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c
index 9677b9e..8186372 100644
--- a/sound/soc/intel/avs/boards/rt5682.c
+++ b/sound/soc/intel/avs/boards/rt5682.c
@@ -204,8 +204,8 @@ avs_rt5682_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_param
return 0;
}
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -215,8 +215,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -231,6 +229,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
@@ -272,7 +271,6 @@ static int avs_rt5682_probe(struct platform_device *pdev)
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
if (pdev->id_entry && pdev->id_entry->driver_data)
@@ -282,14 +280,13 @@ static int avs_rt5682_probe(struct platform_device *pdev)
dev_dbg(dev, "avs_rt5682_quirk = %lx\n", avs_rt5682_quirk);
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -321,10 +318,6 @@ static int avs_rt5682_probe(struct platform_device *pdev)
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c
index 3786eef..ae0e6e2 100644
--- a/sound/soc/intel/avs/boards/ssm4567.c
+++ b/sound/soc/intel/avs/boards/ssm4567.c
@@ -81,8 +81,8 @@ avs_ssm4567_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_para
return 0;
}
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
- int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
@@ -92,8 +92,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
if (!dl || !platform)
return -ENOMEM;
- platform->name = platform_name;
-
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -111,6 +109,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
!dl->codecs[1].name || !dl->codecs[1].dai_name)
return -ENOMEM;
+ platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 2;
dl->platforms = platform;
@@ -135,18 +134,16 @@ static int avs_ssm4567_probe(struct platform_device *pdev)
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
- const char *pname;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
- pname = mach->mach_params.platform;
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
- ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
@@ -176,10 +173,6 @@ static int avs_ssm4567_probe(struct platform_device *pdev)
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
- ret = snd_soc_fixup_dai_links_platform_name(card, pname);
- if (ret)
- return ret;
-
return devm_snd_soc_register_deferrable_card(dev, card);
}
diff --git a/sound/soc/intel/avs/cnl.c b/sound/soc/intel/avs/cnl.c
index 03f8fb0..5b5359e 100644
--- a/sound/soc/intel/avs/cnl.c
+++ b/sound/soc/intel/avs/cnl.c
@@ -8,6 +8,7 @@
#include <sound/hdaudio_ext.h>
#include "avs.h"
+#include "debug.h"
#include "messages.h"
#include "registers.h"
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index 5ebadba..6e0e655 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -27,6 +27,7 @@
#include "../../codecs/hda.h"
#include "avs.h"
#include "cldma.h"
+#include "debug.h"
#include "messages.h"
#include "pcm.h"
diff --git a/sound/soc/intel/avs/debug.h b/sound/soc/intel/avs/debug.h
new file mode 100644
index 0000000..94fe872
--- /dev/null
+++ b/sound/soc/intel/avs/debug.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2024-2025 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_DEBUG_H
+#define __SOUND_SOC_INTEL_AVS_DEBUG_H
+
+#include "messages.h"
+#include "registers.h"
+
+struct avs_dev;
+
+#define avs_log_buffer_size(adev) \
+ ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
+
+#define avs_log_buffer_addr(adev, core) \
+({ \
+ s32 __offset = avs_dsp_op(adev, log_buffer_offset, core); \
+ (__offset < 0) ? NULL : \
+ (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \
+})
+
+static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&adev->trace_lock, flags);
+ ret = avs_dsp_op(adev, log_buffer_status, msg);
+ spin_unlock_irqrestore(&adev->trace_lock, flags);
+
+ return ret;
+}
+
+struct avs_apl_log_buffer_layout {
+ u32 read_ptr;
+ u32 write_ptr;
+ u8 buffer[];
+} __packed;
+static_assert(sizeof(struct avs_apl_log_buffer_layout) == 8);
+
+#define avs_apl_log_payload_size(adev) \
+ (avs_log_buffer_size(adev) - sizeof(struct avs_apl_log_buffer_layout))
+
+#define avs_apl_log_payload_addr(addr) \
+ (addr + sizeof(struct avs_apl_log_buffer_layout))
+
+#ifdef CONFIG_DEBUG_FS
+int avs_register_probe_component(struct avs_dev *adev, const char *name);
+
+#define AVS_SET_ENABLE_LOGS_OP(name) \
+ .enable_logs = avs_##name##_enable_logs
+
+bool avs_logging_fw(struct avs_dev *adev);
+void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len);
+void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len);
+
+void avs_debugfs_init(struct avs_dev *adev);
+void avs_debugfs_exit(struct avs_dev *adev);
+
+#else
+static inline int avs_register_probe_component(struct avs_dev *adev, const char *name)
+{
+ return -EOPNOTSUPP;
+}
+
+#define AVS_SET_ENABLE_LOGS_OP(name)
+
+static inline bool avs_logging_fw(struct avs_dev *adev)
+{
+ return false;
+}
+
+static inline void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
+{
+}
+
+static inline void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src,
+ unsigned int len)
+{
+}
+
+static inline void avs_debugfs_init(struct avs_dev *adev) { }
+static inline void avs_debugfs_exit(struct avs_dev *adev) { }
+#endif
+
+#endif
diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c
index f508f21..3534de4 100644
--- a/sound/soc/intel/avs/debugfs.c
+++ b/sound/soc/intel/avs/debugfs.c
@@ -13,6 +13,7 @@
#include <linux/string_helpers.h>
#include <sound/soc.h>
#include "avs.h"
+#include "debug.h"
#include "messages.h"
static unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len)
diff --git a/sound/soc/intel/avs/icl.c b/sound/soc/intel/avs/icl.c
index f8d327e..d655e72 100644
--- a/sound/soc/intel/avs/icl.c
+++ b/sound/soc/intel/avs/icl.c
@@ -10,6 +10,7 @@
#include <sound/hdaudio.h>
#include <sound/hdaudio_ext.h>
#include "avs.h"
+#include "debug.h"
#include "messages.h"
#define ICL_VS_LTRP_GB_ICCMAX 95
diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
index 6bfb9d1..c0feb9e 100644
--- a/sound/soc/intel/avs/ipc.c
+++ b/sound/soc/intel/avs/ipc.c
@@ -10,6 +10,7 @@
#include <linux/slab.h>
#include <sound/hdaudio_ext.h>
#include "avs.h"
+#include "debug.h"
#include "messages.h"
#include "registers.h"
#include "trace.h"
diff --git a/sound/soc/intel/avs/lnl.c b/sound/soc/intel/avs/lnl.c
index 0320859..4fbc62b 100644
--- a/sound/soc/intel/avs/lnl.c
+++ b/sound/soc/intel/avs/lnl.c
@@ -8,6 +8,7 @@
#include <sound/hdaudio_ext.h>
#include "avs.h"
+#include "debug.h"
#include "registers.h"
int avs_lnl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
diff --git a/sound/soc/intel/avs/mtl.c b/sound/soc/intel/avs/mtl.c
index e7b7915..d8bdd03 100644
--- a/sound/soc/intel/avs/mtl.c
+++ b/sound/soc/intel/avs/mtl.c
@@ -8,6 +8,7 @@
#include <sound/hdaudio_ext.h>
#include "avs.h"
+#include "debug.h"
#include "registers.h"
#include "trace.h"
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index e8e6b1c..7aa20fc 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -115,6 +115,55 @@ avs_path_find_variant(struct avs_dev *adev,
return NULL;
}
+static struct avs_tplg_path *avs_condpath_find_variant(struct avs_dev *adev,
+ struct avs_tplg_path_template *template,
+ struct avs_path *source,
+ struct avs_path *sink)
+{
+ struct avs_tplg_path *variant;
+
+ list_for_each_entry(variant, &template->path_list, node) {
+ if (variant->source_path_id == source->template->id &&
+ variant->sink_path_id == sink->template->id)
+ return variant;
+ }
+
+ return NULL;
+}
+
+static bool avs_tplg_path_template_id_equal(struct avs_tplg_path_template_id *id,
+ struct avs_tplg_path_template_id *id2)
+{
+ return id->id == id2->id && !strcmp(id->tplg_name, id2->tplg_name);
+}
+
+static struct avs_path *avs_condpath_find_match(struct avs_dev *adev,
+ struct avs_tplg_path_template *template,
+ struct avs_path *path, int dir)
+{
+ struct avs_tplg_path_template_id *id, *id2;
+
+ if (dir) {
+ id = &template->source;
+ id2 = &template->sink;
+ } else {
+ id = &template->sink;
+ id2 = &template->source;
+ }
+
+ /* Check whether this path is either source or sink of condpath template. */
+ if (id->id != path->template->owner->id ||
+ strcmp(id->tplg_name, path->template->owner->owner->name))
+ return NULL;
+
+ /* Unidirectional condpaths are allowed. */
+ if (avs_tplg_path_template_id_equal(id, id2))
+ return path;
+
+ /* Now find the counterpart. */
+ return avs_path_find_path(adev, id2->tplg_name, id2->id);
+}
+
static struct acpi_nhlt_config *
avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t);
@@ -1051,6 +1100,10 @@ static int avs_path_init(struct avs_dev *adev, struct avs_path *path,
path->dma_id = dma_id;
INIT_LIST_HEAD(&path->ppl_list);
INIT_LIST_HEAD(&path->node);
+ INIT_LIST_HEAD(&path->source_list);
+ INIT_LIST_HEAD(&path->sink_list);
+ INIT_LIST_HEAD(&path->source_node);
+ INIT_LIST_HEAD(&path->sink_node);
/* create all the pipelines */
list_for_each_entry(tppl, &template->ppl_list, node) {
@@ -1134,12 +1187,129 @@ static struct avs_path *avs_path_create_unlocked(struct avs_dev *adev, u32 dma_i
return ERR_PTR(ret);
}
+static void avs_condpath_free(struct avs_dev *adev, struct avs_path *path)
+{
+ int ret;
+
+ list_del(&path->source_node);
+ list_del(&path->sink_node);
+
+ ret = avs_path_reset(path);
+ if (ret < 0)
+ dev_err(adev->dev, "reset condpath failed: %d\n", ret);
+
+ ret = avs_path_unbind(path);
+ if (ret < 0)
+ dev_err(adev->dev, "unbind condpath failed: %d\n", ret);
+
+ avs_path_free_unlocked(path);
+}
+
+static struct avs_path *avs_condpath_create(struct avs_dev *adev,
+ struct avs_tplg_path *template,
+ struct avs_path *source,
+ struct avs_path *sink)
+{
+ struct avs_path *path;
+ int ret;
+
+ path = avs_path_create_unlocked(adev, 0, template);
+ if (IS_ERR(path))
+ return path;
+
+ ret = avs_path_bind(path);
+ if (ret)
+ goto err_bind;
+
+ ret = avs_path_reset(path);
+ if (ret)
+ goto err_reset;
+
+ path->source = source;
+ path->sink = sink;
+ list_add_tail(&path->source_node, &source->source_list);
+ list_add_tail(&path->sink_node, &sink->sink_list);
+
+ return path;
+
+err_reset:
+ avs_path_unbind(path);
+err_bind:
+ avs_path_free_unlocked(path);
+ return ERR_PTR(ret);
+}
+
+static int avs_condpaths_walk(struct avs_dev *adev, struct avs_path *path, int dir)
+{
+ struct avs_soc_component *acomp;
+ struct avs_path *source, *sink;
+ struct avs_path **other;
+
+ if (dir) {
+ source = path;
+ other = &sink;
+ } else {
+ sink = path;
+ other = &source;
+ }
+
+ list_for_each_entry(acomp, &adev->comp_list, node) {
+ for (int i = 0; i < acomp->tplg->num_condpath_tmpls; i++) {
+ struct avs_tplg_path_template *template;
+ struct avs_tplg_path *variant;
+ struct avs_path *cpath;
+
+ template = &acomp->tplg->condpath_tmpls[i];
+
+ /* Do not create unidirectional condpaths twice. */
+ if (avs_tplg_path_template_id_equal(&template->source,
+ &template->sink) && dir)
+ continue;
+
+ *other = avs_condpath_find_match(adev, template, path, dir);
+ if (!*other)
+ continue;
+
+ variant = avs_condpath_find_variant(adev, template, source, sink);
+ if (!variant)
+ continue;
+
+ cpath = avs_condpath_create(adev, variant, source, sink);
+ if (IS_ERR(cpath))
+ return PTR_ERR(cpath);
+ }
+ }
+
+ return 0;
+}
+
+/* Caller responsible for holding adev->path_mutex. */
+static int avs_condpaths_walk_all(struct avs_dev *adev, struct avs_path *path)
+{
+ int ret;
+
+ ret = avs_condpaths_walk(adev, path, SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ return ret;
+
+ return avs_condpaths_walk(adev, path, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
void avs_path_free(struct avs_path *path)
{
+ struct avs_path *cpath, *csave;
struct avs_dev *adev = path->owner;
mutex_lock(&adev->path_mutex);
+
+ /* Free all condpaths this path spawned. */
+ list_for_each_entry_safe(cpath, csave, &path->source_list, source_node)
+ avs_condpath_free(path->owner, cpath);
+ list_for_each_entry_safe(cpath, csave, &path->sink_list, sink_node)
+ avs_condpath_free(path->owner, cpath);
+
avs_path_free_unlocked(path);
+
mutex_unlock(&adev->path_mutex);
}
@@ -1150,6 +1320,7 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
{
struct avs_tplg_path *variant;
struct avs_path *path;
+ int ret;
variant = avs_path_find_variant(adev, template, fe_params, be_params);
if (!variant) {
@@ -1163,7 +1334,16 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
mutex_lock(&adev->comp_list_mutex);
path = avs_path_create_unlocked(adev, dma_id, variant);
+ if (IS_ERR(path))
+ goto exit;
+ ret = avs_condpaths_walk_all(adev, path);
+ if (ret) {
+ avs_path_free_unlocked(path);
+ path = ERR_PTR(ret);
+ }
+
+exit:
mutex_unlock(&adev->comp_list_mutex);
mutex_unlock(&adev->path_mutex);
@@ -1286,6 +1466,42 @@ int avs_path_reset(struct avs_path *path)
return 0;
}
+static int avs_condpath_pause(struct avs_dev *adev, struct avs_path *cpath)
+{
+ struct avs_path_pipeline *ppl;
+ int ret;
+
+ if (cpath->state == AVS_PPL_STATE_PAUSED)
+ return 0;
+
+ list_for_each_entry_reverse(ppl, &cpath->ppl_list, node) {
+ ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, AVS_PPL_STATE_PAUSED);
+ if (ret) {
+ dev_err(adev->dev, "pause cpath failed: %d\n", ret);
+ cpath->state = AVS_PPL_STATE_INVALID;
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ cpath->state = AVS_PPL_STATE_PAUSED;
+ return 0;
+}
+
+static void avs_condpaths_pause(struct avs_dev *adev, struct avs_path *path)
+{
+ struct avs_path *cpath;
+
+ mutex_lock(&adev->path_mutex);
+
+ /* If either source or sink stops, so do the attached conditional paths. */
+ list_for_each_entry(cpath, &path->source_list, source_node)
+ avs_condpath_pause(adev, cpath);
+ list_for_each_entry(cpath, &path->sink_list, sink_node)
+ avs_condpath_pause(adev, cpath);
+
+ mutex_unlock(&adev->path_mutex);
+}
+
int avs_path_pause(struct avs_path *path)
{
struct avs_path_pipeline *ppl;
@@ -1295,6 +1511,8 @@ int avs_path_pause(struct avs_path *path)
if (path->state == AVS_PPL_STATE_PAUSED)
return 0;
+ avs_condpaths_pause(adev, path);
+
list_for_each_entry_reverse(ppl, &path->ppl_list, node) {
ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
AVS_PPL_STATE_PAUSED);
@@ -1309,6 +1527,50 @@ int avs_path_pause(struct avs_path *path)
return 0;
}
+static int avs_condpath_run(struct avs_dev *adev, struct avs_path *cpath, int trigger)
+{
+ struct avs_path_pipeline *ppl;
+ int ret;
+
+ if (cpath->state == AVS_PPL_STATE_RUNNING)
+ return 0;
+
+ list_for_each_entry(ppl, &cpath->ppl_list, node) {
+ if (ppl->template->cfg->trigger != trigger)
+ continue;
+
+ ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, AVS_PPL_STATE_RUNNING);
+ if (ret) {
+ dev_err(adev->dev, "run cpath failed: %d\n", ret);
+ cpath->state = AVS_PPL_STATE_INVALID;
+ return AVS_IPC_RET(ret);
+ }
+ }
+
+ cpath->state = AVS_PPL_STATE_RUNNING;
+ return 0;
+}
+
+static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int trigger)
+{
+ struct avs_path *cpath;
+
+ mutex_lock(&adev->path_mutex);
+
+ /* Run conditional paths only if source and sink are both running. */
+ list_for_each_entry(cpath, &path->source_list, source_node)
+ if (cpath->source->state == AVS_PPL_STATE_RUNNING &&
+ cpath->sink->state == AVS_PPL_STATE_RUNNING)
+ avs_condpath_run(adev, cpath, trigger);
+
+ list_for_each_entry(cpath, &path->sink_list, sink_node)
+ if (cpath->source->state == AVS_PPL_STATE_RUNNING &&
+ cpath->sink->state == AVS_PPL_STATE_RUNNING)
+ avs_condpath_run(adev, cpath, trigger);
+
+ mutex_unlock(&adev->path_mutex);
+}
+
int avs_path_run(struct avs_path *path, int trigger)
{
struct avs_path_pipeline *ppl;
@@ -1332,5 +1594,10 @@ int avs_path_run(struct avs_path *path, int trigger)
}
path->state = AVS_PPL_STATE_RUNNING;
+
+ /* Granular pipeline triggering not intended for conditional paths. */
+ if (trigger == AVS_TPLG_TRIGGER_AUTO)
+ avs_condpaths_run(adev, path, trigger);
+
return 0;
}
diff --git a/sound/soc/intel/avs/path.h b/sound/soc/intel/avs/path.h
index c65ed84..ceb8997 100644
--- a/sound/soc/intel/avs/path.h
+++ b/sound/soc/intel/avs/path.h
@@ -13,11 +13,24 @@
#include "avs.h"
#include "topology.h"
+#define AVS_COND_TYPE_NONE 0
+#define AVS_COND_TYPE_AECREF 1
+
struct avs_path {
u32 dma_id;
struct list_head ppl_list;
u32 state;
+ /* condpath navigation for standard paths */
+ struct list_head source_list;
+ struct list_head sink_list;
+
+ /* conditional path fields */
+ struct avs_path *source;
+ struct avs_path *sink;
+ struct list_head source_node;
+ struct list_head sink_node;
+
struct avs_tplg_path *template;
struct avs_dev *owner;
/* device path management */
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index 67ce667..d31058e 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -1379,9 +1379,9 @@ static struct snd_soc_component_driver avs_component_driver = {
.topology_name_prefix = "intel/avs",
};
-int avs_soc_component_register(struct device *dev, const char *name,
- struct snd_soc_component_driver *drv,
- struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
+int avs_register_component(struct device *dev, const char *name,
+ struct snd_soc_component_driver *drv,
+ struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
{
struct avs_soc_component *acomp;
int ret;
@@ -1390,16 +1390,18 @@ int avs_soc_component_register(struct device *dev, const char *name,
if (!acomp)
return -ENOMEM;
- ret = snd_soc_component_initialize(&acomp->base, drv, dev);
- if (ret < 0)
- return ret;
+ acomp->base.name = devm_kstrdup(dev, name, GFP_KERNEL);
+ if (!acomp->base.name)
+ return -ENOMEM;
- /* force name change after ASoC is done with its init */
- acomp->base.name = name;
INIT_LIST_HEAD(&acomp->node);
drv->use_dai_pcm_id = !obsolete_card_names;
+ ret = snd_soc_component_initialize(&acomp->base, drv, dev);
+ if (ret < 0)
+ return ret;
+
return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais);
}
@@ -1426,7 +1428,7 @@ static struct snd_soc_dai_driver dmic_cpu_dais[] = {
},
};
-int avs_dmic_platform_register(struct avs_dev *adev, const char *name)
+int avs_register_dmic_component(struct avs_dev *adev, const char *name)
{
const struct snd_soc_dai_ops *ops;
@@ -1437,8 +1439,8 @@ int avs_dmic_platform_register(struct avs_dev *adev, const char *name)
dmic_cpu_dais[0].ops = ops;
dmic_cpu_dais[1].ops = ops;
- return avs_soc_component_register(adev->dev, name, &avs_component_driver, dmic_cpu_dais,
- ARRAY_SIZE(dmic_cpu_dais));
+ return avs_register_component(adev->dev, name, &avs_component_driver, dmic_cpu_dais,
+ ARRAY_SIZE(dmic_cpu_dais));
}
static const struct snd_soc_dai_driver i2s_dai_template = {
@@ -1470,8 +1472,8 @@ static const struct snd_soc_dai_driver i2s_dai_template = {
},
};
-int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
- unsigned long *tdms)
+int avs_register_i2s_component(struct avs_dev *adev, const char *name, unsigned long port_mask,
+ unsigned long *tdms)
{
struct snd_soc_dai_driver *cpus, *dai;
const struct snd_soc_dai_ops *ops;
@@ -1537,7 +1539,7 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l
}
plat_register:
- return avs_soc_component_register(adev->dev, name, &avs_component_driver, cpus, cpu_count);
+ return avs_register_component(adev->dev, name, &avs_component_driver, cpus, cpu_count);
}
/* HD-Audio CPU DAI template */
@@ -1762,8 +1764,7 @@ static struct snd_soc_component_driver avs_hda_component_driver = {
.topology_name_prefix = "intel/avs",
};
-int avs_hda_platform_register(struct avs_dev *adev, const char *name)
+int avs_register_hda_component(struct avs_dev *adev, const char *name)
{
- return avs_soc_component_register(adev->dev, name,
- &avs_hda_component_driver, NULL, 0);
+ return avs_register_component(adev->dev, name, &avs_hda_component_driver, NULL, 0);
}
diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c
index a42736b..c7b7000 100644
--- a/sound/soc/intel/avs/probes.c
+++ b/sound/soc/intel/avs/probes.c
@@ -11,6 +11,7 @@
#include <sound/hdaudio.h>
#include <sound/soc.h>
#include "avs.h"
+#include "debug.h"
#include "messages.h"
static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id node_id,
@@ -284,14 +285,28 @@ static struct snd_soc_dai_driver probe_cpu_dais[] = {
},
};
-static struct snd_soc_component_driver avs_probe_component_driver = {
+static const struct snd_soc_component_driver avs_probe_component_driver = {
.name = "avs-probe-compr",
.compress_ops = &avs_probe_compress_ops,
.module_get_upon_open = 1, /* increment refcount when a stream is opened */
};
-int avs_probe_platform_register(struct avs_dev *adev, const char *name)
+int avs_register_probe_component(struct avs_dev *adev, const char *name)
{
- return avs_soc_component_register(adev->dev, name, &avs_probe_component_driver,
- probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais));
+ struct snd_soc_component *component;
+ int ret;
+
+ component = devm_kzalloc(adev->dev, sizeof(*component), GFP_KERNEL);
+ if (!component)
+ return -ENOMEM;
+
+ component->name = devm_kstrdup(adev->dev, name, GFP_KERNEL);
+ if (!component->name)
+ return -ENOMEM;
+
+ ret = snd_soc_component_initialize(component, &avs_probe_component_driver, adev->dev);
+ if (ret)
+ return ret;
+
+ return snd_soc_add_component(component, probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais));
}
diff --git a/sound/soc/intel/avs/ptl.c b/sound/soc/intel/avs/ptl.c
index 2be4b54..07da9b0 100644
--- a/sound/soc/intel/avs/ptl.c
+++ b/sound/soc/intel/avs/ptl.c
@@ -8,6 +8,7 @@
#include <sound/hdaudio_ext.h>
#include "avs.h"
+#include "debug.h"
#include "registers.h"
#include "trace.h"
diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c
index d66ef00..8fb86f3 100644
--- a/sound/soc/intel/avs/skl.c
+++ b/sound/soc/intel/avs/skl.c
@@ -11,6 +11,7 @@
#include <sound/hdaudio_ext.h>
#include "avs.h"
#include "cldma.h"
+#include "debug.h"
#include "messages.h"
#include "registers.h"
diff --git a/sound/soc/intel/avs/tgl.c b/sound/soc/intel/avs/tgl.c
index 9dbb3ad..afb0665 100644
--- a/sound/soc/intel/avs/tgl.c
+++ b/sound/soc/intel/avs/tgl.c
@@ -8,6 +8,7 @@
#include <linux/pci.h>
#include "avs.h"
+#include "debug.h"
#include "messages.h"
#define CPUID_TSC_LEAF 0x15
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index f2e4ad8..dfe8cf5 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -1387,6 +1387,27 @@ static const struct avs_tplg_token_parser path_parsers[] = {
},
};
+static const struct avs_tplg_token_parser condpath_parsers[] = {
+ {
+ .token = AVS_TKN_CONDPATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_SOURCE_PATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, source_path_id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_SINK_PATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, sink_path_id),
+ .parse = avs_parse_word_token,
+ },
+};
+
static struct avs_tplg_path *
avs_tplg_path_create(struct snd_soc_component *comp, struct avs_tplg_path_template *owner,
struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
@@ -1454,6 +1475,39 @@ static const struct avs_tplg_token_parser path_tmpl_parsers[] = {
},
};
+static const struct avs_tplg_token_parser condpath_tmpl_parsers[] = {
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SOURCE_TPLG_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg_path_template, source.tplg_name),
+ .parse = avs_parse_string_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SOURCE_PATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, source.id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SINK_TPLG_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg_path_template, sink.tplg_name),
+ .parse = avs_parse_string_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SINK_PATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, sink.id),
+ .parse = avs_parse_word_token,
+ },
+};
+
static int parse_path_template(struct snd_soc_component *comp,
struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
struct avs_tplg_path_template *template,
@@ -1524,6 +1578,56 @@ avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *o
return template;
}
+static int avs_tplg_parse_condpath_templates(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+ int ret, i;
+
+ ret = parse_dictionary_header(comp, tuples, (void **)&tplg->condpath_tmpls,
+ &tplg->num_condpath_tmpls,
+ sizeof(*tplg->condpath_tmpls),
+ AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32);
+ if (ret)
+ return ret;
+
+ block_size -= le32_to_cpu(tuples->size);
+ /* With header parsed, move on to parsing entries. */
+ tuples = avs_tplg_vendor_array_next(tuples);
+
+ for (i = 0; i < tplg->num_condpath_tmpls; i++) {
+ struct avs_tplg_path_template *template;
+ u32 esize;
+
+ template = &tplg->condpath_tmpls[i];
+ template->owner = tplg; /* Used when building sysfs hierarchy. */
+ INIT_LIST_HEAD(&template->path_list);
+ INIT_LIST_HEAD(&template->node);
+
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_CONDPATH_TMPL_ID_U32, &esize);
+ if (ret)
+ return ret;
+
+ ret = parse_path_template(comp, tuples, esize, template,
+ condpath_tmpl_parsers,
+ ARRAY_SIZE(condpath_tmpl_parsers),
+ condpath_parsers,
+ ARRAY_SIZE(condpath_parsers));
+ if (ret < 0) {
+ dev_err(comp->dev, "parse condpath_tmpl: %d failed: %d\n", i, ret);
+ return ret;
+ }
+
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ }
+
+ return 0;
+}
+
static const struct avs_tplg_token_parser mod_init_config_parsers[] = {
{
.token = AVS_TKN_INIT_CONFIG_ID_U32,
@@ -1891,6 +1995,12 @@ static int avs_manifest(struct snd_soc_component *comp, int index,
return ret;
}
+ /* Condpaths dictionary. */
+ ret = avs_tplg_parse_condpath_templates(comp, tuples,
+ has_init_config ? offset : remaining);
+ if (ret < 0)
+ return ret;
+
if (!has_init_config)
return 0;
diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h
index f5601a4..1e83fcc 100644
--- a/sound/soc/intel/avs/topology.h
+++ b/sound/soc/intel/avs/topology.h
@@ -33,6 +33,7 @@ struct avs_tplg {
u32 num_pplcfgs;
struct avs_tplg_binding *bindings;
u32 num_bindings;
+ struct avs_tplg_path_template *condpath_tmpls;
u32 num_condpath_tmpls;
struct avs_tplg_init_config *init_configs;
u32 num_init_configs;
@@ -155,6 +156,10 @@ struct avs_tplg_path_template {
struct snd_soc_dapm_widget *w;
+ /* Conditional path. */
+ struct avs_tplg_path_template_id source;
+ struct avs_tplg_path_template_id sink;
+
struct list_head path_list;
struct avs_tplg *owner;
@@ -176,6 +181,9 @@ struct avs_tplg_path {
/* Path format requirements. */
struct avs_audio_format *fe_fmt;
struct avs_audio_format *be_fmt;
+ /* Condpath path-variant requirements. */
+ u32 source_path_id;
+ u32 sink_path_id;
struct list_head ppl_list;
diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c
index 68a3d34..27b63a8 100644
--- a/sound/soc/intel/boards/bytcht_cx2072x.c
+++ b/sound/soc/intel/boards/bytcht_cx2072x.c
@@ -77,7 +77,7 @@ static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd)
byt_cht_cx2072x_acpi_gpios))
dev_warn(rtd->dev, "Unable to add GPIO mapping table\n");
- card->dapm.idle_bias_off = true;
+ card->dapm.idle_bias = false;
/* set the default PLL rate, the clock is handled by the codec driver */
ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), CX2072X_MCLK_EXTERNAL_PLL,
diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c
index 62594e7..3b5f631 100644
--- a/sound/soc/intel/boards/bytcht_es8316.c
+++ b/sound/soc/intel/boards/bytcht_es8316.c
@@ -47,7 +47,8 @@ enum {
BYT_CHT_ES8316_INTMIC_IN2_MAP,
};
-#define BYT_CHT_ES8316_MAP(quirk) ((quirk) & GENMASK(3, 0))
+#define BYT_CHT_ES8316_MAP_MASK GENMASK(3, 0)
+#define BYT_CHT_ES8316_MAP(quirk) ((quirk) & BYT_CHT_ES8316_MAP_MASK)
#define BYT_CHT_ES8316_SSP0 BIT(16)
#define BYT_CHT_ES8316_MONO_SPEAKER BIT(17)
#define BYT_CHT_ES8316_JD_INVERTED BIT(18)
@@ -60,10 +61,23 @@ MODULE_PARM_DESC(quirk, "Board-specific quirk override");
static void log_quirks(struct device *dev)
{
- if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN1_MAP)
+ int map;
+
+ map = BYT_CHT_ES8316_MAP(quirk);
+ switch (map) {
+ case BYT_CHT_ES8316_INTMIC_IN1_MAP:
dev_info(dev, "quirk IN1_MAP enabled");
- if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN2_MAP)
+ break;
+ case BYT_CHT_ES8316_INTMIC_IN2_MAP:
dev_info(dev, "quirk IN2_MAP enabled");
+ break;
+ default:
+ dev_warn_once(dev, "quirk sets invalid input map: 0x%x, default to INTMIC_IN1_MAP\n", map);
+ quirk &= ~BYT_CHT_ES8316_MAP_MASK;
+ quirk |= BYT_CHT_ES8316_INTMIC_IN1_MAP;
+ break;
+ }
+
if (quirk & BYT_CHT_ES8316_SSP0)
dev_info(dev, "quirk SSP0 enabled");
if (quirk & BYT_CHT_ES8316_MONO_SPEAKER)
@@ -165,7 +179,7 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
int num_routes;
int ret;
- card->dapm.idle_bias_off = true;
+ card->dapm.idle_bias = false;
switch (BYT_CHT_ES8316_MAP(quirk)) {
case BYT_CHT_ES8316_INTMIC_IN1_MAP:
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index 0f3b8f4..1e9b1903 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -68,7 +68,8 @@ enum {
BYT_RT5640_OVCD_SF_1P5 = (RT5640_OVCD_SF_1P5 << 13),
};
-#define BYT_RT5640_MAP(quirk) ((quirk) & GENMASK(3, 0))
+#define BYT_RT5640_MAP_MASK GENMASK(3, 0)
+#define BYT_RT5640_MAP(quirk) ((quirk) & BYT_RT5640_MAP_MASK)
#define BYT_RT5640_JDSRC(quirk) (((quirk) & GENMASK(7, 4)) >> 4)
#define BYT_RT5640_OVCD_TH(quirk) (((quirk) & GENMASK(12, 8)) >> 8)
#define BYT_RT5640_OVCD_SF(quirk) (((quirk) & GENMASK(14, 13)) >> 13)
@@ -140,7 +141,9 @@ static void log_quirks(struct device *dev)
dev_info(dev, "quirk NO_INTERNAL_MIC_MAP enabled\n");
break;
default:
- dev_err(dev, "quirk map 0x%x is not supported, microphone input will not work\n", map);
+ dev_warn_once(dev, "quirk sets invalid input map: 0x%x, default to DMIC1_MAP\n", map);
+ byt_rt5640_quirk &= ~BYT_RT5640_MAP_MASK;
+ byt_rt5640_quirk |= BYT_RT5640_DMIC1_MAP;
break;
}
if (byt_rt5640_quirk & BYT_RT5640_HSMIC2_ON_IN1)
@@ -1321,7 +1324,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
int num_routes = 0;
int ret;
- card->dapm.idle_bias_off = true;
+ card->dapm.idle_bias = false;
jack_data->use_platform_clock = true;
/* Start with RC clk for jack-detect (we disable MCLK below) */
diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
index 67c6284..ca540a6 100644
--- a/sound/soc/intel/boards/bytcr_rt5651.c
+++ b/sound/soc/intel/boards/bytcr_rt5651.c
@@ -58,7 +58,8 @@ enum {
BYT_RT5651_OVCD_SF_1P5 = (RT5651_OVCD_SF_1P5 << 13),
};
-#define BYT_RT5651_MAP(quirk) ((quirk) & GENMASK(3, 0))
+#define BYT_RT5651_MAP_MASK GENMASK(3, 0)
+#define BYT_RT5651_MAP(quirk) ((quirk) & BYT_RT5651_MAP_MASK)
#define BYT_RT5651_JDSRC(quirk) (((quirk) & GENMASK(7, 4)) >> 4)
#define BYT_RT5651_OVCD_TH(quirk) (((quirk) & GENMASK(12, 8)) >> 8)
#define BYT_RT5651_OVCD_SF(quirk) (((quirk) & GENMASK(14, 13)) >> 13)
@@ -100,14 +101,29 @@ MODULE_PARM_DESC(quirk, "Board-specific quirk override");
static void log_quirks(struct device *dev)
{
- if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_DMIC_MAP)
+ int map;
+
+ map = BYT_RT5651_MAP(byt_rt5651_quirk);
+ switch (map) {
+ case BYT_RT5651_DMIC_MAP:
dev_info(dev, "quirk DMIC_MAP enabled");
- if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_MAP)
+ break;
+ case BYT_RT5651_IN1_MAP:
dev_info(dev, "quirk IN1_MAP enabled");
- if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN2_MAP)
+ break;
+ case BYT_RT5651_IN2_MAP:
dev_info(dev, "quirk IN2_MAP enabled");
- if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_IN2_MAP)
+ break;
+ case BYT_RT5651_IN1_IN2_MAP:
dev_info(dev, "quirk IN1_IN2_MAP enabled");
+ break;
+ default:
+ dev_warn_once(dev, "quirk sets invalid input map: 0x%x, default to DMIC_MAP\n", map);
+ byt_rt5651_quirk &= ~BYT_RT5651_MAP_MASK;
+ byt_rt5651_quirk |= BYT_RT5651_DMIC_MAP;
+ break;
+ }
+
if (BYT_RT5651_JDSRC(byt_rt5651_quirk)) {
dev_info(dev, "quirk realtek,jack-detect-source %ld\n",
BYT_RT5651_JDSRC(byt_rt5651_quirk));
@@ -570,7 +586,7 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
int report;
int ret;
- card->dapm.idle_bias_off = true;
+ card->dapm.idle_bias = false;
/* Start with RC clk for jack-detect (we disable MCLK below) */
if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN)
diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c
index a6dfbcf..72c0e59 100644
--- a/sound/soc/intel/boards/bytcr_wm5102.c
+++ b/sound/soc/intel/boards/bytcr_wm5102.c
@@ -288,7 +288,7 @@ static int byt_wm5102_init(struct snd_soc_pcm_runtime *runtime)
const struct snd_soc_dapm_route *custom_map = NULL;
int ret, jack_type, num_routes = 0;
- card->dapm.idle_bias_off = true;
+ card->dapm.idle_bias = false;
ret = snd_soc_add_card_controls(card, byt_wm5102_controls,
ARRAY_SIZE(byt_wm5102_controls));
diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c
index 1211a2b..10b189e 100644
--- a/sound/soc/intel/boards/sof_es8336.c
+++ b/sound/soc/intel/boards/sof_es8336.c
@@ -276,7 +276,7 @@ static int sof_es8316_init(struct snd_soc_pcm_runtime *runtime)
int num_routes;
int ret;
- card->dapm.idle_bias_off = true;
+ card->dapm.idle_bias = false;
if (quirk & SOC_ES8336_HEADSET_MIC1) {
custom_map = sof_es8316_headset_mic1_map;
diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.c b/sound/soc/mediatek/common/mtk-soundcard-driver.c
index 95a0839..a2a30a8 100644
--- a/sound/soc/mediatek/common/mtk-soundcard-driver.c
+++ b/sound/soc/mediatek/common/mtk-soundcard-driver.c
@@ -89,40 +89,31 @@ static int set_dailink_daifmt(struct snd_soc_card *card,
int parse_dai_link_info(struct snd_soc_card *card)
{
struct device *dev = card->dev;
- struct device_node *sub_node;
struct snd_soc_dai_link *dai_link;
const char *dai_link_name;
int ret, i;
/* Loop over all the dai link sub nodes */
- for_each_available_child_of_node(dev->of_node, sub_node) {
+ for_each_available_child_of_node_scoped(dev->of_node, sub_node) {
if (of_property_read_string(sub_node, "link-name",
- &dai_link_name)) {
- of_node_put(sub_node);
+ &dai_link_name))
return -EINVAL;
- }
for_each_card_prelinks(card, i, dai_link) {
if (!strcmp(dai_link_name, dai_link->name))
break;
}
- if (i >= card->num_links) {
- of_node_put(sub_node);
+ if (i >= card->num_links)
return -EINVAL;
- }
ret = set_card_codec_info(card, sub_node, dai_link);
- if (ret < 0) {
- of_node_put(sub_node);
+ if (ret < 0)
return ret;
- }
ret = set_dailink_daifmt(card, sub_node, dai_link);
- if (ret < 0) {
- of_node_put(sub_node);
+ if (ret < 0)
return ret;
- }
}
return 0;
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
index 7d6a358..3d6d7bc 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -159,7 +159,7 @@ static int mt8173_rt5650_hdmi_init(struct snd_soc_pcm_runtime *rtd)
{
int ret;
- ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT,
&mt8173_rt5650_hdmi_jack);
if (ret)
return ret;
diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
index 3388e07..983f3b9 100644
--- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
@@ -378,7 +378,7 @@ static int mt8183_da7219_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_card_get_drvdata(rtd->card);
int ret;
- ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT,
&priv->hdmi_jack);
if (ret)
return ret;
diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
index 497a904..0bc1f11 100644
--- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
@@ -383,7 +383,7 @@ mt8183_mt6358_ts3a227_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_card_get_drvdata(rtd->card);
int ret;
- ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT,
&priv->hdmi_jack);
if (ret)
return ret;
diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366.c b/sound/soc/mediatek/mt8186/mt8186-mt6366.c
index 4354601..45df698 100644
--- a/sound/soc/mediatek/mt8186/mt8186-mt6366.c
+++ b/sound/soc/mediatek/mt8186/mt8186-mt6366.c
@@ -362,7 +362,7 @@ static int mt8186_mt6366_rt1019_rt5682s_hdmi_init(struct snd_soc_pcm_runtime *rt
return ret;
}
- ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, jack);
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, jack);
if (ret) {
dev_err(rtd->dev, "HDMI Jack creation failed: %d\n", ret);
return ret;
diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
index ea814a0..c6e7461 100644
--- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c
+++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
@@ -250,14 +250,14 @@ enum mt8188_jacks {
static struct snd_soc_jack_pin mt8188_hdmi_jack_pins[] = {
{
.pin = "HDMI",
- .mask = SND_JACK_LINEOUT,
+ .mask = SND_JACK_AVOUT,
},
};
static struct snd_soc_jack_pin mt8188_dp_jack_pins[] = {
{
.pin = "DP",
- .mask = SND_JACK_LINEOUT,
+ .mask = SND_JACK_AVOUT,
},
};
@@ -638,7 +638,7 @@ static int mt8188_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
int ret = 0;
ret = snd_soc_card_jack_new_pins(rtd->card, "HDMI Jack",
- SND_JACK_LINEOUT, jack,
+ SND_JACK_AVOUT, jack,
mt8188_hdmi_jack_pins,
ARRAY_SIZE(mt8188_hdmi_jack_pins));
if (ret) {
@@ -663,7 +663,7 @@ static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
int ret = 0;
- ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_LINEOUT,
+ ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_AVOUT,
jack, mt8188_dp_jack_pins,
ARRAY_SIZE(mt8188_dp_jack_pins));
if (ret) {
diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
index bf483a8..91c5776 100644
--- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
+++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
@@ -368,7 +368,7 @@ static int mt8192_mt6359_hdmi_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_rtd_to_codec(rtd, 0)->component;
int ret;
- ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, jack);
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, jack);
if (ret) {
dev_err(rtd->dev, "HDMI Jack creation failed: %d\n", ret);
return ret;
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
index e57391c..7b96c84 100644
--- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c
+++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
@@ -360,7 +360,7 @@ static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_rtd_to_codec(rtd, 0)->component;
int ret;
- ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT, jack);
+ ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_AVOUT, jack);
if (ret)
return ret;
@@ -375,7 +375,7 @@ static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_rtd_to_codec(rtd, 0)->component;
int ret;
- ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, jack);
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, jack);
if (ret)
return ret;
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index e026f99..e54abcd 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -3,7 +3,7 @@
config SND_PXA2XX_SOC
tristate "SoC Audio for the Intel PXA2xx chip"
- depends on ARCH_PXA || COMPILE_TEST
+ depends on ARCH_PXA || (COMPILE_TEST && GPIOLIB_LEGACY)
select SND_PXA2XX_LIB
help
Say Y or M if you want to add support for codecs attached to
@@ -26,7 +26,7 @@
config SND_PXA_SOC_SSP
tristate "Soc Audio via PXA2xx/PXA3xx SSP ports"
- depends on PLAT_PXA
+ depends on ARCH_PXA
select PXA_SSP
select SND_PXA2XX_LIB
diff --git a/sound/soc/qcom/lpass-cdc-dma.c b/sound/soc/qcom/lpass-cdc-dma.c
index 8106c58..2dc8c75 100644
--- a/sound/soc/qcom/lpass-cdc-dma.c
+++ b/sound/soc/qcom/lpass-cdc-dma.c
@@ -217,8 +217,9 @@ static int lpass_cdc_dma_daiops_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
struct lpaif_dmactl *dmactl = NULL;
- unsigned int ret, regval;
+ unsigned int regval;
unsigned int channels = params_channels(params);
+ int ret;
int id;
switch (channels) {
diff --git a/sound/soc/qcom/lpass-hdmi.c b/sound/soc/qcom/lpass-hdmi.c
index ce753eb..6d97953 100644
--- a/sound/soc/qcom/lpass-hdmi.c
+++ b/sound/soc/qcom/lpass-hdmi.c
@@ -23,7 +23,6 @@ static int lpass_hdmi_daiops_hw_params(struct snd_pcm_substream *substream,
snd_pcm_format_t format = params_format(params);
unsigned int rate = params_rate(params);
unsigned int channels = params_channels(params);
- unsigned int ret;
int bitwidth;
unsigned int word_length;
unsigned int ch_sts_buf0;
@@ -33,6 +32,7 @@ static int lpass_hdmi_daiops_hw_params(struct snd_pcm_substream *substream,
unsigned int ch = 0;
struct lpass_dp_metadata_ctl *meta_ctl = drvdata->meta_ctl;
struct lpass_sstream_ctl *sstream_ctl = drvdata->sstream_ctl;
+ int ret;
bitwidth = snd_pcm_format_width(format);
if (bitwidth < 0) {
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
index 4ebaaf7..bbfd51d 100644
--- a/sound/soc/qcom/qdsp6/audioreach.c
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -811,6 +811,30 @@ static int audioreach_gapless_set_media_format(struct q6apm_graph *graph,
EARLY_EOS_DELAY_MS);
}
+static int audioreach_set_module_config(struct q6apm_graph *graph,
+ struct audioreach_module *module,
+ struct audioreach_module_config *cfg)
+{
+ int payload_size = le32_to_cpu(module->data->size);
+ struct gpr_pkt *pkt;
+ int rc;
+ void *p;
+
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ memcpy(p, module->data->data, payload_size);
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
struct audioreach_module *module,
struct audioreach_module_config *cfg)
@@ -1247,6 +1271,9 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod
case MODULE_ID_DISPLAY_PORT_SINK:
rc = audioreach_display_port_set_media_format(graph, module, cfg);
break;
+ case MODULE_ID_SMECNS_V2:
+ rc = audioreach_set_module_config(graph, module, cfg);
+ break;
case MODULE_ID_I2S_SOURCE:
case MODULE_ID_I2S_SINK:
rc = audioreach_i2s_set_media_format(graph, module, cfg);
diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
index 61a69df..790fba9 100644
--- a/sound/soc/qcom/qdsp6/audioreach.h
+++ b/sound/soc/qcom/qdsp6/audioreach.h
@@ -4,6 +4,7 @@
#define __AUDIOREACH_H__
#include <linux/types.h>
#include <linux/soc/qcom/apr.h>
+#include <uapi/sound/snd_ar_tokens.h>
#include <sound/soc.h>
struct q6apm;
struct q6apm_graph;
@@ -17,15 +18,16 @@ struct q6apm_graph;
#define MODULE_ID_PCM_DEC 0x07001005
#define MODULE_ID_PLACEHOLDER_ENCODER 0x07001008
#define MODULE_ID_PLACEHOLDER_DECODER 0x07001009
-#define MODULE_ID_SAL 0x07001010
-#define MODULE_ID_MFC 0x07001015
-#define MODULE_ID_CODEC_DMA_SINK 0x07001023
-#define MODULE_ID_CODEC_DMA_SOURCE 0x07001024
#define MODULE_ID_I2S_SINK 0x0700100A
#define MODULE_ID_I2S_SOURCE 0x0700100B
+#define MODULE_ID_SAL 0x07001010
+#define MODULE_ID_MFC 0x07001015
#define MODULE_ID_DATA_LOGGING 0x0700101A
#define MODULE_ID_AAC_DEC 0x0700101F
+#define MODULE_ID_CODEC_DMA_SINK 0x07001023
+#define MODULE_ID_CODEC_DMA_SOURCE 0x07001024
#define MODULE_ID_FLAC_DEC 0x0700102F
+#define MODULE_ID_SMECNS_V2 0x07001031
#define MODULE_ID_MP3_DECODE 0x0700103B
#define MODULE_ID_GAPLESS 0x0700104D
#define MODULE_ID_DISPLAY_PORT_SINK 0x07001069
@@ -461,8 +463,8 @@ struct param_id_i2s_intf_cfg {
} __packed;
#define I2S_INTF_TYPE_PRIMARY 0
-#define I2S_INTF_TYPE_SECOINDARY 1
-#define I2S_INTF_TYPE_TERTINARY 2
+#define I2S_INTF_TYPE_SECONDARY 1
+#define I2S_INTF_TYPE_TERTIARY 2
#define I2S_INTF_TYPE_QUATERNARY 3
#define I2S_INTF_TYPE_QUINARY 4
#define I2S_SD0 1
@@ -707,9 +709,6 @@ struct audioreach_module {
uint32_t max_ip_port;
uint32_t max_op_port;
- uint32_t in_port;
- uint32_t out_port;
-
uint32_t num_connections;
/* Connections */
uint32_t src_mod_inst_id;
@@ -745,6 +744,7 @@ struct audioreach_module {
struct list_head node;
struct audioreach_container *container;
struct snd_soc_dapm_widget *widget;
+ struct audioreach_module_priv_data *data;
};
struct audioreach_module_config {
diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c
index 83319a9..f61285e 100644
--- a/sound/soc/qcom/qdsp6/topology.c
+++ b/sound/soc/qcom/qdsp6/topology.c
@@ -305,6 +305,34 @@ static struct snd_soc_tplg_vendor_array *audioreach_get_module_array(
return NULL;
}
+static struct audioreach_module_priv_data *audioreach_get_module_priv_data(
+ struct snd_soc_tplg_private *private)
+{
+ int sz;
+
+ for (sz = 0; sz < le32_to_cpu(private->size); ) {
+ struct snd_soc_tplg_vendor_array *mod_array;
+
+ mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
+ if (le32_to_cpu(mod_array->type) == SND_SOC_AR_TPLG_MODULE_CFG_TYPE) {
+ struct audioreach_module_priv_data *pdata;
+
+ pdata = kzalloc(struct_size(pdata, data, le32_to_cpu(mod_array->size)),
+ GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(pdata, ((u8 *)private->data + sz), struct_size(pdata, data,
+ le32_to_cpu(mod_array->size)));
+ return pdata;
+ }
+
+ sz = sz + le32_to_cpu(mod_array->size);
+ }
+
+ return NULL;
+}
+
static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm,
struct snd_soc_tplg_private *private)
{
@@ -412,7 +440,7 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap
struct snd_soc_tplg_private *private,
struct snd_soc_dapm_widget *w)
{
- uint32_t max_ip_port = 0, max_op_port = 0, in_port = 0, out_port = 0;
+ uint32_t max_ip_port = 0, max_op_port = 0;
uint32_t src_mod_op_port_id[AR_MAX_MOD_LINKS] = { 0, };
uint32_t dst_mod_inst_id[AR_MAX_MOD_LINKS] = { 0, };
uint32_t dst_mod_ip_port_id[AR_MAX_MOD_LINKS] = { 0, };
@@ -455,12 +483,6 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap
case AR_TKN_U32_MODULE_MAX_OP_PORTS:
max_op_port = le32_to_cpu(mod_elem->value);
break;
- case AR_TKN_U32_MODULE_IN_PORTS:
- in_port = le32_to_cpu(mod_elem->value);
- break;
- case AR_TKN_U32_MODULE_OUT_PORTS:
- out_port = le32_to_cpu(mod_elem->value);
- break;
case AR_TKN_U32_MODULE_SRC_INSTANCE_ID:
src_mod_inst_id = le32_to_cpu(mod_elem->value);
break;
@@ -550,8 +572,6 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap
mod->module_id = module_id;
mod->max_ip_port = max_ip_port;
mod->max_op_port = max_op_port;
- mod->in_port = in_port;
- mod->out_port = out_port;
mod->src_mod_inst_id = src_mod_inst_id;
for (pn = 0; pn < mod->max_op_port; pn++) {
if (src_mod_op_port_id[pn] && dst_mod_inst_id[pn] &&
@@ -587,8 +607,10 @@ static int audioreach_widget_load_module_common(struct snd_soc_component *compon
return PTR_ERR(cont);
mod = audioreach_parse_common_tokens(apm, cont, &tplg_w->priv, w);
- if (IS_ERR(mod))
- return PTR_ERR(mod);
+ if (IS_ERR_OR_NULL(mod))
+ return mod ? PTR_ERR(mod) : -ENODEV;
+
+ mod->data = audioreach_get_module_priv_data(&tplg_w->priv);
dobj = &w->dobj;
dobj->private = mod;
@@ -947,6 +969,7 @@ static int audioreach_widget_unload(struct snd_soc_component *scomp,
cont->num_modules--;
list_del(&mod->node);
+ kfree(mod->data);
kfree(mod);
/* Graph Info has N sub-graphs, sub-graph has N containers, Container has N Modules */
if (list_empty(&cont->modules_list)) { /* if no modules in the container then remove it */
diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 73f9f82..5d10b1c 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -7,6 +7,7 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <linux/soundwire/sdw.h>
#include <sound/jack.h>
#include <linux/input-event-codes.h>
@@ -82,8 +83,10 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
rate->min = rate->max = 48000;
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
channels->min = 2;
channels->max = 2;
switch (cpu_dai->id) {
diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c
index 8eb57fc..444f216 100644
--- a/sound/soc/qcom/x1e80100.c
+++ b/sound/soc/qcom/x1e80100.c
@@ -210,14 +210,15 @@ static int x1e80100_platform_probe(struct platform_device *pdev)
if (ret)
return ret;
- card->driver_name = "x1e80100";
+ card->driver_name = of_device_get_match_data(dev);
x1e80100_add_be_ops(card);
return devm_snd_soc_register_card(dev, card);
}
static const struct of_device_id snd_x1e80100_dt_match[] = {
- { .compatible = "qcom,x1e80100-sndcard", },
+ { .compatible = "qcom,x1e80100-sndcard", .data = "x1e80100" },
+ { .compatible = "qcom,glymur-sndcard", .data = "glymur" },
{}
};
MODULE_DEVICE_TABLE(of, snd_x1e80100_dt_match);
diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c
index 221ce91..630c2f5 100644
--- a/sound/soc/renesas/fsi.c
+++ b/sound/soc/renesas/fsi.c
@@ -343,14 +343,9 @@ static void __fsi_reg_mask_set(u32 __iomem *reg, u32 mask, u32 data)
#define fsi_core_read(p, r) _fsi_master_read(p, p->core->r)
static u32 _fsi_master_read(struct fsi_master *master, u32 reg)
{
- u32 ret;
- unsigned long flags;
+ guard(spinlock_irqsave)(&master->lock);
- spin_lock_irqsave(&master->lock, flags);
- ret = __fsi_reg_read(master->base + reg);
- spin_unlock_irqrestore(&master->lock, flags);
-
- return ret;
+ return __fsi_reg_read(master->base + reg);
}
#define fsi_master_mask_set(p, r, m, d) _fsi_master_mask_set(p, MST_##r, m, d)
@@ -358,11 +353,9 @@ static u32 _fsi_master_read(struct fsi_master *master, u32 reg)
static void _fsi_master_mask_set(struct fsi_master *master,
u32 reg, u32 mask, u32 data)
{
- unsigned long flags;
+ guard(spinlock_irqsave)(&master->lock);
- spin_lock_irqsave(&master->lock, flags);
__fsi_reg_mask_set(master->base + reg, mask, data);
- spin_unlock_irqrestore(&master->lock, flags);
}
/*
@@ -499,14 +492,10 @@ static int fsi_stream_is_working(struct fsi_priv *fsi,
struct fsi_stream *io)
{
struct fsi_master *master = fsi_get_master(fsi);
- unsigned long flags;
- int ret;
- spin_lock_irqsave(&master->lock, flags);
- ret = !!(io->substream && io->substream->runtime);
- spin_unlock_irqrestore(&master->lock, flags);
+ guard(spinlock_irqsave)(&master->lock);
- return ret;
+ return !!(io->substream && io->substream->runtime);
}
static struct fsi_priv *fsi_stream_to_priv(struct fsi_stream *io)
@@ -520,9 +509,9 @@ static void fsi_stream_init(struct fsi_priv *fsi,
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsi_master *master = fsi_get_master(fsi);
- unsigned long flags;
- spin_lock_irqsave(&master->lock, flags);
+ guard(spinlock_irqsave)(&master->lock);
+
io->substream = substream;
io->buff_sample_capa = fsi_frame2sample(fsi, runtime->buffer_size);
io->buff_sample_pos = 0;
@@ -533,16 +522,14 @@ static void fsi_stream_init(struct fsi_priv *fsi,
io->oerr_num = -1; /* ignore 1st err */
io->uerr_num = -1; /* ignore 1st err */
fsi_stream_handler_call(io, init, fsi, io);
- spin_unlock_irqrestore(&master->lock, flags);
}
static void fsi_stream_quit(struct fsi_priv *fsi, struct fsi_stream *io)
{
struct snd_soc_dai *dai = fsi_get_dai(io->substream);
struct fsi_master *master = fsi_get_master(fsi);
- unsigned long flags;
- spin_lock_irqsave(&master->lock, flags);
+ guard(spinlock_irqsave)(&master->lock);
if (io->oerr_num > 0)
dev_err(dai->dev, "over_run = %d\n", io->oerr_num);
@@ -560,7 +547,6 @@ static void fsi_stream_quit(struct fsi_priv *fsi, struct fsi_stream *io)
io->bus_option = 0;
io->oerr_num = 0;
io->uerr_num = 0;
- spin_unlock_irqrestore(&master->lock, flags);
}
static int fsi_stream_transfer(struct fsi_stream *io)
diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c
index 9f086906a..69fb199 100644
--- a/sound/soc/renesas/rcar/core.c
+++ b/sound/soc/renesas/rcar/core.c
@@ -696,25 +696,21 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
int ret;
- unsigned long flags;
- spin_lock_irqsave(&priv->lock, flags);
+ guard(spinlock_irqsave)(&priv->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
ret = rsnd_dai_call(init, io, priv);
if (ret < 0)
- goto dai_trigger_end;
+ break;
ret = rsnd_dai_call(start, io, priv);
if (ret < 0)
- goto dai_trigger_end;
+ break;
ret = rsnd_dai_call(irq, io, priv, 1);
- if (ret < 0)
- goto dai_trigger_end;
-
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -729,9 +725,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret = -EINVAL;
}
-dai_trigger_end:
- spin_unlock_irqrestore(&priv->lock, flags);
-
return ret;
}
@@ -1545,15 +1538,14 @@ static int rsnd_hw_update(struct snd_pcm_substream *substream,
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
struct rsnd_priv *priv = rsnd_io_to_priv(io);
- unsigned long flags;
int ret;
- spin_lock_irqsave(&priv->lock, flags);
+ guard(spinlock_irqsave)(&priv->lock);
+
if (hw_params)
ret = rsnd_dai_call(hw_params, io, substream, hw_params);
else
ret = rsnd_dai_call(hw_free, io, substream);
- spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
diff --git a/sound/soc/renesas/rcar/msiof.c b/sound/soc/renesas/rcar/msiof.c
index 36d31ab..f5338bb 100644
--- a/sound/soc/renesas/rcar/msiof.c
+++ b/sound/soc/renesas/rcar/msiof.c
@@ -37,7 +37,6 @@
/* SISTR */
#define SISTR_ERR_TX (SISTR_TFSERR | SISTR_TFOVF | SISTR_TFUDF)
#define SISTR_ERR_RX (SISTR_RFSERR | SISTR_RFOVF | SISTR_RFUDF)
-#define SISTR_ERR (SISTR_ERR_TX | SISTR_ERR_RX)
/*
* The data on memory in 24bit case is located at <right> side
@@ -80,15 +79,19 @@ struct msiof_priv {
#define msiof_is_play(substream) ((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK)
#define msiof_read(priv, reg) ioread32((priv)->base + reg)
#define msiof_write(priv, reg, val) iowrite32(val, (priv)->base + reg)
-#define msiof_status_clear(priv) msiof_write(priv, SISTR, SISTR_ERR)
-static void msiof_update(struct msiof_priv *priv, u32 reg, u32 mask, u32 val)
+static int msiof_update(struct msiof_priv *priv, u32 reg, u32 mask, u32 val)
{
u32 old = msiof_read(priv, reg);
u32 new = (old & ~mask) | (val & mask);
+ int updated = false;
- if (old != new)
+ if (old != new) {
msiof_write(priv, reg, new);
+ updated = true;
+ }
+
+ return updated;
}
static void msiof_update_and_wait(struct msiof_priv *priv, u32 reg, u32 mask, u32 val, u32 expect)
@@ -96,7 +99,9 @@ static void msiof_update_and_wait(struct msiof_priv *priv, u32 reg, u32 mask, u3
u32 data;
int ret;
- msiof_update(priv, reg, mask, val);
+ ret = msiof_update(priv, reg, mask, val);
+ if (!ret) /* no update */
+ return;
ret = readl_poll_timeout_atomic(priv->base + reg, data,
(data & mask) == expect, 1, 128);
@@ -131,6 +136,9 @@ static int msiof_hw_start(struct snd_soc_component *component,
priv->err_ovf[substream->stream] =
priv->err_udf[substream->stream] = 0;
+ /* Start DMAC */
+ snd_dmaengine_pcm_trigger(substream, cmd);
+
/* SITMDRx */
if (is_play) {
val = SITMDR1_PCON |
@@ -167,6 +175,13 @@ static int msiof_hw_start(struct snd_soc_component *component,
val = SIIER_RDREQE | SIIER_RDMAE | SISTR_ERR_RX;
msiof_update(priv, SIIER, val, val);
+ /* clear status */
+ if (is_play)
+ val = SISTR_ERR_TX;
+ else
+ val = SISTR_ERR_RX;
+ msiof_update(priv, SISTR, val, val);
+
/* SICTR */
if (is_play)
val = SICTR_TXE | SICTR_TEDG;
@@ -174,11 +189,6 @@ static int msiof_hw_start(struct snd_soc_component *component,
val = SICTR_RXE | SICTR_REDG;
msiof_update_and_wait(priv, SICTR, val, val, val);
- msiof_status_clear(priv);
-
- /* Start DMAC */
- snd_dmaengine_pcm_trigger(substream, cmd);
-
return 0;
}
@@ -211,7 +221,7 @@ static int msiof_hw_stop(struct snd_soc_component *component,
if (priv->err_syc[substream->stream] ||
priv->err_ovf[substream->stream] ||
priv->err_udf[substream->stream])
- dev_warn(dev, "FSERR(%s) = %d, FOVF = %d, FUDF = %d\n",
+ dev_warn(dev, "%s: FSERR = %d, FOVF = %d, FUDF = %d\n",
snd_pcm_direction_name(substream->stream),
priv->err_syc[substream->stream],
priv->err_ovf[substream->stream],
@@ -362,10 +372,9 @@ static int msiof_trigger(struct snd_soc_component *component,
{
struct device *dev = component->dev;
struct msiof_priv *priv = dev_get_drvdata(dev);
- unsigned long flags;
int ret = -EINVAL;
- spin_lock_irqsave(&priv->lock, flags);
+ guard(spinlock_irqsave)(&priv->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -382,8 +391,6 @@ static int msiof_trigger(struct snd_soc_component *component,
break;
}
- spin_unlock_irqrestore(&priv->lock, flags);
-
return ret;
}
@@ -394,23 +401,18 @@ static int msiof_hw_params(struct snd_soc_component *component,
struct msiof_priv *priv = dev_get_drvdata(component->dev);
struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
struct dma_slave_config cfg = {};
- unsigned long flags;
int ret;
- spin_lock_irqsave(&priv->lock, flags);
+ guard(spinlock_irqsave)(&priv->lock);
ret = snd_hwparams_to_dma_slave_config(substream, params, &cfg);
if (ret < 0)
- goto hw_params_out;
+ return ret;
cfg.dst_addr = priv->phy_addr + SITFDR;
cfg.src_addr = priv->phy_addr + SIRFDR;
- ret = dmaengine_slave_config(chan, &cfg);
-hw_params_out:
- spin_unlock_irqrestore(&priv->lock, flags);
-
- return ret;
+ return dmaengine_slave_config(chan, &cfg);
}
static const struct snd_soc_component_driver msiof_component_driver = {
@@ -429,12 +431,10 @@ static irqreturn_t msiof_interrupt(int irq, void *data)
struct snd_pcm_substream *substream;
u32 sistr;
- spin_lock(&priv->lock);
-
- sistr = msiof_read(priv, SISTR);
- msiof_status_clear(priv);
-
- spin_unlock(&priv->lock);
+ scoped_guard(spinlock, &priv->lock) {
+ sistr = msiof_read(priv, SISTR);
+ msiof_write(priv, SISTR, SISTR_ERR_TX | SISTR_ERR_RX);
+ }
/* overflow/underflow error */
substream = priv->substream[SNDRV_PCM_STREAM_PLAYBACK];
diff --git a/sound/soc/renesas/rcar/src.c b/sound/soc/renesas/rcar/src.c
index f47bf38..6a3dbc8 100644
--- a/sound/soc/renesas/rcar/src.c
+++ b/sound/soc/renesas/rcar/src.c
@@ -558,19 +558,16 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
bool stop = false;
- spin_lock(&priv->lock);
+ scoped_guard(spinlock, &priv->lock) {
+ /* ignore all cases if not working */
+ if (!rsnd_io_is_working(io))
+ break;
- /* ignore all cases if not working */
- if (!rsnd_io_is_working(io))
- goto rsnd_src_interrupt_out;
+ if (rsnd_src_error_occurred(mod))
+ stop = true;
- if (rsnd_src_error_occurred(mod))
- stop = true;
-
- rsnd_src_status_clear(mod);
-rsnd_src_interrupt_out:
-
- spin_unlock(&priv->lock);
+ rsnd_src_status_clear(mod);
+ }
if (stop)
snd_pcm_stop_xrun(io->substream);
diff --git a/sound/soc/renesas/rcar/ssi.c b/sound/soc/renesas/rcar/ssi.c
index d52056c..0420041 100644
--- a/sound/soc/renesas/rcar/ssi.c
+++ b/sound/soc/renesas/rcar/ssi.c
@@ -680,32 +680,31 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
bool elapsed = false;
bool stop = false;
- spin_lock(&priv->lock);
+ scoped_guard(spinlock, &priv->lock) {
- /* ignore all cases if not working */
- if (!rsnd_io_is_working(io))
- goto rsnd_ssi_interrupt_out;
+ /* ignore all cases if not working */
+ if (!rsnd_io_is_working(io))
+ break;
- status = rsnd_ssi_status_get(mod);
+ status = rsnd_ssi_status_get(mod);
- /* PIO only */
- if (!is_dma && (status & DIRQ))
- elapsed = rsnd_ssi_pio_interrupt(mod, io);
+ /* PIO only */
+ if (!is_dma && (status & DIRQ))
+ elapsed = rsnd_ssi_pio_interrupt(mod, io);
- /* DMA only */
- if (is_dma && (status & (UIRQ | OIRQ))) {
- rsnd_print_irq_status(dev, "%s err status : 0x%08x\n",
- rsnd_mod_name(mod), status);
+ /* DMA only */
+ if (is_dma && (status & (UIRQ | OIRQ))) {
+ rsnd_print_irq_status(dev, "%s err status : 0x%08x\n",
+ rsnd_mod_name(mod), status);
- stop = true;
+ stop = true;
+ }
+
+ stop |= rsnd_ssiu_busif_err_status_clear(mod);
+
+ rsnd_ssi_status_clear(mod);
}
- stop |= rsnd_ssiu_busif_err_status_clear(mod);
-
- rsnd_ssi_status_clear(mod);
-rsnd_ssi_interrupt_out:
- spin_unlock(&priv->lock);
-
if (elapsed)
snd_pcm_period_elapsed(io->substream);
diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c
index 0f7458a..e009408 100644
--- a/sound/soc/renesas/rz-ssi.c
+++ b/sound/soc/renesas/rz-ssi.c
@@ -188,24 +188,18 @@ static void rz_ssi_set_substream(struct rz_ssi_stream *strm,
struct snd_pcm_substream *substream)
{
struct rz_ssi_priv *ssi = strm->priv;
- unsigned long flags;
- spin_lock_irqsave(&ssi->lock, flags);
+ guard(spinlock_irqsave)(&ssi->lock);
+
strm->substream = substream;
- spin_unlock_irqrestore(&ssi->lock, flags);
}
static bool rz_ssi_stream_is_valid(struct rz_ssi_priv *ssi,
struct rz_ssi_stream *strm)
{
- unsigned long flags;
- bool ret;
+ guard(spinlock_irqsave)(&ssi->lock);
- spin_lock_irqsave(&ssi->lock, flags);
- ret = strm->substream && strm->substream->runtime;
- spin_unlock_irqrestore(&ssi->lock, flags);
-
- return ret;
+ return strm->substream && strm->substream->runtime;
}
static inline bool rz_ssi_is_stream_running(struct rz_ssi_stream *strm)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index cc9125f..9dd84d7 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -717,7 +717,7 @@ int snd_soc_suspend(struct device *dev)
* means it's doing something,
* otherwise fall through.
*/
- if (dapm->idle_bias_off) {
+ if (!dapm->idle_bias) {
dev_dbg(component->dev,
"ASoC: idle_bias_off CODEC on over suspend\n");
break;
@@ -1652,7 +1652,7 @@ static int soc_probe_component(struct snd_soc_card *card,
if (ret < 0)
goto err_probe;
- WARN(dapm->idle_bias_off &&
+ WARN(!dapm->idle_bias &&
dapm->bias_level != SND_SOC_BIAS_OFF,
"codec %s can not start from non-off bias with idle_bias_off==1\n",
component->name);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index a37d44c..51fb09d 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -165,6 +165,27 @@ static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...)
kfree(buf);
}
+struct device *snd_soc_dapm_to_dev(struct snd_soc_dapm_context *dapm)
+{
+ if (dapm->component)
+ return dapm->component->dev;
+
+ return dapm->card->dev;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_to_dev);
+
+struct snd_soc_card *snd_soc_dapm_to_card(struct snd_soc_dapm_context *dapm)
+{
+ return dapm->card;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_to_card);
+
+struct snd_soc_component *snd_soc_dapm_to_component(struct snd_soc_dapm_context *dapm)
+{
+ return dapm->component;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_to_component);
+
static bool dapm_dirty_widget(struct snd_soc_dapm_widget *w)
{
return !list_empty(&w->dirty);
@@ -838,13 +859,13 @@ static struct list_head *dapm_kcontrol_get_path_list(
list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \
list_kcontrol)
-unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol)
+unsigned int snd_soc_dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol)
{
struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
return data->value;
}
-EXPORT_SYMBOL_GPL(dapm_kcontrol_get_value);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_get_value);
static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
unsigned int value)
@@ -877,31 +898,42 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
}
/**
- * snd_soc_dapm_kcontrol_widget() - Returns the widget associated to a
+ * snd_soc_dapm_kcontrol_to_widget() - Returns the widget associated to a
* kcontrol
* @kcontrol: The kcontrol
*/
-struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(
- struct snd_kcontrol *kcontrol)
+struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_to_widget(struct snd_kcontrol *kcontrol)
{
return dapm_kcontrol_get_wlist(kcontrol)->widgets[0];
}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_widget);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_to_widget);
/**
- * snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a
- * kcontrol
+ * snd_soc_dapm_kcontrol_to_dapm() - Returns the dapm context associated to a kcontrol
* @kcontrol: The kcontrol
*
* Note: This function must only be used on kcontrols that are known to have
* been registered for a CODEC. Otherwise the behaviour is undefined.
*/
-struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
- struct snd_kcontrol *kcontrol)
+struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_to_dapm(struct snd_kcontrol *kcontrol)
{
return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->dapm;
}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_to_dapm);
+
+/**
+ * snd_soc_dapm_kcontrol_to_component() - Returns the component associated to a
+ * kcontrol
+ * @kcontrol: The kcontrol
+ *
+ * This function must only be used on DAPM contexts that are known to be part of
+ * a COMPONENT (e.g. in a COMPONENT driver). Otherwise the behavior is undefined
+ */
+struct snd_soc_component *snd_soc_dapm_kcontrol_to_component(struct snd_kcontrol *kcontrol)
+{
+ return snd_soc_dapm_to_component(snd_soc_dapm_kcontrol_to_dapm(kcontrol));
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_to_component);
static void dapm_reset(struct snd_soc_card *card)
{
@@ -1000,6 +1032,25 @@ int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm,
EXPORT_SYMBOL_GPL(snd_soc_dapm_force_bias_level);
/**
+ * snd_soc_dapm_init_bias_level() - Initialize DAPM bias level
+ * @dapm: The DAPM context to initialize
+ * @level: The DAPM level to initialize to
+ *
+ * This function only sets the driver internal state of the DAPM level and will
+ * not modify the state of the device. Hence it should not be used during normal
+ * operation, but only to synchronize the internal state to the device state.
+ * E.g. during driver probe to set the DAPM level to the one corresponding with
+ * the power-on reset state of the device.
+ *
+ * To change the DAPM state of the device use snd_soc_dapm_set_bias_level().
+ */
+void snd_soc_dapm_init_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
+{
+ dapm->bias_level = level;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_init_bias_level);
+
+/**
* snd_soc_dapm_set_bias_level - set the bias level for the system
* @dapm: DAPM context
* @level: level to configure
@@ -1037,6 +1088,18 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
return ret;
}
+/**
+ * snd_soc_dapm_get_bias_level() - Get current DAPM bias level
+ * @dapm: The context for which to get the bias level
+ *
+ * Returns: The current bias level of the passed DAPM context.
+ */
+enum snd_soc_bias_level snd_soc_dapm_get_bias_level(struct snd_soc_dapm_context *dapm)
+{
+ return dapm->bias_level;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_bias_level);
+
static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *kcontrolw,
const struct snd_kcontrol_new *kcontrol_new,
@@ -2117,21 +2180,26 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w,
dapm_seq_insert(w, down_list, false);
}
-static bool dapm_idle_bias_off(struct snd_soc_dapm_context *dapm)
+bool snd_soc_dapm_get_idle_bias(struct snd_soc_dapm_context *dapm)
{
- if (dapm->idle_bias_off)
- return true;
+ if (dapm->idle_bias) {
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ unsigned int state = snd_power_get_state(dapm->card->snd_card);
- switch (snd_power_get_state(dapm->card->snd_card)) {
- case SNDRV_CTL_POWER_D3hot:
- case SNDRV_CTL_POWER_D3cold:
- return dapm->suspend_bias_off;
- default:
- break;
+ if ((state == SNDRV_CTL_POWER_D3hot || (state == SNDRV_CTL_POWER_D3cold)) &&
+ component)
+ return !component->driver->suspend_bias_off;
}
- return false;
+ return dapm->idle_bias;
}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_idle_bias);
+
+void snd_soc_dapm_set_idle_bias(struct snd_soc_dapm_context *dapm, bool on)
+{
+ dapm->idle_bias = on;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_set_idle_bias);
/*
* Scan each dapm widget for complete audio path.
@@ -2158,10 +2226,10 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event,
trace_snd_soc_dapm_start(card, event);
for_each_card_dapms(card, d) {
- if (dapm_idle_bias_off(d))
- d->target_bias_level = SND_SOC_BIAS_OFF;
- else
+ if (snd_soc_dapm_get_idle_bias(d))
d->target_bias_level = SND_SOC_BIAS_STANDBY;
+ else
+ d->target_bias_level = SND_SOC_BIAS_OFF;
}
dapm_reset(card);
@@ -2225,7 +2293,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event,
if (d->target_bias_level > bias)
bias = d->target_bias_level;
for_each_card_dapms(card, d)
- if (!dapm_idle_bias_off(d))
+ if (snd_soc_dapm_get_idle_bias(d))
d->target_bias_level = bias;
trace_snd_soc_dapm_walk_done(card);
@@ -4759,8 +4827,7 @@ void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm,
if (component) {
dapm->dev = component->dev;
- dapm->idle_bias_off = !component->driver->idle_bias_on;
- dapm->suspend_bias_off = component->driver->suspend_bias_off;
+ dapm->idle_bias = component->driver->idle_bias_on;
} else {
dapm->dev = card->dev;
}
diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c
index f00b381..40debc5 100644
--- a/sound/soc/sof/imx/imx-common.c
+++ b/sound/soc/sof/imx/imx-common.c
@@ -354,8 +354,8 @@ static int imx_probe(struct snd_sof_dev *sdev)
common = devm_kzalloc(sdev->dev, sizeof(*common), GFP_KERNEL);
if (!common)
- return dev_err_probe(sdev->dev, -ENOMEM,
- "failed to allocate common data\n");
+ return -ENOMEM;
+
sdev->pdata->hw_pdata = common;
common->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
@@ -382,7 +382,7 @@ static int imx_probe(struct snd_sof_dev *sdev)
imx_unregister_action,
sdev);
if (ret)
- return dev_err_probe(sdev->dev, ret, "failed to add devm action\n");
+ return ret;
common->ipc_handle = dev_get_drvdata(&common->ipc_dev->dev);
if (!common->ipc_handle)
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c
index b73dd91..7e9eab2 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -171,8 +171,7 @@ static int imx8m_probe(struct snd_sof_dev *sdev)
chip = devm_kzalloc(sdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
- return dev_err_probe(sdev->dev, -ENOMEM,
- "failed to allocate chip data\n");
+ return -ENOMEM;
chip->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE);
if (!chip->dap)
diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c
index 2f99258..37674ea 100644
--- a/sound/soc/sof/intel/hda-codec.c
+++ b/sound/soc/sof/intel/hda-codec.c
@@ -260,9 +260,6 @@ void hda_codec_detect_mask(struct snd_sof_dev *sdev)
sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
return;
- /* Accept unsolicited responses */
- snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
-
/* detect codecs */
if (!bus->codec_mask) {
bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c
index 4f34fd9..8332d4b 100644
--- a/sound/soc/sof/intel/hda-ctrl.c
+++ b/sound/soc/sof/intel/hda-ctrl.c
@@ -183,7 +183,7 @@ int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable)
}
EXPORT_SYMBOL_NS(hda_dsp_ctrl_clock_power_gating, "SND_SOC_SOF_INTEL_HDA_COMMON");
-int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev)
+int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec)
{
struct hdac_bus *bus = sof_to_bus(sdev);
struct hdac_stream *stream;
@@ -220,7 +220,11 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev)
}
usleep_range(1000, 1200);
- hda_codec_detect_mask(sdev);
+ /* Accept unsolicited responses */
+ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
+
+ if (detect_codec)
+ hda_codec_detect_mask(sdev);
/* clear stream status */
list_for_each_entry(stream, &bus->stream_list, list) {
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index f64e8a6..3ab6d5c 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -870,7 +870,7 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0);
/* reset and start hda controller */
- ret = hda_dsp_ctrl_init_chip(sdev);
+ ret = hda_dsp_ctrl_init_chip(sdev, false);
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to start controller after resume\n");
diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c
index 1327f1c..ff5abccf 100644
--- a/sound/soc/sof/intel/hda-sdw-bpt.c
+++ b/sound/soc/sof/intel/hda-sdw-bpt.c
@@ -150,7 +150,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream
u32 mask;
int ret;
- ret = hda_cl_cleanup(sdev->dev, dmab_bdl, true, sdw_bpt_stream);
+ ret = hda_cl_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream);
if (ret < 0) {
dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n",
__func__);
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index c387efe..52e86fa 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -616,7 +616,7 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
dev_dbg(sdev->dev, "PP capability, will probe DSP later.\n");
/* Init HDA controller after i915 init */
- ret = hda_dsp_ctrl_init_chip(sdev);
+ ret = hda_dsp_ctrl_init_chip(sdev, true);
if (ret < 0) {
dev_err(bus->dev, "error: init chip failed with ret: %d\n",
ret);
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index e14f82c..28daf0a 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -757,7 +757,7 @@ void hda_dsp_ctrl_ppcap_int_enable(struct snd_sof_dev *sdev, bool enable);
int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev, bool reset);
void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable);
int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable);
-int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev);
+int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec);
void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev);
/*
* HDA bus operations.
diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c
index e5c8fec..6ec391f 100644
--- a/sound/soc/sof/ipc3-dtrace.c
+++ b/sound/soc/sof/ipc3-dtrace.c
@@ -126,7 +126,7 @@ static int trace_filter_parse(struct snd_sof_dev *sdev, char *string,
capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY;
entry = strchr(entry + 1, entry_delimiter[0]);
}
- *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL);
+ *out = kmalloc_array(capacity, sizeof(**out), GFP_KERNEL);
if (!*out)
return -ENOMEM;
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 374dc10..cb9a067 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -727,6 +727,58 @@ static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev,
return 0;
}
+static int sof_ipc4_pcm_dai_link_fixup_channels(struct snd_sof_dev *sdev,
+ struct snd_pcm_hw_params *params,
+ struct sof_ipc4_copier *ipc4_copier)
+{
+ struct sof_ipc4_pin_format *pin_fmts = ipc4_copier->available_fmt.input_pin_fmts;
+ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ int num_input_formats = ipc4_copier->available_fmt.num_input_formats;
+ unsigned int fe_channels = params_channels(params);
+ bool fe_be_match = false;
+ bool single_be_channels = true;
+ unsigned int be_channels, val;
+ int i;
+
+ if (WARN_ON_ONCE(!num_input_formats))
+ return -EINVAL;
+
+ /*
+ * Copier does not change channels, so we
+ * need to only consider the input pin information.
+ */
+ be_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(pin_fmts[0].audio_fmt.fmt_cfg);
+ for (i = 0; i < num_input_formats; i++) {
+ val = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(pin_fmts[i].audio_fmt.fmt_cfg);
+
+ if (val != be_channels)
+ single_be_channels = false;
+
+ if (val == fe_channels) {
+ fe_be_match = true;
+ break;
+ }
+ }
+
+ /*
+ * If channels is different than FE channels, topology must contain a
+ * module which can change the number of channels. But we do require
+ * topology to define a single channels in the DAI copier config in
+ * this case (FE channels may be variable).
+ */
+ if (!fe_be_match) {
+ if (!single_be_channels) {
+ dev_err(sdev->dev, "Unable to select channels for DAI link\n");
+ return -EINVAL;
+ }
+
+ channels->min = be_channels;
+ channels->max = be_channels;
+ }
+
+ return 0;
+}
+
static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -790,6 +842,10 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
if (ret)
return ret;
+ ret = sof_ipc4_pcm_dai_link_fixup_channels(sdev, params, ipc4_copier);
+ if (ret)
+ return ret;
+
if (single_bitdepth) {
snd_mask_none(fmt);
valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(ipc4_fmt->fmt_cfg);
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 591ee30..b6a732d 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -38,6 +38,36 @@ MODULE_PARM_DESC(ipc4_ignore_cpc,
static DEFINE_IDA(alh_group_ida);
static DEFINE_IDA(pipeline_ida);
+struct sof_comp_domains {
+ const char *name;
+ enum sof_comp_domain domain;
+};
+
+static const struct sof_comp_domains sof_domains[] = {
+ { "LL", SOF_COMP_DOMAIN_LL, },
+ { "DP", SOF_COMP_DOMAIN_DP, }
+};
+
+static enum sof_comp_domain find_domain(const char *name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sof_domains); i++) {
+ if (strcmp(name, sof_domains[i].name) == 0)
+ return sof_domains[i].domain;
+ }
+ /* No valid value found, fall back to manifest value */
+ return SOF_COMP_DOMAIN_UNSET;
+}
+
+static int get_token_comp_domain(void *elem, void *object, u32 offset)
+{
+ u32 *val = (u32 *)((u8 *)object + offset);
+
+ *val = find_domain((const char *)elem);
+ return 0;
+}
+
static const struct sof_topology_token ipc4_sched_tokens[] = {
{SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
offsetof(struct sof_ipc4_pipeline, lp_mode)},
@@ -127,6 +157,8 @@ static const struct sof_topology_token comp_ext_tokens[] = {
offsetof(struct snd_sof_widget, uuid)},
{SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
offsetof(struct snd_sof_widget, core)},
+ {SOF_TKN_COMP_SCHED_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_domain,
+ offsetof(struct snd_sof_widget, comp_domain)},
};
static const struct sof_topology_token gain_tokens[] = {
@@ -497,7 +529,17 @@ static int sof_ipc4_widget_setup_msg(struct snd_sof_widget *swidget, struct sof_
msg->extension = SOF_IPC4_MOD_EXT_CORE_ID(swidget->core);
- type = (fw_module->man4_module_entry.type & SOF_IPC4_MODULE_DP) ? 1 : 0;
+ switch (swidget->comp_domain) {
+ case SOF_COMP_DOMAIN_LL:
+ type = 0;
+ break;
+ case SOF_COMP_DOMAIN_DP:
+ type = 1;
+ break;
+ default:
+ type = (fw_module->man4_module_entry.type & SOF_IPC4_MODULE_DP) ? 1 : 0;
+ break;
+ }
msg->extension |= SOF_IPC4_MOD_EXT_DOMAIN(type);
return 0;
@@ -1292,6 +1334,23 @@ static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev,
return 0;
}
+static u32 sof_ipc4_fmt_cfg_to_type(u32 fmt_cfg)
+{
+ /* Fetch the sample type from the fmt for 8 and 32 bit formats */
+ u32 __bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt_cfg);
+
+ if (__bits == 8 || __bits == 32)
+ return SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE(fmt_cfg);
+
+ /*
+ * Return LSB integer type for 20 and 24 formats as the firmware is
+ * handling the LSB/MSB alignment internally, for the kernel this
+ * should not be taken into account, we treat them as LSB to match with
+ * the format we support on the PCM side.
+ */
+ return SOF_IPC4_TYPE_LSB_INTEGER;
+}
+
/* update hw_params based on the audio stream format */
static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params,
struct sof_ipc4_audio_format *fmt, u32 param_to_update)
@@ -1300,10 +1359,27 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw
if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) {
int valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+ int type = sof_ipc4_fmt_cfg_to_type(fmt->fmt_cfg);
snd_pcm_format_t snd_fmt;
struct snd_mask *m;
switch (valid_bits) {
+ case 8:
+ switch (type) {
+ case SOF_IPC4_TYPE_A_LAW:
+ snd_fmt = SNDRV_PCM_FORMAT_A_LAW;
+ break;
+ case SOF_IPC4_TYPE_MU_LAW:
+ snd_fmt = SNDRV_PCM_FORMAT_MU_LAW;
+ break;
+ case SOF_IPC4_TYPE_UNSIGNED_INTEGER:
+ snd_fmt = SNDRV_PCM_FORMAT_U8;
+ break;
+ default:
+ dev_err(sdev->dev, "Unsupported PCM 8-bit IPC4 type %d\n", type);
+ return -EINVAL;
+ }
+ break;
case 16:
snd_fmt = SNDRV_PCM_FORMAT_S16_LE;
break;
@@ -1311,7 +1387,17 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw
snd_fmt = SNDRV_PCM_FORMAT_S24_LE;
break;
case 32:
- snd_fmt = SNDRV_PCM_FORMAT_S32_LE;
+ switch (type) {
+ case SOF_IPC4_TYPE_LSB_INTEGER:
+ snd_fmt = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ case SOF_IPC4_TYPE_FLOAT:
+ snd_fmt = SNDRV_PCM_FORMAT_FLOAT_LE;
+ break;
+ default:
+ dev_err(sdev->dev, "Unsupported PCM 32-bit IPC4 type %d\n", type);
+ return -EINVAL;
+ }
break;
default:
dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits);
@@ -1375,7 +1461,7 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,
struct sof_ipc4_base_module_cfg *base_config,
struct sof_ipc4_available_audio_format *available_fmt,
u32 out_ref_rate, u32 out_ref_channels,
- u32 out_ref_valid_bits)
+ u32 out_ref_valid_bits, u32 out_ref_type)
{
struct sof_ipc4_pin_format *pin_fmts = available_fmt->output_pin_fmts;
u32 pin_fmts_size = available_fmt->num_output_formats;
@@ -1401,19 +1487,22 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,
for (i = 0; i < pin_fmts_size; i++) {
struct sof_ipc4_audio_format *fmt = &pin_fmts[i].audio_fmt;
- u32 _out_rate, _out_channels, _out_valid_bits;
+ u32 _out_rate, _out_channels, _out_valid_bits, _out_type;
_out_rate = fmt->sampling_frequency;
_out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
_out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+ _out_type = sof_ipc4_fmt_cfg_to_type(fmt->fmt_cfg);
if (_out_rate == out_ref_rate && _out_channels == out_ref_channels &&
- _out_valid_bits == out_ref_valid_bits)
+ _out_valid_bits == out_ref_valid_bits && _out_type == out_ref_type)
goto out_fmt;
}
- dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n",
- __func__, out_ref_rate, out_ref_valid_bits, out_ref_channels);
+ dev_err(sdev->dev,
+ "%s: Unsupported audio format: %uHz, %ubit, %u channels, type: %d\n",
+ __func__, out_ref_rate, out_ref_valid_bits, out_ref_channels,
+ out_ref_type);
return -EINVAL;
@@ -1426,18 +1515,46 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,
static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params)
{
switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ case SNDRV_PCM_FORMAT_A_LAW:
+ return 8;
case SNDRV_PCM_FORMAT_S16_LE:
return 16;
case SNDRV_PCM_FORMAT_S24_LE:
return 24;
case SNDRV_PCM_FORMAT_S32_LE:
return 32;
+ case SNDRV_PCM_FORMAT_FLOAT_LE:
+ return 32;
default:
dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params));
return -EINVAL;
}
}
+static int sof_ipc4_get_sample_type(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params)
+{
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_A_LAW:
+ return SOF_IPC4_TYPE_A_LAW;
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ return SOF_IPC4_TYPE_MU_LAW;
+ case SNDRV_PCM_FORMAT_U8:
+ return SOF_IPC4_TYPE_UNSIGNED_INTEGER;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+ return SOF_IPC4_TYPE_LSB_INTEGER;
+ case SNDRV_PCM_FORMAT_FLOAT_LE:
+ return SOF_IPC4_TYPE_FLOAT;
+ default:
+ dev_err(sdev->dev, "invalid pcm sample type %d\n", params_format(params));
+ return -EINVAL;
+ }
+}
+
static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev,
struct snd_sof_widget *swidget,
struct sof_ipc4_base_module_cfg *base_config,
@@ -1449,8 +1566,10 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev,
u32 valid_bits;
u32 channels;
u32 rate;
+ u32 type;
bool single_format;
int sample_valid_bits;
+ int sample_type;
int i = 0;
if (!pin_fmts_size) {
@@ -1466,6 +1585,10 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev,
if (sample_valid_bits < 0)
return sample_valid_bits;
+ sample_type = sof_ipc4_get_sample_type(sdev, params);
+ if (sample_type < 0)
+ return sample_type;
+
/*
* Search supported input audio formats with pin index 0 to match rate, channels and
* sample_valid_bits from reference params
@@ -1479,14 +1602,17 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev,
rate = fmt->sampling_frequency;
channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+ type = sof_ipc4_fmt_cfg_to_type(fmt->fmt_cfg);
if (params_rate(params) == rate && params_channels(params) == channels &&
- sample_valid_bits == valid_bits)
+ sample_valid_bits == valid_bits && sample_type == type)
break;
}
if (i == pin_fmts_size) {
- dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n",
- __func__, params_rate(params), sample_valid_bits, params_channels(params));
+ dev_err(sdev->dev,
+ "%s: Unsupported audio format: %uHz, %ubit, %u channels, type: %d\n",
+ __func__, params_rate(params), sample_valid_bits,
+ params_channels(params), sample_type);
return -EINVAL;
}
@@ -1882,7 +2008,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
int *ipc_config_size;
u32 **data;
int ipc_size, ret, out_ref_valid_bits;
- u32 out_ref_rate, out_ref_channels;
+ u32 out_ref_rate, out_ref_channels, out_ref_type;
u32 deep_buffer_dma_ms = 0;
bool single_output_bitdepth;
int i;
@@ -1923,10 +2049,13 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
host_dma_id = platform_params->stream_tag - 1;
pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id);
- /* Set SCS bit for S16_LE format only */
if (params_format(fe_params) == SNDRV_PCM_FORMAT_S16_LE)
pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK;
+ /* Set SCS bit for 8 and 16 bit formats */
+ if (params_physical_width(fe_params) <= 16)
+ pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK;
+
/*
* Despite its name the bitfield 'fifo_size' is used to define DMA buffer
* size. The expression calculates 2ms buffer size.
@@ -2051,6 +2180,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt;
out_ref_rate = in_fmt->sampling_frequency;
out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+ out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
if (!single_output_bitdepth)
out_ref_valid_bits =
@@ -2061,6 +2191,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
case snd_soc_dapm_dai_in:
out_ref_rate = params_rate(fe_params);
out_ref_channels = params_channels(fe_params);
+ ret = sof_ipc4_get_sample_type(sdev, fe_params);
+ if (ret < 0)
+ return ret;
+ out_ref_type = (u32)ret;
+
if (!single_output_bitdepth) {
out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params);
if (out_ref_valid_bits < 0)
@@ -2085,12 +2220,14 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt;
out_ref_valid_bits =
SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg);
+ out_ref_type = sof_ipc4_fmt_cfg_to_type(out_fmt->fmt_cfg);
}
output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget,
&copier_data->base_config,
available_fmt, out_ref_rate,
- out_ref_channels, out_ref_valid_bits);
+ out_ref_channels, out_ref_valid_bits,
+ out_ref_type);
if (output_fmt_index < 0)
return output_fmt_index;
@@ -2319,7 +2456,7 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,
struct sof_ipc4_gain *gain = swidget->private;
struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt;
struct sof_ipc4_audio_format *in_fmt;
- u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
+ u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type;
int input_fmt_index, output_fmt_index;
input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget,
@@ -2333,13 +2470,15 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,
out_ref_rate = in_fmt->sampling_frequency;
out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+ out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget,
&gain->data.base_config,
available_fmt,
out_ref_rate,
out_ref_channels,
- out_ref_valid_bits);
+ out_ref_valid_bits,
+ out_ref_type);
if (output_fmt_index < 0)
return output_fmt_index;
@@ -2362,7 +2501,7 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,
struct sof_ipc4_mixer *mixer = swidget->private;
struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt;
struct sof_ipc4_audio_format *in_fmt;
- u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
+ u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type;
int input_fmt_index, output_fmt_index;
input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget,
@@ -2376,13 +2515,15 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,
out_ref_rate = in_fmt->sampling_frequency;
out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+ out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget,
&mixer->base_config,
available_fmt,
out_ref_rate,
out_ref_channels,
- out_ref_valid_bits);
+ out_ref_valid_bits,
+ out_ref_type);
if (output_fmt_index < 0)
return output_fmt_index;
@@ -2406,7 +2547,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt;
struct sof_ipc4_audio_format *out_audio_fmt;
struct sof_ipc4_audio_format *in_audio_fmt;
- u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
+ u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type;
int output_fmt_index, input_fmt_index;
input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget,
@@ -2433,6 +2574,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
in_audio_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt;
out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_audio_fmt->fmt_cfg);
out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_audio_fmt->fmt_cfg);
+ out_ref_type = sof_ipc4_fmt_cfg_to_type(in_audio_fmt->fmt_cfg);
/*
* For capture, the SRC module should convert the rate to match the rate requested by the
@@ -2446,7 +2588,8 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
available_fmt,
out_ref_rate,
out_ref_channels,
- out_ref_valid_bits);
+ out_ref_valid_bits,
+ out_ref_type);
if (output_fmt_index < 0)
return output_fmt_index;
@@ -2570,20 +2713,22 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
struct sof_ipc4_audio_format *in_fmt;
struct sof_ipc4_pin_format *pin_fmt;
u32 out_ref_rate, out_ref_channels;
- int out_ref_valid_bits;
+ int out_ref_valid_bits, out_ref_type;
in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt;
out_ref_rate = in_fmt->sampling_frequency;
out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+ out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget,
&process->base_config,
available_fmt,
out_ref_rate,
out_ref_channels,
- out_ref_valid_bits);
+ out_ref_valid_bits,
+ out_ref_type);
if (output_fmt_index < 0)
return output_fmt_index;
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index 14ba58d..dfa1a6c 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -41,6 +41,15 @@
#define SOF_IPC4_FW_MAX_PAGE_COUNT 20
#define SOF_IPC4_FW_MAX_QUEUE_COUNT 8
+/* IPC4 sample types */
+#define SOF_IPC4_TYPE_MSB_INTEGER 0
+#define SOF_IPC4_TYPE_LSB_INTEGER 1
+#define SOF_IPC4_TYPE_SIGNED_INTEGER 2
+#define SOF_IPC4_TYPE_UNSIGNED_INTEGER 3
+#define SOF_IPC4_TYPE_FLOAT 4
+#define SOF_IPC4_TYPE_A_LAW 5
+#define SOF_IPC4_TYPE_MU_LAW 6
+
/* Node index and mask applicable for host copier and ALH/HDA type DAI copiers */
#define SOF_IPC4_NODE_INDEX_MASK 0xFF
#define SOF_IPC4_NODE_INDEX(x) ((x) & SOF_IPC4_NODE_INDEX_MASK)
@@ -109,6 +118,13 @@ enum sof_ipc4_copier_module_config_params {
SOF_IPC4_COPIER_MODULE_CFG_ATTENUATION,
};
+/* Scheduling domain, unset, Low Latency, or Data Processing */
+enum sof_comp_domain {
+ SOF_COMP_DOMAIN_UNSET = 0, /* Take domain value from manifest */
+ SOF_COMP_DOMAIN_LL, /* Low Latency scheduling domain */
+ SOF_COMP_DOMAIN_DP, /* Data Processing scheduling domain */
+};
+
struct sof_ipc4_copier_config_set_sink_format {
/* Id of sink */
u32 sink_id;
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 36ab75e1..db6973c 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -451,6 +451,9 @@ struct snd_sof_widget {
*/
bool dynamic_pipeline_widget;
+ /* Scheduling domain (enum sof_comp_domain), unset, Low Latency, or Data Processing */
+ u32 comp_domain;
+
struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */
struct snd_sof_pipeline *spipe;
diff --git a/sound/soc/sof/sof-client-probes-ipc3.c b/sound/soc/sof/sof-client-probes-ipc3.c
index 816df74..a78ec09 100644
--- a/sound/soc/sof/sof-client-probes-ipc3.c
+++ b/sound/soc/sof/sof-client-probes-ipc3.c
@@ -100,9 +100,11 @@ static int ipc3_probes_deinit(struct sof_client_dev *cdev)
}
static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
- void **params, size_t *num_params)
+ void **params, size_t *num_params,
+ enum sof_probe_info_type type)
{
size_t max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
+ struct device *dev = &cdev->auxdev.dev;
struct sof_ipc_probe_info_params msg = {{{0}}};
struct sof_ipc_probe_info_params *reply;
size_t bytes;
@@ -111,6 +113,11 @@ static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
*params = NULL;
*num_params = 0;
+ if (type != PROBES_INFO_ACTIVE_PROBES) {
+ dev_err(dev, "%s: info type %u not supported", __func__, type);
+ return -EOPNOTSUPP;
+ }
+
reply = kzalloc(max_msg_size, GFP_KERNEL);
if (!reply)
return -ENOMEM;
@@ -142,21 +149,25 @@ static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
}
/**
- * ipc3_probes_points_info - retrieve list of active probe points
+ * ipc3_probes_points_info - retrieve list of probe points
* @cdev: SOF client device
* @desc: Returned list of active probes
* @num_desc: Returned count of active probes
+ * @type: Either PROBES_INFO_ACTIVE_PROBES or PROBES_INFO_AVAILABE_PROBES
*
- * Host sends PROBE_POINT_INFO request to obtain list of active probe
- * points, valid for disconnection when given probe is no longer
- * required.
+ * If type is PROBES_INFO_ACTIVE_PROBES, host sends PROBE_POINT_INFO
+ * request to obtain list of active probe points, valid for
+ * disconnection when given probe is no longer required.
+ *
+ * Type PROBES_INFO_AVAILABE_PROBES is not yet supported.
*/
static int ipc3_probes_points_info(struct sof_client_dev *cdev,
struct sof_probe_point_desc **desc,
- size_t *num_desc)
+ size_t *num_desc,
+ enum sof_probe_info_type type)
{
return ipc3_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO,
- (void **)desc, num_desc);
+ (void **)desc, num_desc, type);
}
/**
diff --git a/sound/soc/sof/sof-client-probes-ipc4.c b/sound/soc/sof/sof-client-probes-ipc4.c
index 603aed222..758a56d 100644
--- a/sound/soc/sof/sof-client-probes-ipc4.c
+++ b/sound/soc/sof/sof-client-probes-ipc4.c
@@ -8,7 +8,7 @@
#include <sound/soc.h>
#include <sound/sof/ipc4/header.h>
#include <uapi/sound/sof/header.h>
-#include "sof-priv.h"
+#include "sof-audio.h"
#include "ipc4-priv.h"
#include "sof-client.h"
#include "sof-client-probes.h"
@@ -28,6 +28,7 @@ enum sof_ipc4_probe_runtime_param {
SOF_IPC4_PROBE_INJECTION_DMA_DETACH,
SOF_IPC4_PROBE_POINTS,
SOF_IPC4_PROBE_POINTS_DISCONNECT,
+ SOF_IPC4_PROBE_POINTS_AVAILABLE,
};
struct sof_ipc4_probe_gtw_cfg {
@@ -49,14 +50,42 @@ enum sof_ipc4_probe_type {
SOF_IPC4_PROBE_TYPE_INTERNAL
};
+#define SOF_IPC4_PROBE_TYPE_SHIFT 24
+#define SOF_IPC4_PROBE_TYPE_MASK GENMASK(25, 24)
+#define SOF_IPC4_PROBE_TYPE_GET(x) (((x) & SOF_IPC4_PROBE_TYPE_MASK) \
+ >> SOF_IPC4_PROBE_TYPE_SHIFT)
+#define SOF_IPC4_PROBE_IDX_SHIFT 26
+#define SOF_IPC4_PROBE_IDX_MASK GENMASK(31, 26)
+#define SOF_IPC4_PROBE_IDX_GET(x) (((x) & SOF_IPC4_PROBE_IDX_MASK) \
+ >> SOF_IPC4_PROBE_IDX_SHIFT)
+
struct sof_ipc4_probe_point {
u32 point_id;
u32 purpose;
u32 stream_tag;
} __packed __aligned(4);
+struct sof_ipc4_probe_info {
+ unsigned int num_elems;
+ DECLARE_FLEX_ARRAY(struct sof_ipc4_probe_point, points);
+} __packed;
+
#define INVALID_PIPELINE_ID 0xFF
+static const char *sof_probe_ipc4_type_string(u32 type)
+{
+ switch (type) {
+ case SOF_IPC4_PROBE_TYPE_INPUT:
+ return "input";
+ case SOF_IPC4_PROBE_TYPE_OUTPUT:
+ return "output";
+ case SOF_IPC4_PROBE_TYPE_INTERNAL:
+ return "internal";
+ default:
+ return "UNKNOWN";
+ }
+}
+
/**
* sof_ipc4_probe_get_module_info - Get IPC4 module info for probe module
* @cdev: SOF client device
@@ -164,25 +193,113 @@ static int ipc4_probes_deinit(struct sof_client_dev *cdev)
}
/**
- * ipc4_probes_points_info - retrieve list of active probe points
+ * ipc4_probes_points_info - retrieve list of probe points
* @cdev: SOF client device
* @desc: Returned list of active probes
* @num_desc: Returned count of active probes
+ * @type: Either PROBES_INFO_ACTIVE_PROBES or PROBES_INFO_AVAILABE_PROBES
* @return: 0 on success, negative error code on error
*
- * Dummy implementation returning empty list of probes.
+ * Returns list if active probe points if type is
+ * PROBES_INFO_ACTIVE_PROBES, or list of all available probe points if
+ * type is PROBES_INFO_AVAILABE_PROBES.
*/
static int ipc4_probes_points_info(struct sof_client_dev *cdev,
struct sof_probe_point_desc **desc,
- size_t *num_desc)
+ size_t *num_desc,
+ enum sof_probe_info_type type)
{
- /* TODO: Firmware side implementation needed first */
- *desc = NULL;
- *num_desc = 0;
+ struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev);
+ struct device *dev = &cdev->auxdev.dev;
+ struct sof_ipc4_probe_info *info;
+ struct sof_ipc4_msg msg;
+ u32 param_id;
+ int i, ret;
+
+ if (!mentry)
+ return -ENODEV;
+
+ switch (type) {
+ case PROBES_INFO_ACTIVE_PROBES:
+ param_id = SOF_IPC4_PROBE_POINTS;
+ break;
+ case PROBES_INFO_AVAILABE_PROBES:
+ param_id = SOF_IPC4_PROBE_POINTS_AVAILABLE;
+ break;
+ default:
+ dev_err(dev, "%s: info type %u not supported", __func__, type);
+ return -EOPNOTSUPP;
+ }
+
+ msg.primary = mentry->id;
+ msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+
+ msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(param_id);
+
+ msg.data_size = sof_client_get_ipc_max_payload_size(cdev);
+ msg.data_ptr = kzalloc(msg.data_size, GFP_KERNEL);
+ if (!msg.data_ptr)
+ return -ENOMEM;
+
+ ret = sof_client_ipc_set_get_data(cdev, &msg, false);
+ if (ret) {
+ kfree(msg.data_ptr);
+ return ret;
+ }
+ info = msg.data_ptr;
+ *num_desc = info->num_elems;
+ dev_dbg(dev, "%s: got %zu probe points", __func__, *num_desc);
+
+ *desc = kzalloc(*num_desc * sizeof(**desc), GFP_KERNEL);
+ if (!*desc) {
+ kfree(msg.data_ptr);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < *num_desc; i++) {
+ (*desc)[i].buffer_id = info->points[i].point_id;
+ (*desc)[i].purpose = info->points[i].purpose;
+ (*desc)[i].stream_tag = info->points[i].stream_tag;
+ }
+ kfree(msg.data_ptr);
+
return 0;
}
/**
+ * ipc4_probes_point_print - Human readable print of probe point descriptor
+ * @cdev: SOF client device
+ * @buf: Buffer to print to
+ * @size: Available bytes in buffer
+ * @desc: Describes the probe point to print
+ * @return: Number of bytes printed or an error code (snprintf return value)
+ */
+static int ipc4_probes_point_print(struct sof_client_dev *cdev, char *buf, size_t size,
+ struct sof_probe_point_desc *desc)
+{
+ struct device *dev = &cdev->auxdev.dev;
+ struct snd_sof_widget *swidget;
+ int ret;
+
+ swidget = sof_client_ipc4_find_swidget_by_id(cdev, SOF_IPC4_MOD_ID_GET(desc->buffer_id),
+ SOF_IPC4_MOD_INSTANCE_GET(desc->buffer_id));
+ if (!swidget)
+ dev_err(dev, "%s: Failed to find widget for module %lu.%lu\n",
+ __func__, SOF_IPC4_MOD_ID_GET(desc->buffer_id),
+ SOF_IPC4_MOD_INSTANCE_GET(desc->buffer_id));
+
+ ret = snprintf(buf, size, "%#x,%#x,%#x\t%s %s buf idx %lu %s\n",
+ desc->buffer_id, desc->purpose, desc->stream_tag,
+ swidget ? swidget->widget->name : "<unknown>",
+ sof_probe_ipc4_type_string(SOF_IPC4_PROBE_TYPE_GET(desc->buffer_id)),
+ SOF_IPC4_PROBE_IDX_GET(desc->buffer_id),
+ desc->stream_tag ? "(connected)" : "");
+
+ return ret;
+}
+
+/**
* ipc4_probes_points_add - connect specified probes
* @cdev: SOF client device
* @desc: List of probe points to connect
@@ -202,7 +319,7 @@ static int ipc4_probes_points_add(struct sof_client_dev *cdev,
int i, ret;
if (!mentry)
- return -ENODEV;
+ return -EOPNOTSUPP;
/* The sof_probe_point_desc and sof_ipc4_probe_point structs
* are of same size and even the integers are the same in the
@@ -286,6 +403,7 @@ const struct sof_probes_ipc_ops ipc4_probe_ops = {
.init = ipc4_probes_init,
.deinit = ipc4_probes_deinit,
.points_info = ipc4_probes_points_info,
+ .point_print = ipc4_probes_point_print,
.points_add = ipc4_probes_points_add,
.points_remove = ipc4_probes_points_remove,
};
diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c
index 663c0d3c..aaf0ae4 100644
--- a/sound/soc/sof/sof-client-probes.c
+++ b/sound/soc/sof/sof-client-probes.c
@@ -17,8 +17,14 @@
#include <sound/soc.h>
#include <sound/sof/header.h>
+#include <sound/sof/ipc4/header.h>
#include "sof-client.h"
#include "sof-client-probes.h"
+#include "sof-audio.h"
+
+#ifdef CONFIG_SND_SOC_SOF_IPC4
+#include "ipc4-priv.h"
+#endif
#define SOF_PROBES_SUSPEND_DELAY_MS 3000
/* only extraction supported for now */
@@ -69,7 +75,8 @@ static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream,
int i, ret;
/* disconnect all probe points */
- ret = ipc->points_info(cdev, &desc, &num_desc);
+ ret = ipc->points_info(cdev, &desc, &num_desc,
+ PROBES_INFO_ACTIVE_PROBES);
if (ret < 0) {
dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
goto exit;
@@ -189,7 +196,8 @@ static const struct snd_compress_ops sof_probes_compressed_ops = {
};
static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos,
+ enum sof_probe_info_type type)
{
struct sof_client_dev *cdev = file->private_data;
struct sof_probes_priv *priv = cdev->data;
@@ -216,16 +224,20 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
goto exit;
}
- ret = ipc->points_info(cdev, &desc, &num_desc);
+ ret = ipc->points_info(cdev, &desc, &num_desc, type);
if (ret < 0)
goto pm_error;
for (i = 0; i < num_desc; i++) {
offset = strlen(buf);
remaining = PAGE_SIZE - offset;
- ret = snprintf(buf + offset, remaining,
- "Id: %#010x Purpose: %u Node id: %#x\n",
- desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
+ if (ipc->point_print)
+ ret = ipc->point_print(cdev, buf + offset, remaining, &desc[i]);
+ else
+ ret = snprintf(buf + offset, remaining,
+ "Id: %#010x Purpose: %u Node id: %#x\n",
+ desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
+
if (ret < 0 || ret >= remaining) {
/* truncate the output buffer at the last full line */
buf[offset] = '\0';
@@ -247,6 +259,22 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
return ret;
}
+static ssize_t sof_probes_dfs_active_points_read(struct file *file,
+ char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return sof_probes_dfs_points_read(file, to, count, ppos,
+ PROBES_INFO_ACTIVE_PROBES);
+}
+
+static ssize_t sof_probes_dfs_available_points_read(struct file *file,
+ char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return sof_probes_dfs_points_read(file, to, count, ppos,
+ PROBES_INFO_AVAILABE_PROBES);
+}
+
static ssize_t
sof_probes_dfs_points_write(struct file *file, const char __user *from,
size_t count, loff_t *ppos)
@@ -296,15 +324,23 @@ sof_probes_dfs_points_write(struct file *file, const char __user *from,
return ret;
}
-static const struct file_operations sof_probes_points_fops = {
+static const struct file_operations sof_probes_active_points_fops = {
.open = simple_open,
- .read = sof_probes_dfs_points_read,
+ .read = sof_probes_dfs_active_points_read,
.write = sof_probes_dfs_points_write,
.llseek = default_llseek,
.owner = THIS_MODULE,
};
+static const struct file_operations sof_probes_available_points_fops = {
+ .open = simple_open,
+ .read = sof_probes_dfs_available_points_read,
+ .llseek = default_llseek,
+
+ .owner = THIS_MODULE,
+};
+
static ssize_t
sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
size_t count, loff_t *ppos)
@@ -449,13 +485,17 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev,
/* create read-write probes_points debugfs entry */
priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot,
- cdev, &sof_probes_points_fops);
+ cdev, &sof_probes_active_points_fops);
/* create read-write probe_points_remove debugfs entry */
priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644,
dfsroot, cdev,
&sof_probes_points_remove_fops);
+ /* create read-write probes_points debugfs entry */
+ priv->dfs_points = debugfs_create_file("probe_points_available", 0644, dfsroot,
+ cdev, &sof_probes_available_points_fops);
+
links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL);
cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL);
if (!links || !cpus) {
@@ -485,7 +525,7 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev,
card->dai_link = links;
/* set idle_bias_off to prevent the core from resuming the card->dev */
- card->dapm.idle_bias_off = true;
+ card->dapm.idle_bias = false;
snd_soc_card_set_drvdata(card, cdev);
diff --git a/sound/soc/sof/sof-client-probes.h b/sound/soc/sof/sof-client-probes.h
index da04d65..5fb9555 100644
--- a/sound/soc/sof/sof-client-probes.h
+++ b/sound/soc/sof/sof-client-probes.h
@@ -34,13 +34,20 @@ struct sof_probe_point_desc {
unsigned int stream_tag;
} __packed;
+enum sof_probe_info_type {
+ PROBES_INFO_ACTIVE_PROBES,
+ PROBES_INFO_AVAILABE_PROBES,
+};
+
struct sof_probes_ipc_ops {
int (*init)(struct sof_client_dev *cdev, u32 stream_tag,
size_t buffer_size);
int (*deinit)(struct sof_client_dev *cdev);
int (*points_info)(struct sof_client_dev *cdev,
struct sof_probe_point_desc **desc,
- size_t *num_desc);
+ size_t *num_desc, enum sof_probe_info_type type);
+ int (*point_print)(struct sof_client_dev *cdev, char *buf, size_t size,
+ struct sof_probe_point_desc *desc);
int (*points_add)(struct sof_client_dev *cdev,
struct sof_probe_point_desc *desc,
size_t num_desc);
diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c
index 4c79513..2dbfc76 100644
--- a/sound/soc/sof/sof-client.c
+++ b/sound/soc/sof/sof-client.c
@@ -45,13 +45,30 @@ struct sof_state_event_entry {
struct list_head list;
};
+/**
+ * struct sof_client_dev_entry - client device entry for internal management use
+ * @sdev: pointer to SOF core device struct
+ * @list: item in SOF core client dev list
+ * @client_dev: SOF client device
+ */
+struct sof_client_dev_entry {
+ struct snd_sof_dev *sdev;
+ struct list_head list;
+
+ struct sof_client_dev client_dev;
+};
+
+#define cdev_to_centry(cdev) \
+ container_of(cdev, struct sof_client_dev_entry, client_dev)
+
static void sof_client_auxdev_release(struct device *dev)
{
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
+ struct sof_client_dev_entry *centry = cdev_to_centry(cdev);
kfree(cdev->auxdev.dev.platform_data);
- kfree(cdev);
+ kfree(centry);
}
static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data,
@@ -208,15 +225,18 @@ void sof_unregister_clients(struct snd_sof_dev *sdev)
int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
const void *data, size_t size)
{
+ struct sof_client_dev_entry *centry;
struct auxiliary_device *auxdev;
struct sof_client_dev *cdev;
int ret;
- cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
- if (!cdev)
+ centry = kzalloc(sizeof(*centry), GFP_KERNEL);
+ if (!centry)
return -ENOMEM;
- cdev->sdev = sdev;
+ cdev = ¢ry->client_dev;
+
+ centry->sdev = sdev;
auxdev = &cdev->auxdev;
auxdev->name = name;
auxdev->dev.parent = sdev->dev;
@@ -246,7 +266,7 @@ int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
/* add to list of SOF client devices */
mutex_lock(&sdev->ipc_client_mutex);
- list_add(&cdev->list, &sdev->ipc_client_list);
+ list_add(¢ry->list, &sdev->ipc_client_list);
mutex_unlock(&sdev->ipc_client_mutex);
return 0;
@@ -255,7 +275,7 @@ int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
kfree(cdev->auxdev.dev.platform_data);
err_dev_add_data:
- kfree(cdev);
+ kfree(centry);
return ret;
}
@@ -263,7 +283,7 @@ EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, "SND_SOC_SOF_CLIENT");
void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id)
{
- struct sof_client_dev *cdev;
+ struct sof_client_dev_entry *centry;
mutex_lock(&sdev->ipc_client_mutex);
@@ -271,9 +291,11 @@ void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 i
* sof_client_auxdev_release() will be invoked to free up memory
* allocations through put_device()
*/
- list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
+ list_for_each_entry(centry, &sdev->ipc_client_list, list) {
+ struct sof_client_dev *cdev = ¢ry->client_dev;
+
if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) {
- list_del(&cdev->list);
+ list_del(¢ry->list);
auxiliary_device_delete(&cdev->auxdev);
auxiliary_device_uninit(&cdev->auxdev);
break;
@@ -287,15 +309,17 @@ EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, "SND_SOC_SOF_CLIENT");
int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
void *reply_data, size_t reply_bytes)
{
- if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
struct sof_ipc_cmd_hdr *hdr = ipc_msg;
- return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, hdr->size,
+ return sof_ipc_tx_message(sdev->ipc, ipc_msg, hdr->size,
reply_data, reply_bytes);
- } else if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ } else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct sof_ipc4_msg *msg = ipc_msg;
- return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, msg->data_size,
+ return sof_ipc_tx_message(sdev->ipc, ipc_msg, msg->data_size,
reply_data, reply_bytes);
}
@@ -305,16 +329,18 @@ EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, "SND_SOC_SOF_CLIENT");
int sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf)
{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
if (IS_ENABLED(CONFIG_SND_SOC_SOF_IPC3) &&
- cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+ sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
struct sof_ipc_cmd_hdr *hdr = ipc_msg;
if (hdr->size < sizeof(hdr)) {
- dev_err(cdev->sdev->dev, "The received message size is invalid\n");
+ dev_err(sdev->dev, "The received message size is invalid\n");
return -EINVAL;
}
- sof_ipc3_do_rx_work(cdev->sdev, ipc_msg, msg_buf);
+ sof_ipc3_do_rx_work(sdev, ipc_msg, msg_buf);
return 0;
}
@@ -325,16 +351,17 @@ EXPORT_SYMBOL_NS_GPL(sof_client_ipc_rx_message, "SND_SOC_SOF_CLIENT");
int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg,
bool set)
{
- if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
struct sof_ipc_cmd_hdr *hdr = ipc_msg;
- return sof_ipc_set_get_data(cdev->sdev->ipc, ipc_msg, hdr->size,
- set);
- } else if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ return sof_ipc_set_get_data(sdev->ipc, ipc_msg, hdr->size, set);
+ } else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct sof_ipc4_msg *msg = ipc_msg;
- return sof_ipc_set_get_data(cdev->sdev->ipc, ipc_msg,
- msg->data_size, set);
+ return sof_ipc_set_get_data(sdev->ipc, ipc_msg, msg->data_size,
+ set);
}
return -EINVAL;
@@ -344,7 +371,7 @@ EXPORT_SYMBOL_NS_GPL(sof_client_ipc_set_get_data, "SND_SOC_SOF_CLIENT");
#ifdef CONFIG_SND_SOC_SOF_IPC4
struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, const guid_t *uuid)
{
- struct snd_sof_dev *sdev = c->sdev;
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(c);
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4)
return sof_ipc4_find_module_by_uuid(sdev, uuid);
@@ -353,16 +380,31 @@ struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c,
return NULL;
}
EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_module, "SND_SOC_SOF_CLIENT");
+
+struct snd_sof_widget *sof_client_ipc4_find_swidget_by_id(struct sof_client_dev *cdev,
+ u32 module_id, int instance_id)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4)
+ return sof_ipc4_find_swidget_by_ids(sdev, module_id, instance_id);
+ dev_err(sdev->dev, "Only supported with IPC4\n");
+
+ return NULL;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_swidget_by_id, "SND_SOC_SOF_CLIENT");
#endif
int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)
{
const struct auxiliary_driver *adrv;
- struct sof_client_dev *cdev;
+ struct sof_client_dev_entry *centry;
mutex_lock(&sdev->ipc_client_mutex);
- list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
+ list_for_each_entry(centry, &sdev->ipc_client_list, list) {
+ struct sof_client_dev *cdev = ¢ry->client_dev;
+
/* Skip devices without loaded driver */
if (!cdev->auxdev.dev.driver)
continue;
@@ -381,11 +423,13 @@ EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, "SND_SOC_SOF_CLIENT");
int sof_resume_clients(struct snd_sof_dev *sdev)
{
const struct auxiliary_driver *adrv;
- struct sof_client_dev *cdev;
+ struct sof_client_dev_entry *centry;
mutex_lock(&sdev->ipc_client_mutex);
- list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
+ list_for_each_entry(centry, &sdev->ipc_client_list, list) {
+ struct sof_client_dev *cdev = ¢ry->client_dev;
+
/* Skip devices without loaded driver */
if (!cdev->auxdev.dev.driver)
continue;
@@ -403,14 +447,18 @@ EXPORT_SYMBOL_NS_GPL(sof_resume_clients, "SND_SOC_SOF_CLIENT");
struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev)
{
- return cdev->sdev->debugfs_root;
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ return sdev->debugfs_root;
}
EXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, "SND_SOC_SOF_CLIENT");
/* DMA buffer allocation in client drivers must use the core SOF device */
struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev)
{
- return cdev->sdev->dev;
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ return sdev->dev;
}
EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, "SND_SOC_SOF_CLIENT");
@@ -498,10 +546,10 @@ int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
if (!callback)
return -EINVAL;
- if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
if (!(ipc_msg_type & SOF_GLB_TYPE_MASK))
return -EINVAL;
- } else if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ } else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
if (!(ipc_msg_type & SOF_IPC4_NOTIFICATION_TYPE_MASK))
return -EINVAL;
} else {
@@ -611,3 +659,11 @@ enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev)
return sdev->fw_state;
}
EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, "SND_SOC_SOF_CLIENT");
+
+struct snd_sof_dev *sof_client_dev_to_sof_dev(struct sof_client_dev *cdev)
+{
+ struct sof_client_dev_entry *centry = cdev_to_centry(cdev);
+
+ return centry->sdev;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_dev_to_sof_dev, "SND_SOC_SOF_CLIENT");
diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h
index b6ccc2c..1a9015e 100644
--- a/sound/soc/sof/sof-client.h
+++ b/sound/soc/sof/sof-client.h
@@ -18,19 +18,13 @@ struct sof_ipc4_fw_module;
/**
* struct sof_client_dev - SOF client device
* @auxdev: auxiliary device
- * @sdev: pointer to SOF core device struct
- * @list: item in SOF core client dev list
* @data: device specific data
*/
struct sof_client_dev {
struct auxiliary_device auxdev;
- struct snd_sof_dev *sdev;
- struct list_head list;
void *data;
};
-#define sof_client_dev_to_sof_dev(cdev) ((cdev)->sdev)
-
#define auxiliary_dev_to_sof_client_dev(auxiliary_dev) \
container_of(auxiliary_dev, struct sof_client_dev, auxdev)
@@ -47,6 +41,8 @@ int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg,
bool set);
struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, const guid_t *u);
+struct snd_sof_widget *sof_client_ipc4_find_swidget_by_id(struct sof_client_dev *cdev,
+ u32 module_id, int instance_id);
struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev);
struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev);
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index abbb5ee..0f624d8 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -838,7 +838,11 @@ int sof_stream_pcm_close(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream);
/* SOF client support */
+struct sof_client_dev;
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_CLIENT)
+struct snd_sof_dev *sof_client_dev_to_sof_dev(struct sof_client_dev *cdev);
+
int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
const void *data, size_t size);
void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id);
@@ -849,6 +853,11 @@ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev);
int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state);
int sof_resume_clients(struct snd_sof_dev *sdev);
#else /* CONFIG_SND_SOC_SOF_CLIENT */
+static inline struct snd_sof_dev *
+sof_client_dev_to_sof_dev(struct sof_client_dev *cdev) {
+ return NULL;
+}
+
static inline int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name,
u32 id, const void *data, size_t size)
{