aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinaro CI <ci_notify@linaro.org>2020-12-13 22:54:35 +0000
committerLinaro CI <ci_notify@linaro.org>2020-12-13 22:54:35 +0000
commit4b8bf7dc24f12ad055199fb3ec35420235faed98 (patch)
treefe0f22b424061434211dd958697d7890ca7a2131
parent83bcafa17e28c2b1efe0215e8febc3ce5687c6b3 (diff)
parenta196f371b5435d42667a435e191362b1218b84a0 (diff)
Merge remote-tracking branch 'audio/tracking-qcomlt-audio' into integration-linux-qcomlt
-rw-r--r--Documentation/devicetree/bindings/clock/qcom,aoncc-sm8250.yaml58
-rw-r--r--Documentation/devicetree/bindings/clock/qcom,audiocc-sm8250.yaml58
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-pinctrl.yaml124
-rw-r--r--Documentation/devicetree/bindings/slimbus/slim-ngd-qcom-ctrl.txt1
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml68
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,sc8250.yaml157
-rw-r--r--drivers/clk/qcom/Kconfig6
-rw-r--r--drivers/clk/qcom/Makefile1
-rw-r--r--drivers/clk/qcom/lpass-gfm-sm8250.c320
-rw-r--r--drivers/mfd/mfd-core.c7
-rw-r--r--drivers/mfd/wcd934x.c4
-rw-r--r--drivers/pinctrl/qcom/Kconfig8
-rw-r--r--drivers/pinctrl/qcom/Makefile1
-rw-r--r--drivers/pinctrl/qcom/pinctrl-lpass-lpi.c623
-rw-r--r--drivers/slimbus/Kconfig2
-rw-r--r--drivers/slimbus/qcom-ngd-ctrl.c124
-rw-r--r--include/dt-bindings/clock/qcom,sm8250-lpass-aoncc.h11
-rw-r--r--include/dt-bindings/clock/qcom,sm8250-lpass-audiocc.h13
-rw-r--r--sound/soc/codecs/Kconfig8
-rw-r--r--sound/soc/codecs/Makefile4
-rw-r--r--sound/soc/codecs/lpass-va-macro.c1501
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c2472
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.h15
-rw-r--r--sound/soc/codecs/wcd934x.c10
-rw-r--r--sound/soc/qcom/Kconfig11
-rw-r--r--sound/soc/qcom/Makefile2
-rw-r--r--sound/soc/qcom/apq8096.c48
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-clocks.c1
-rw-r--r--sound/soc/qcom/sm8250.c229
29 files changed, 5882 insertions, 5 deletions
diff --git a/Documentation/devicetree/bindings/clock/qcom,aoncc-sm8250.yaml b/Documentation/devicetree/bindings/clock/qcom,aoncc-sm8250.yaml
new file mode 100644
index 000000000000..c40a74b5d672
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,aoncc-sm8250.yaml
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/qcom,aoncc-sm8250.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Clock bindings for LPASS Always ON Clock Controller on SM8250 SoCs
+
+maintainers:
+ - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+
+description: |
+ The clock consumer should specify the desired clock by having the clock
+ ID in its "clocks" phandle cell.
+ See include/dt-bindings/clock/qcom,sm8250-lpass-aoncc.h for the full list
+ of Audio Clock controller clock IDs.
+
+properties:
+ compatible:
+ const: qcom,sm8250-lpass-aon
+
+ reg:
+ maxItems: 1
+
+ '#clock-cells':
+ const: 1
+
+ clocks:
+ items:
+ - description: LPASS Core voting clock
+ - description: Glitch Free Mux register clock
+
+ clock-names:
+ items:
+ - const: core
+ - const: bus
+
+required:
+ - compatible
+ - reg
+ - '#clock-cells'
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,sm8250-lpass-aoncc.h>
+ #include <dt-bindings/sound/qcom,q6afe.h>
+ clock-controller@3800000 {
+ #clock-cells = <1>;
+ compatible = "qcom,sm8250-lpass-aon";
+ reg = <0x03380000 0x40000>;
+ clocks = <&q6afecc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+ <&q6afecc LPASS_CLK_ID_TX_CORE_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
+ clock-names = "core", "bus";
+ };
diff --git a/Documentation/devicetree/bindings/clock/qcom,audiocc-sm8250.yaml b/Documentation/devicetree/bindings/clock/qcom,audiocc-sm8250.yaml
new file mode 100644
index 000000000000..915d76206ad0
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,audiocc-sm8250.yaml
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/qcom,audiocc-sm8250.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Clock bindings for LPASS Audio Clock Controller on SM8250 SoCs
+
+maintainers:
+ - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+
+description: |
+ The clock consumer should specify the desired clock by having the clock
+ ID in its "clocks" phandle cell.
+ See include/dt-bindings/clock/qcom,sm8250-lpass-audiocc.h for the full list
+ of Audio Clock controller clock IDs.
+
+properties:
+ compatible:
+ const: qcom,sm8250-lpass-audiocc
+
+ reg:
+ maxItems: 1
+
+ '#clock-cells':
+ const: 1
+
+ clocks:
+ items:
+ - description: LPASS Core voting clock
+ - description: Glitch Free Mux register clock
+
+ clock-names:
+ items:
+ - const: core
+ - const: bus
+
+required:
+ - compatible
+ - reg
+ - '#clock-cells'
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,sm8250-lpass-audiocc.h>
+ #include <dt-bindings/sound/qcom,q6afe.h>
+ clock-controller@3300000 {
+ #clock-cells = <1>;
+ compatible = "qcom,sm8250-lpass-audiocc";
+ reg = <0x03300000 0x30000>;
+ clocks = <&q6afecc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+ <&q6afecc LPASS_CLK_ID_TX_CORE_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
+ clock-names = "core", "bus";
+ };
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-pinctrl.yaml
new file mode 100644
index 000000000000..0e0c98ba9376
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-pinctrl.yaml
@@ -0,0 +1,124 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/qcom,lpass-lpi-pinctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Technologies, Inc. Low Power Audio SubSystem (LPASS)
+ Low Power Island (LPI) TLMM block
+
+maintainers:
+ - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+
+description: |
+ This binding describes the Top Level Mode Multiplexer block found in the
+ LPASS LPI IP on most Qualcomm SoCs
+
+properties:
+ compatible:
+ const: qcom,sm8250-lpass-lpi-pinctrl
+
+ reg:
+ minItems: 2
+ maxItems: 2
+
+ clocks:
+ items:
+ - description: LPASS Core voting clock
+ - description: LPASS Audio voting clock
+
+ clock-names:
+ items:
+ - const: core
+ - const: audio
+
+ gpio-controller: true
+
+ '#gpio-cells':
+ description: Specifying the pin number and flags, as defined in
+ include/dt-bindings/gpio/gpio.h
+ const: 2
+
+ gpio-ranges:
+ maxItems: 1
+
+#PIN CONFIGURATION NODES
+patternProperties:
+ '^.*$':
+ if:
+ type: object
+ then:
+ properties:
+ pins:
+ description:
+ List of gpio pins affected by the properties specified in this
+ subnode.
+ items:
+ oneOf:
+ - pattern: "^gpio([0-9]|[1-9][0-9])$"
+ minItems: 1
+ maxItems: 14
+
+ function:
+ enum: [ gpio, func1, func2, func3, func4, func5 ]
+ description:
+ Specify the alternative function to be configured for the specified
+ pins.
+
+ drive-strength:
+ enum: [2, 4, 6, 8, 10, 12, 14, 16]
+ default: 2
+ description:
+ Selects the drive strength for the specified pins, in mA.
+
+ slew-rate:
+ enum: [0, 1, 2, 3]
+ default: 0
+ description: |
+ 0: No adjustments
+ 1: Higher Slew rate (faster edges)
+ 2: Lower Slew rate (slower edges)
+ 3: Reserved (No adjustments)
+
+ bias-pull-down: true
+
+ bias-pull-up: true
+
+ bias-disable: true
+
+ output-high: true
+
+ output-low: true
+
+ required:
+ - pins
+ - function
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+ - clock-names
+ - gpio-controller
+ - '#gpio-cells'
+ - gpio-ranges
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/sound/qcom,q6afe.h>
+ lpi_tlmm: pinctrl@33c0000 {
+ compatible = "qcom,sm8250-lpass-lpi-pinctrl";
+ reg = <0 0x33c0000 0x0 0x20000>,
+ <0 0x355a000 0x0 0x1000>;
+ clocks = <&q6afecc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+ <&q6afecc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
+ clock-names = "core", "audio";
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&lpi_tlmm 0 0 14>;
+ };
diff --git a/Documentation/devicetree/bindings/slimbus/slim-ngd-qcom-ctrl.txt b/Documentation/devicetree/bindings/slimbus/slim-ngd-qcom-ctrl.txt
index e94a2ad3a710..1c615b622d07 100644
--- a/Documentation/devicetree/bindings/slimbus/slim-ngd-qcom-ctrl.txt
+++ b/Documentation/devicetree/bindings/slimbus/slim-ngd-qcom-ctrl.txt
@@ -14,6 +14,7 @@ Please refer to slimbus/bus.txt for details of the common SLIMBus bindings.
must be one of the following.
"qcom,slim-ngd-v1.5.0" for MSM8996
"qcom,slim-ngd-v2.1.0" for SDM845
+ "qcom,slim-ngd-v2.2.0" for SM8250
- reg:
Usage: required
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
new file mode 100644
index 000000000000..214b4b117d39
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,lpass-va-macro.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: LPASS(Low Power Audio Subsystem) VA Macro audio codec DT bindings
+
+maintainers:
+ - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+
+properties:
+ compatible:
+ const: qcom,sm8250-lpass-va-macro
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 1
+
+ '#clock-cells':
+ const: 0
+
+ clocks:
+ maxItems: 3
+
+ clock-names:
+ items:
+ - const: mclk
+ - const: core
+ - const: dcodec
+
+ clock-output-names:
+ items:
+ - const: fsgen
+
+ qcom,dmic-sample-rate:
+ description: dmic sample rate
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ vdd-micb-supply:
+ description: phandle to voltage regulator of MIC Bias
+
+required:
+ - compatible
+ - reg
+ - "#sound-dai-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,sm8250-lpass-aoncc.h>
+ #include <dt-bindings/sound/qcom,q6afe.h>
+ codec@3370000 {
+ compatible = "qcom,sm8250-lpass-va-macro";
+ reg = <0x3370000 0x1000>;
+ #sound-dai-cells = <1>;
+ #clock-cells = <0>;
+ clocks = <&aoncc LPASS_CDC_VA_MCLK>,
+ <&q6afecc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+ <&q6afecc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
+ clock-names = "mclk", "core", "dcodec";
+ clock-output-names = "fsgen";
+ qcom,dmic-sample-rate = <600000>;
+ vdd-micb-supply = <&vreg_s4a_1p8>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/qcom,sc8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sc8250.yaml
new file mode 100644
index 000000000000..101da93e2e85
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,sc8250.yaml
@@ -0,0 +1,157 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,sc8250.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Technologies Inc. SC8250 ASoC sound card driver
+
+maintainers:
+ - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+
+description:
+ This bindings describes SC8250 SoC based sound cards
+ which uses LPASS internal codec for audio.
+
+properties:
+ compatible:
+ const: qcom,qrb5165-rb5
+
+ audio-routing:
+ $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+ description:
+ 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,
+ MicBias of codec and the jacks on the board.
+
+ model:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: User visible long sound card name
+
+patternProperties:
+ ".*-dai-link$":
+ description:
+ Each subnode represents a dai link. Subnodes of each dai links would be
+ cpu/codec dais.
+
+ type: object
+
+ properties:
+ link-name:
+ description: Indicates dai-link name and PCM stream name.
+ $ref: /schemas/types.yaml#/definitions/string
+ maxItems: 1
+
+ cpu:
+ description: Holds subnode which indicates cpu dai.
+ type: object
+ properties:
+ sound-dai: true
+
+ platform:
+ description: Holds subnode which indicates platform dai.
+ type: object
+ properties:
+ sound-dai: true
+
+ codec:
+ description: Holds subnode which indicates codec dai.
+ type: object
+ properties:
+ sound-dai: true
+
+ required:
+ - link-name
+ - cpu
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - model
+
+additionalProperties: false
+
+examples:
+
+ - |
+ #include <dt-bindings/sound/qcom,q6afe.h>
+ #include <dt-bindings/sound/qcom,q6asm.h>
+ sound {
+ compatible = "qcom,qrb5165-rb5";
+ model = "Qualcomm-qrb5165-RB5-WSA8815-Speakers-DMIC0";
+ audio-routing = "SpkrLeft IN", "WSA_SPK1 OUT",
+ "SpkrRight IN", "WSA_SPK2 OUT",
+ "VA DMIC0", "vdd-micb",
+ "VA DMIC1", "vdd-micb",
+ "MM_DL1", "MultiMedia1 Playback",
+ "MM_DL2", "MultiMedia2 Playback",
+ "MultiMedia3 Capture", "MM_UL3";
+
+ mm1-dai-link {
+ link-name = "MultiMedia0";
+ cpu {
+ sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>;
+ };
+ };
+
+ mm2-dai-link {
+ link-name = "MultiMedia2";
+ cpu {
+ sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA2>;
+ };
+ };
+
+ mm3-dai-link {
+ link-name = "MultiMedia3";
+ cpu {
+ sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA3>;
+ };
+ };
+
+ hdmi-dai-link {
+ link-name = "HDMI Playback";
+ cpu {
+ sound-dai = <&q6afedai TERTIARY_MI2S_RX>;
+ };
+
+ platform {
+ sound-dai = <&q6routing>;
+ };
+
+ codec {
+ sound-dai = <&lt9611_codec 0>;
+ };
+ };
+
+ wsa-dai-link {
+ link-name = "WSA Playback";
+ cpu {
+ sound-dai = <&q6afedai WSA_CODEC_DMA_RX_0>;
+ };
+
+ platform {
+ sound-dai = <&q6routing>;
+ };
+
+ codec {
+ sound-dai = <&left_spkr>, <&right_spkr>, <&swr0 0>, <&wsamacro>;
+ };
+ };
+
+ va-dai-link {
+ link-name = "VA Capture";
+ cpu {
+ sound-dai = <&q6afedai VA_CODEC_DMA_TX_0>;
+ };
+
+ platform {
+ sound-dai = <&q6routing>;
+ };
+
+ codec {
+ sound-dai = <&vamacro 0>;
+ };
+ };
+ };
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 3a965bd326d5..b619d861cb2e 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -502,4 +502,10 @@ config KRAITCC
Support for the Krait CPU clocks on Qualcomm devices.
Say Y if you want to support CPU frequency scaling.
+config CLK_GFM_LPASS_SM8250
+ tristate "SM8250 GFM LPASS Clocks"
+ help
+ Support for the Glitch Free Mux (GFM) Low power audio
+ subsystem (LPASS) clocks found on SM8250 SoCs.
+
endif
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 11ae86febe87..6f4f25ba9139 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -19,6 +19,7 @@ clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
# Keep alphabetically sorted by config
obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o
+obj-$(CONFIG_CLK_GFM_LPASS_SM8250) += lpass-gfm-sm8250.o
obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o
obj-$(CONFIG_IPQ_APSS_6018) += apss-ipq6018.o
obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o
diff --git a/drivers/clk/qcom/lpass-gfm-sm8250.c b/drivers/clk/qcom/lpass-gfm-sm8250.c
new file mode 100644
index 000000000000..d366c7c2abc7
--- /dev/null
+++ b/drivers/clk/qcom/lpass-gfm-sm8250.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * LPASS Audio CC and Always ON CC Glitch Free Mux clock driver
+ *
+ * Copyright (c) 2020 Linaro Ltd.
+ * Author: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/pm_clock.h>
+#include <linux/pm_runtime.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <dt-bindings/clock/qcom,sm8250-lpass-audiocc.h>
+#include <dt-bindings/clock/qcom,sm8250-lpass-aoncc.h>
+
+struct lpass_gfm {
+ struct device *dev;
+ void __iomem *base;
+};
+
+struct clk_gfm {
+ unsigned int mux_reg;
+ unsigned int mux_mask;
+ struct clk_hw hw;
+ struct lpass_gfm *priv;
+ void __iomem *gfm_mux;
+};
+
+#define GFM_MASK BIT(1)
+#define to_clk_gfm(_hw) container_of(_hw, struct clk_gfm, hw)
+
+static u8 clk_gfm_get_parent(struct clk_hw *hw)
+{
+ struct clk_gfm *clk = to_clk_gfm(hw);
+
+ return readl(clk->gfm_mux) & GFM_MASK;
+}
+
+static int clk_gfm_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_gfm *clk = to_clk_gfm(hw);
+ unsigned int val;
+
+ val = readl(clk->gfm_mux);
+
+ if (index)
+ val |= GFM_MASK;
+ else
+ val &= ~GFM_MASK;
+
+ writel(val, clk->gfm_mux);
+
+ return 0;
+}
+
+static const struct clk_ops clk_gfm_ops = {
+ .get_parent = clk_gfm_get_parent,
+ .set_parent = clk_gfm_set_parent,
+ .determine_rate = __clk_mux_determine_rate,
+};
+
+static struct clk_gfm lpass_gfm_va_mclk = {
+ .mux_reg = 0x20000,
+ .mux_mask = BIT(0),
+ .hw.init = &(struct clk_init_data) {
+ .name = "VA_MCLK",
+ .ops = &clk_gfm_ops,
+ .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
+ .num_parents = 2,
+ .parent_data = (const struct clk_parent_data[]){
+ {
+ .index = 0,
+ .fw_name = "LPASS_CLK_ID_TX_CORE_MCLK",
+ }, {
+ .index = 1,
+ .fw_name = "LPASS_CLK_ID_VA_CORE_MCLK",
+ },
+ },
+ },
+};
+
+static struct clk_gfm lpass_gfm_tx_npl = {
+ .mux_reg = 0x20000,
+ .mux_mask = BIT(0),
+ .hw.init = &(struct clk_init_data) {
+ .name = "TX_NPL",
+ .ops = &clk_gfm_ops,
+ .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
+ .parent_data = (const struct clk_parent_data[]){
+ {
+ .index = 0,
+ .fw_name = "LPASS_CLK_ID_TX_CORE_NPL_MCLK",
+ }, {
+ .index = 1,
+ .fw_name = "LPASS_CLK_ID_VA_CORE_2X_MCLK",
+ },
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_gfm lpass_gfm_wsa_mclk = {
+ .mux_reg = 0x220d8,
+ .mux_mask = BIT(0),
+ .hw.init = &(struct clk_init_data) {
+ .name = "WSA_MCLK",
+ .ops = &clk_gfm_ops,
+ .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
+ .parent_data = (const struct clk_parent_data[]){
+ {
+ .index = 0,
+ .fw_name = "LPASS_CLK_ID_TX_CORE_MCLK",
+ }, {
+ .index = 1,
+ .fw_name = "LPASS_CLK_ID_WSA_CORE_MCLK",
+ },
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_gfm lpass_gfm_wsa_npl = {
+ .mux_reg = 0x220d8,
+ .mux_mask = BIT(0),
+ .hw.init = &(struct clk_init_data) {
+ .name = "WSA_NPL",
+ .ops = &clk_gfm_ops,
+ .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
+ .parent_data = (const struct clk_parent_data[]){
+ {
+ .index = 0,
+ .fw_name = "LPASS_CLK_ID_TX_CORE_NPL_MCLK",
+ }, {
+ .index = 1,
+ .fw_name = "LPASS_CLK_ID_WSA_CORE_NPL_MCLK",
+ },
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_gfm lpass_gfm_rx_mclk_mclk2 = {
+ .mux_reg = 0x240d8,
+ .mux_mask = BIT(0),
+ .hw.init = &(struct clk_init_data) {
+ .name = "RX_MCLK_MCLK2",
+ .ops = &clk_gfm_ops,
+ .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
+ .parent_data = (const struct clk_parent_data[]){
+ {
+ .index = 0,
+ .fw_name = "LPASS_CLK_ID_TX_CORE_MCLK",
+ }, {
+ .index = 1,
+ .fw_name = "LPASS_CLK_ID_RX_CORE_MCLK",
+ },
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_gfm lpass_gfm_rx_npl = {
+ .mux_reg = 0x240d8,
+ .mux_mask = BIT(0),
+ .hw.init = &(struct clk_init_data) {
+ .name = "RX_NPL",
+ .ops = &clk_gfm_ops,
+ .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
+ .parent_data = (const struct clk_parent_data[]){
+ {
+ .index = 0,
+ .fw_name = "LPASS_CLK_ID_TX_CORE_NPL_MCLK",
+ }, {
+ .index = 1,
+ .fw_name = "LPASS_CLK_ID_RX_CORE_NPL_MCLK",
+ },
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_gfm *aoncc_gfm_clks[] = {
+ [LPASS_CDC_VA_MCLK] = &lpass_gfm_va_mclk,
+ [LPASS_CDC_TX_NPL] = &lpass_gfm_tx_npl,
+};
+
+static struct clk_hw_onecell_data aoncc_hw_onecell_data = {
+ .hws = {
+ [LPASS_CDC_VA_MCLK] = &lpass_gfm_va_mclk.hw,
+ [LPASS_CDC_TX_NPL] = &lpass_gfm_tx_npl.hw,
+ },
+ .num = ARRAY_SIZE(aoncc_gfm_clks),
+};
+
+static struct clk_gfm *audiocc_gfm_clks[] = {
+ [LPASS_CDC_WSA_NPL] = &lpass_gfm_wsa_npl,
+ [LPASS_CDC_WSA_MCLK] = &lpass_gfm_wsa_mclk,
+ [LPASS_CDC_RX_NPL] = &lpass_gfm_rx_npl,
+ [LPASS_CDC_RX_MCLK_MCLK2] = &lpass_gfm_rx_mclk_mclk2,
+};
+
+static struct clk_hw_onecell_data audiocc_hw_onecell_data = {
+ .hws = {
+ [LPASS_CDC_WSA_NPL] = &lpass_gfm_wsa_npl.hw,
+ [LPASS_CDC_WSA_MCLK] = &lpass_gfm_wsa_mclk.hw,
+ [LPASS_CDC_RX_NPL] = &lpass_gfm_rx_npl.hw,
+ [LPASS_CDC_RX_MCLK_MCLK2] = &lpass_gfm_rx_mclk_mclk2.hw,
+ },
+ .num = ARRAY_SIZE(audiocc_gfm_clks),
+};
+
+struct lpass_gfm_data {
+ struct clk_hw_onecell_data *onecell_data;
+ struct clk_gfm **gfm_clks;
+};
+
+static struct lpass_gfm_data audiocc_data = {
+ .onecell_data = &audiocc_hw_onecell_data,
+ .gfm_clks = audiocc_gfm_clks,
+};
+
+static struct lpass_gfm_data aoncc_data = {
+ .onecell_data = &aoncc_hw_onecell_data,
+ .gfm_clks = aoncc_gfm_clks,
+};
+
+static int lpass_gfm_clk_driver_probe(struct platform_device *pdev)
+{
+ const struct lpass_gfm_data *data;
+ struct device *dev = &pdev->dev;
+ struct clk_gfm *gfm;
+ struct lpass_gfm *cc;
+ int err, i;
+
+ data = of_device_get_match_data(dev);
+ if (!data)
+ return -EINVAL;
+
+ cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
+ if (!cc)
+ return -ENOMEM;
+
+ cc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(cc->base))
+ return PTR_ERR(cc->base);
+
+ pm_runtime_enable(dev);
+ err = pm_clk_create(dev);
+ if (err)
+ goto pm_clk_err;
+
+ err = of_pm_clk_add_clks(dev);
+ if (err < 0) {
+ dev_dbg(dev, "Failed to get lpass core voting clocks\n");
+ goto clk_reg_err;
+ }
+
+ for (i = 0; i < data->onecell_data->num; i++) {
+ if (!data->gfm_clks[i])
+ continue;
+
+ gfm = data->gfm_clks[i];
+ gfm->priv = cc;
+ gfm->gfm_mux = cc->base;
+ gfm->gfm_mux = gfm->gfm_mux + data->gfm_clks[i]->mux_reg;
+
+ err = devm_clk_hw_register(dev, &data->gfm_clks[i]->hw);
+ if (err)
+ goto clk_reg_err;
+
+ }
+
+ err = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ data->onecell_data);
+ if (err)
+ goto clk_reg_err;
+
+ return 0;
+
+clk_reg_err:
+ pm_clk_destroy(dev);
+pm_clk_err:
+ pm_runtime_disable(dev);
+ return err;
+}
+
+static const struct of_device_id lpass_gfm_clk_match_table[] = {
+ {
+ .compatible = "qcom,sm8250-lpass-aoncc",
+ .data = &aoncc_data,
+ },
+ {
+ .compatible = "qcom,sm8250-lpass-audiocc",
+ .data = &audiocc_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lpass_gfm_clk_match_table);
+
+static const struct dev_pm_ops lpass_gfm_pm_ops = {
+ SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
+};
+
+static struct platform_driver lpass_gfm_clk_driver = {
+ .probe = lpass_gfm_clk_driver_probe,
+ .driver = {
+ .name = "lpass-gfm-clk",
+ .of_match_table = lpass_gfm_clk_match_table,
+ .pm = &lpass_gfm_pm_ops,
+ },
+};
+module_platform_driver(lpass_gfm_clk_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index fc00aaccb5f7..3e845be895ac 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -359,6 +359,7 @@ static int mfd_remove_devices_fn(struct device *dev, void *data)
struct platform_device *pdev;
const struct mfd_cell *cell;
int *level = data;
+ struct mfd_of_node_entry *of_entry, *tmp;
if (dev->type != &mfd_dev_type)
return 0;
@@ -372,6 +373,12 @@ static int mfd_remove_devices_fn(struct device *dev, void *data)
regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies,
cell->num_parent_supplies);
+ list_for_each_entry_safe(of_entry, tmp, &mfd_of_node_list, list)
+ if (of_entry->dev == &pdev->dev) {
+ list_del(&of_entry->list);
+ kfree(of_entry);
+ }
+
platform_device_unregister(pdev);
return 0;
}
diff --git a/drivers/mfd/wcd934x.c b/drivers/mfd/wcd934x.c
index c274d733b656..b5252053a624 100644
--- a/drivers/mfd/wcd934x.c
+++ b/drivers/mfd/wcd934x.c
@@ -194,10 +194,14 @@ static int wcd934x_slim_status_up(struct slim_device *sdev)
static int wcd934x_slim_status(struct slim_device *sdev,
enum slim_device_status status)
{
+ struct wcd934x_ddata *ddata;
+
switch (status) {
case SLIM_DEVICE_STATUS_UP:
return wcd934x_slim_status_up(sdev);
case SLIM_DEVICE_STATUS_DOWN:
+ ddata = dev_get_drvdata(&sdev->dev);
+ regcache_cache_only(ddata->regmap, true);
mfd_remove_devices(&sdev->dev);
break;
default:
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index 5fe7b8aaf69d..af26f4c51f77 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -236,4 +236,12 @@ config PINCTRL_SM8250
Qualcomm Technologies Inc TLMM block found on the Qualcomm
Technologies Inc SM8250 platform.
+config PINCTRL_LPASS_LPI
+ tristate "Qualcomm Technologies Inc LPASS LPI pin controller driver"
+ depends on GPIOLIB && OF
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+ Qualcomm Technologies Inc LPASS (Low Power Audio SubSystem) LPI
+ (Low Power Island) found on the Qualcomm Technologies Inc SoCs.
+
endif
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 9e3d9c91a444..c8520155fb1b 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_PINCTRL_SDM660) += pinctrl-sdm660.o
obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o
obj-$(CONFIG_PINCTRL_SM8150) += pinctrl-sm8150.o
obj-$(CONFIG_PINCTRL_SM8250) += pinctrl-sm8250.o
+obj-$(CONFIG_PINCTRL_LPASS_LPI) += pinctrl-lpass-lpi.o
diff --git a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c
new file mode 100644
index 000000000000..9b79230952e0
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/bitops.h>
+
+#include "../core.h"
+#include "../pinctrl-utils.h"
+
+#define LPI_GPIO_REG_VAL_CTL 0x00
+#define LPI_GPIO_REG_DIR_CTL 0x04
+#define LPI_SLEW_REG_VAL_CTL 0x00
+#define LPI_SLEW_RATE_MAX 0x03
+#define LPI_SLEW_BITS_SIZE 0x02
+
+#define LPI_GPIO_REG_PULL_SHIFT 0x0
+#define LPI_GPIO_REG_PULL_MASK GENMASK(1, 0)
+
+#define LPI_GPIO_REG_FUNCTION_SHIFT 0x2
+#define LPI_GPIO_REG_FUNCTION_MASK GENMASK(5, 2)
+
+#define LPI_GPIO_REG_OUT_STRENGTH_SHIFT 0x6
+#define LPI_GPIO_REG_OUT_STRENGTH_MASK GENMASK(8, 6)
+
+#define LPI_GPIO_REG_OE_SHIFT 0x9
+#define LPI_GPIO_REG_OE_MASK BIT(9)
+
+#define LPI_GPIO_REG_DIR_SHIFT 0x1
+#define LPI_GPIO_REG_DIR_MASK 0x2
+
+#define LPI_GPIO_BIAS_DISABLE 0x0
+#define LPI_GPIO_PULL_DOWN 0x1
+#define LPI_GPIO_KEEPER 0x2
+#define LPI_GPIO_PULL_UP 0x3
+
+#define LPI_GPIO_FUNC_GPIO "gpio"
+#define LPI_GPIO_FUNC_FUNC1 "func1"
+#define LPI_GPIO_FUNC_FUNC2 "func2"
+#define LPI_GPIO_FUNC_FUNC3 "func3"
+#define LPI_GPIO_FUNC_FUNC4 "func4"
+#define LPI_GPIO_FUNC_FUNC5 "func5"
+
+/* The index of each function in lpi_gpio_functions[] array */
+enum lpi_gpio_func_index {
+ LPI_GPIO_FUNC_INDEX_GPIO = 0x00,
+ LPI_GPIO_FUNC_INDEX_FUNC1,
+ LPI_GPIO_FUNC_INDEX_FUNC2,
+ LPI_GPIO_FUNC_INDEX_FUNC3,
+ LPI_GPIO_FUNC_INDEX_FUNC4,
+ LPI_GPIO_FUNC_INDEX_FUNC5,
+};
+
+struct lpi_pinctrl_variant_data {
+ int tlmm_reg_offset;
+ const struct pinctrl_pin_desc *pins;
+ int npins;
+ const char *const *groups;
+ int ngroups;
+ int *slew_reg_pin_offsets;
+};
+
+struct lpi_pinctrl {
+ struct device *dev;
+ struct pinctrl_dev *ctrl;
+ struct gpio_chip chip;
+ struct pinctrl_desc desc;
+ char __iomem *tlmm_base;
+ char __iomem *slew_base;
+ struct clk *core_vote;
+ struct clk *audio_vote;
+ struct mutex slew_access_lock;
+ const struct lpi_pinctrl_variant_data *data;
+};
+
+/* sm8250 variant specific data */
+#define SM8250_LPASS_PINS 14
+
+static const char *const sm8250_gpio_groups[SM8250_LPASS_PINS] = {
+ "gpio0", "gpio1", "gpio2", "gpio3", "gpio4",
+ "gpio5", "gpio6", "gpio7", "gpio8", "gpio9",
+ "gpio10", "gpio11", "gpio12", "gpio13"
+};
+
+static const struct pinctrl_pin_desc sm8250_lpi_pins[] = {
+ PINCTRL_PIN(0, "gpio0"),
+ PINCTRL_PIN(1, "gpio1"),
+ PINCTRL_PIN(2, "gpio2"),
+ PINCTRL_PIN(3, "gpio3"),
+ PINCTRL_PIN(4, "gpio4"),
+ PINCTRL_PIN(5, "gpio5"),
+ PINCTRL_PIN(6, "gpio6"),
+ PINCTRL_PIN(7, "gpio7"),
+ PINCTRL_PIN(8, "gpio8"),
+ PINCTRL_PIN(9, "gpio9"),
+ PINCTRL_PIN(10, "gpio10"),
+ PINCTRL_PIN(11, "gpio11"),
+ PINCTRL_PIN(12, "gpio12"),
+ PINCTRL_PIN(13, "gpio13"),
+};
+
+static int sm8250_slew_reg_offsets[] = {
+ 0x0, 0x2, 0x4, 0x8, 0xa,
+ 0xc, 0x0, 0x0, 0x0, 0x0,
+ 0x10, 0x12, 0x0, 0x0,
+};
+
+static struct lpi_pinctrl_variant_data sm8250_lpi_data = {
+ .tlmm_reg_offset = 0x1000,
+ .pins = sm8250_lpi_pins,
+ .npins = ARRAY_SIZE(sm8250_lpi_pins),
+ .slew_reg_pin_offsets = sm8250_slew_reg_offsets,
+ .groups = sm8250_gpio_groups,
+ .ngroups = ARRAY_SIZE(sm8250_gpio_groups),
+};
+
+static const char *const lpi_gpio_functions[] = {
+ [LPI_GPIO_FUNC_INDEX_GPIO] = LPI_GPIO_FUNC_GPIO,
+ [LPI_GPIO_FUNC_INDEX_FUNC1] = LPI_GPIO_FUNC_FUNC1,
+ [LPI_GPIO_FUNC_INDEX_FUNC2] = LPI_GPIO_FUNC_FUNC2,
+ [LPI_GPIO_FUNC_INDEX_FUNC3] = LPI_GPIO_FUNC_FUNC3,
+ [LPI_GPIO_FUNC_INDEX_FUNC4] = LPI_GPIO_FUNC_FUNC4,
+ [LPI_GPIO_FUNC_INDEX_FUNC5] = LPI_GPIO_FUNC_FUNC5,
+};
+
+static int lpi_gpio_read(struct lpi_pinctrl *state, unsigned int pin,
+ unsigned int addr)
+{
+ return ioread32(state->tlmm_base +
+ state->data->tlmm_reg_offset * pin + addr);
+}
+
+static int lpi_gpio_write(struct lpi_pinctrl *state, unsigned int pin,
+ unsigned int addr, unsigned int val)
+{
+ iowrite32(val, state->tlmm_base +
+ state->data->tlmm_reg_offset * pin + addr);
+ pr_err("DEBUG: %s: %x-> %x \n", __func__,
+ state->data->tlmm_reg_offset * pin, val);
+
+ return 0;
+}
+
+static int lpi_gpio_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ /* Every PIN is a group */
+ return pctldev->desc->npins;
+}
+
+static const char *lpi_gpio_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned int pin)
+{
+ struct lpi_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ return pctrl->data->groups[pin];
+}
+
+static int lpi_gpio_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned int pin,
+ const unsigned int **pins,
+ unsigned int *num_pins)
+{
+ *pins = &pctldev->desc->pins[pin].number;
+ *num_pins = 1;
+
+ return 0;
+}
+
+static const struct pinctrl_ops lpi_gpio_pinctrl_ops = {
+ .get_groups_count = lpi_gpio_get_groups_count,
+ .get_group_name = lpi_gpio_get_group_name,
+ .get_group_pins = lpi_gpio_get_group_pins,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_group,
+ .dt_free_map = pinctrl_utils_free_map,
+};
+
+static int lpi_gpio_get_functions_count(struct pinctrl_dev *pctldev)
+{
+ return ARRAY_SIZE(lpi_gpio_functions);
+}
+
+static const char *lpi_gpio_get_function_name(struct pinctrl_dev *pctldev,
+ unsigned int function)
+{
+ return lpi_gpio_functions[function];
+}
+
+static int lpi_gpio_get_function_groups(struct pinctrl_dev *pctldev,
+ unsigned int function,
+ const char *const **groups,
+ unsigned *const num_qgroups)
+{
+ struct lpi_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = pctrl->data->groups;
+ *num_qgroups = pctrl->data->ngroups;
+
+ return 0;
+}
+
+static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
+ unsigned int pin)
+{
+ struct lpi_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned int val;
+
+ val = lpi_gpio_read(pctrl, pin, LPI_GPIO_REG_VAL_CTL);
+ val &= ~(LPI_GPIO_REG_FUNCTION_MASK);
+ val |= function << LPI_GPIO_REG_FUNCTION_SHIFT;
+ lpi_gpio_write(pctrl, pin, LPI_GPIO_REG_VAL_CTL, val);
+
+ return 0;
+}
+
+static const struct pinmux_ops lpi_gpio_pinmux_ops = {
+ .get_functions_count = lpi_gpio_get_functions_count,
+ .get_function_name = lpi_gpio_get_function_name,
+ .get_function_groups = lpi_gpio_get_function_groups,
+ .set_mux = lpi_gpio_set_mux,
+};
+
+static int lpi_config_get(struct pinctrl_dev *pctldev,
+ unsigned int pin, unsigned long *config)
+{
+ unsigned int param = pinconf_to_config_param(*config);
+ struct lpi_pinctrl *state = dev_get_drvdata(pctldev->dev);
+ unsigned int arg = 0;
+ int is_out;
+ int pull;
+ u32 ctl_reg;
+
+ ctl_reg = lpi_gpio_read(state, pin, LPI_GPIO_REG_DIR_CTL);
+
+ is_out = (ctl_reg & LPI_GPIO_REG_DIR_MASK) >> LPI_GPIO_REG_DIR_SHIFT;
+
+ ctl_reg = lpi_gpio_read(state, pin, LPI_GPIO_REG_VAL_CTL);
+
+ pull = (ctl_reg & LPI_GPIO_REG_PULL_MASK) >> LPI_GPIO_REG_PULL_SHIFT;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ if (pull == LPI_GPIO_BIAS_DISABLE)
+ arg = 1;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ if (pull == LPI_GPIO_PULL_DOWN)
+ arg = 1;
+ break;
+ case PIN_CONFIG_BIAS_BUS_HOLD:
+ if (pull == LPI_GPIO_KEEPER)
+ arg = 1;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ if (pull == LPI_GPIO_PULL_UP)
+ arg = 1;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ case PIN_CONFIG_OUTPUT:
+ if (is_out)
+ arg = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *config = pinconf_to_config_packed(param, arg);
+ return 0;
+}
+
+static unsigned int lpi_drive_to_regval(u32 arg)
+{
+ return (arg/2 - 1);
+}
+
+static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long *configs, unsigned int nconfs)
+{
+ unsigned int param, arg;
+ int i, ret = 0;
+ volatile unsigned long val;
+ struct lpi_pinctrl *state = dev_get_drvdata(pctldev->dev);
+ bool output_enabled;
+ unsigned int pullup;
+ unsigned int strength;
+ unsigned int offset;
+ bool value;
+
+ for (i = 0; i < nconfs; i++) {
+ param = pinconf_to_config_param(configs[i]);
+ arg = pinconf_to_config_argument(configs[i]);
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ pullup = LPI_GPIO_BIAS_DISABLE;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ pullup = LPI_GPIO_PULL_DOWN;
+ break;
+ case PIN_CONFIG_BIAS_BUS_HOLD:
+ pullup = LPI_GPIO_KEEPER;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ pullup = LPI_GPIO_PULL_UP;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ output_enabled = false;
+ break;
+ case PIN_CONFIG_OUTPUT:
+ output_enabled = true;
+ lpi_gpio_write(state, pin, LPI_GPIO_REG_DIR_CTL,
+ output_enabled << LPI_GPIO_REG_DIR_SHIFT);
+ value = arg;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ strength = arg;
+ break;
+ case PIN_CONFIG_SLEW_RATE:
+ if (arg > LPI_SLEW_RATE_MAX) {
+ dev_err(pctldev->dev, "%s: invalid slew rate %u for pin: %d\n",
+ __func__, arg, pin);
+ goto set_gpio;
+ }
+
+ mutex_lock(&state->slew_access_lock);
+
+ val = ioread32(state->slew_base + LPI_SLEW_REG_VAL_CTL);
+
+ offset = state->data->slew_reg_pin_offsets[pin];
+ for (i = 0; i < LPI_SLEW_BITS_SIZE; i++) {
+ if (arg & 0x01)
+ set_bit(offset, &val);
+ else
+ clear_bit(offset, &val);
+ offset++;
+ arg = arg >> 1;
+ }
+
+ iowrite32(val, state->slew_base + LPI_SLEW_REG_VAL_CTL);
+
+ mutex_unlock(&state->slew_access_lock);
+ break;
+ default:
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+set_gpio:
+
+ val = lpi_gpio_read(state, pin, LPI_GPIO_REG_VAL_CTL);
+ val &= ~(LPI_GPIO_REG_PULL_MASK | LPI_GPIO_REG_OUT_STRENGTH_MASK |
+ LPI_GPIO_REG_OE_MASK);
+ val |= pullup << LPI_GPIO_REG_PULL_SHIFT;
+ val |= lpi_drive_to_regval(strength) << LPI_GPIO_REG_OUT_STRENGTH_SHIFT;
+ if (output_enabled)
+ val |= value << LPI_GPIO_REG_OE_SHIFT;
+
+ lpi_gpio_write(state, pin, LPI_GPIO_REG_VAL_CTL, val);
+ lpi_gpio_write(state, pin, LPI_GPIO_REG_DIR_CTL,
+ output_enabled << LPI_GPIO_REG_DIR_SHIFT);
+done:
+ return ret;
+}
+
+static const struct pinconf_ops lpi_gpio_pinconf_ops = {
+ .is_generic = true,
+ .pin_config_group_get = lpi_config_get,
+ .pin_config_group_set = lpi_config_set,
+};
+
+static int lpi_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
+{
+ struct lpi_pinctrl *state = gpiochip_get_data(chip);
+ unsigned long config;
+
+ config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
+
+ return lpi_config_set(state->ctrl, pin, &config, 1);
+}
+
+static int lpi_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int pin, int val)
+{
+ struct lpi_pinctrl *state = gpiochip_get_data(chip);
+ unsigned long config;
+
+ config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
+
+ return lpi_config_set(state->ctrl, pin, &config, 1);
+}
+
+static int lpi_gpio_get(struct gpio_chip *chip, unsigned int pin)
+{
+ struct lpi_pinctrl *state = gpiochip_get_data(chip);
+ int value;
+
+ value = lpi_gpio_read(state, pin, LPI_GPIO_REG_VAL_CTL);
+ return value;
+}
+
+static void lpi_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)
+{
+ struct lpi_pinctrl *state = gpiochip_get_data(chip);
+ unsigned long config;
+
+ config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
+
+ lpi_config_set(state->ctrl, pin, &config, 1);
+}
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+
+static unsigned int lpi_regval_to_drive(u32 val)
+{
+ return (val + 1) * 2;
+}
+
+static void lpi_gpio_dbg_show_one(struct seq_file *s,
+ struct pinctrl_dev *pctldev,
+ struct gpio_chip *chip,
+ unsigned int offset,
+ unsigned int gpio)
+{
+ struct lpi_pinctrl *state = gpiochip_get_data(chip);
+ struct pinctrl_pin_desc pindesc;
+ unsigned int func;
+ int is_out;
+ int drive;
+ int pull;
+ u32 ctl_reg;
+
+ static const char * const pulls[] = {
+ "no pull",
+ "pull down",
+ "keeper",
+ "pull up"
+ };
+
+ pctldev = pctldev ? : state->ctrl;
+ pindesc = pctldev->desc->pins[offset];
+ ctl_reg = lpi_gpio_read(state, offset, LPI_GPIO_REG_DIR_CTL);
+ is_out = (ctl_reg & LPI_GPIO_REG_DIR_MASK) >> LPI_GPIO_REG_DIR_SHIFT;
+ ctl_reg = lpi_gpio_read(state, offset, LPI_GPIO_REG_VAL_CTL);
+
+ func = (ctl_reg & LPI_GPIO_REG_FUNCTION_MASK) >>
+ LPI_GPIO_REG_FUNCTION_SHIFT;
+ drive = (ctl_reg & LPI_GPIO_REG_OUT_STRENGTH_MASK) >>
+ LPI_GPIO_REG_OUT_STRENGTH_SHIFT;
+ pull = (ctl_reg & LPI_GPIO_REG_PULL_MASK) >> LPI_GPIO_REG_PULL_SHIFT;
+
+ seq_printf(s, " %-8s: %-3s %d",
+ pindesc.name, is_out ? "out" : "in", func);
+ seq_printf(s, " %dmA", lpi_regval_to_drive(drive));
+ seq_printf(s, " %s", pulls[pull]);
+}
+
+static void lpi_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ unsigned int gpio = chip->base;
+ unsigned int i;
+
+ for (i = 0; i < chip->ngpio; i++, gpio++) {
+ lpi_gpio_dbg_show_one(s, NULL, chip, i, gpio);
+ seq_puts(s, "\n");
+ }
+}
+
+#else
+#define lpi_gpio_dbg_show NULL
+#endif
+
+static const struct gpio_chip lpi_gpio_template = {
+ .direction_input = lpi_gpio_direction_input,
+ .direction_output = lpi_gpio_direction_output,
+ .get = lpi_gpio_get,
+ .set = lpi_gpio_set,
+ .request = gpiochip_generic_request,
+ .free = gpiochip_generic_free,
+ .dbg_show = lpi_gpio_dbg_show,
+};
+
+static int lpi_pinctrl_probe(struct platform_device *pdev)
+{
+ int ret, npins;
+ struct clk *core_vote = NULL;
+ struct clk *audio_vote = NULL;
+
+ struct lpi_pinctrl *pctrl;
+ const struct lpi_pinctrl_variant_data *data;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+
+ pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL);
+ if (!pctrl)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, pctrl);
+
+ data = of_device_get_match_data(dev);
+ pctrl->data = data;
+ pctrl->dev = &pdev->dev;
+ npins = data->npins;
+
+ /* Register LPASS core hw vote */
+ core_vote = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(core_vote)) {
+ dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
+ __func__, "core_vote", ret);
+ return PTR_ERR(core_vote);
+ }
+ pctrl->core_vote = core_vote;
+
+ /* Register LPASS audio hw vote */
+ audio_vote = devm_clk_get(&pdev->dev, "audio");
+ if (IS_ERR(audio_vote)) {
+ dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
+ __func__, "audio_vote", ret);
+ return PTR_ERR(audio_vote);
+ }
+
+ pctrl->audio_vote = audio_vote;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pctrl->tlmm_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pctrl->tlmm_base))
+ return PTR_ERR(pctrl->tlmm_base);
+
+ clk_prepare_enable(pctrl->core_vote);
+ clk_prepare_enable(pctrl->audio_vote);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ pctrl->slew_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pctrl->slew_base))
+ return PTR_ERR(pctrl->slew_base);
+
+ pctrl->desc.pctlops = &lpi_gpio_pinctrl_ops;
+ pctrl->desc.pmxops = &lpi_gpio_pinmux_ops;
+ pctrl->desc.confops = &lpi_gpio_pinconf_ops;
+
+ pctrl->desc.owner = THIS_MODULE;
+ pctrl->desc.name = dev_name(dev);
+ pctrl->desc.pins = data->pins;
+ pctrl->desc.npins = data->npins;
+
+ pctrl->chip = lpi_gpio_template;
+ pctrl->chip.parent = dev;
+ pctrl->chip.base = -1;
+ pctrl->chip.ngpio = npins;
+ pctrl->chip.label = dev_name(dev);
+ pctrl->chip.of_gpio_n_cells = 2;
+ pctrl->chip.can_sleep = false;
+
+ mutex_init(&pctrl->slew_access_lock);
+
+ pctrl->ctrl = devm_pinctrl_register(dev, &pctrl->desc, pctrl);
+ if (IS_ERR(pctrl->ctrl))
+ return PTR_ERR(pctrl->ctrl);
+
+ ret = gpiochip_add_data(&pctrl->chip, pctrl);
+ if (ret) {
+ dev_err(pctrl->dev, "can't add gpio chip\n");
+ goto err_chip;
+ }
+
+ ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(dev), 0, 0, npins);
+ if (ret) {
+ dev_err(dev, "failed to add pin range\n");
+ goto err_range;
+ }
+
+ return 0;
+
+err_range:
+ gpiochip_remove(&pctrl->chip);
+err_chip:
+ mutex_destroy(&pctrl->slew_access_lock);
+ return ret;
+}
+
+static int lpi_pinctrl_remove(struct platform_device *pdev)
+{
+ struct lpi_pinctrl *state = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&state->chip);
+ mutex_destroy(&state->slew_access_lock);
+
+ return 0;
+}
+
+static const struct of_device_id lpi_pinctrl_of_match[] = {
+ {
+ .compatible = "qcom,sm8250-lpass-lpi-pinctrl",
+ .data = &sm8250_lpi_data,
+ },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match);
+
+static struct platform_driver lpi_pinctrl_driver = {
+ .driver = {
+ .name = "qcom-lpass-lpi-pinctrl",
+ .of_match_table = lpi_pinctrl_of_match,
+ },
+ .probe = lpi_pinctrl_probe,
+ .remove = lpi_pinctrl_remove,
+};
+
+module_platform_driver(lpi_pinctrl_driver);
+
+MODULE_DESCRIPTION("QTI LPI GPIO pin control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig
index 8cd595148d17..060a2c65978a 100644
--- a/drivers/slimbus/Kconfig
+++ b/drivers/slimbus/Kconfig
@@ -25,6 +25,8 @@ config SLIM_QCOM_NGD_CTRL
depends on HAS_IOMEM && DMA_ENGINE && NET
depends on ARCH_QCOM || COMPILE_TEST
select QCOM_QMI_HELPERS
+ select QCOM_RPROC_COMMON
+ select QCOM_PDR_HELPERS
help
Select driver if Qualcomm's SLIMbus Satellite Non-Generic Device
Component is programmed using Linux kernel.
diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c
index 218aefc3531c..8c08074fef30 100644
--- a/drivers/slimbus/qcom-ngd-ctrl.c
+++ b/drivers/slimbus/qcom-ngd-ctrl.c
@@ -13,9 +13,13 @@
#include <linux/slimbus.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/remoteproc/qcom_rproc.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/soc/qcom/qmi.h>
+#include <linux/soc/qcom/pdr.h>
#include <net/sock.h>
#include "slimbus.h"
@@ -155,8 +159,15 @@ struct qcom_slim_ngd_ctrl {
struct qcom_slim_ngd_dma_desc txdesc[QCOM_SLIM_NGD_DESC_NUM];
struct completion reconf;
struct work_struct m_work;
+ struct work_struct ngd_up_work;
struct workqueue_struct *mwq;
+ struct completion qmi_up;
spinlock_t tx_buf_lock;
+ struct mutex tx_lock;
+ struct mutex ssr_lock;
+ struct notifier_block nb;
+ void *notifier;
+ struct pdr_handle *pdr;
enum qcom_slim_ngd_state state;
dma_addr_t rx_phys_base;
dma_addr_t tx_phys_base;
@@ -868,14 +879,18 @@ static int qcom_slim_ngd_xfer_msg(struct slim_controller *sctrl,
if (txn->msg && txn->msg->wbuf)
memcpy(puc, txn->msg->wbuf, txn->msg->num_bytes);
+ mutex_lock(&ctrl->tx_lock);
ret = qcom_slim_ngd_tx_msg_post(ctrl, pbuf, txn->rl);
- if (ret)
+ if (ret) {
+ mutex_unlock(&ctrl->tx_lock);
return ret;
+ }
timeout = wait_for_completion_timeout(&tx_sent, HZ);
if (!timeout) {
dev_err(sctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc,
txn->mt);
+ mutex_unlock(&ctrl->tx_lock);
return -ETIMEDOUT;
}
@@ -884,10 +899,12 @@ static int qcom_slim_ngd_xfer_msg(struct slim_controller *sctrl,
if (!timeout) {
dev_err(sctrl->dev, "TX timed out:MC:0x%x,mt:0x%x",
txn->mc, txn->mt);
+ mutex_unlock(&ctrl->tx_lock);
return -ETIMEDOUT;
}
}
+ mutex_unlock(&ctrl->tx_lock);
return 0;
}
@@ -1200,6 +1217,13 @@ capability_retry:
}
}
+static int qcom_slim_ngd_update_device_status(struct device *dev, void *null)
+{
+ slim_report_absent(to_slim_device(dev));
+
+ return 0;
+}
+
static int qcom_slim_ngd_runtime_resume(struct device *dev)
{
struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(dev);
@@ -1267,7 +1291,7 @@ static int qcom_slim_ngd_qmi_new_server(struct qmi_handle *hdl,
qmi->svc_info.sq_node = service->node;
qmi->svc_info.sq_port = service->port;
- qcom_slim_ngd_enable(ctrl, true);
+ complete(&ctrl->qmi_up);
return 0;
}
@@ -1280,10 +1304,9 @@ static void qcom_slim_ngd_qmi_del_server(struct qmi_handle *hdl,
struct qcom_slim_ngd_ctrl *ctrl =
container_of(qmi, struct qcom_slim_ngd_ctrl, qmi);
+ reinit_completion(&ctrl->qmi_up);
qmi->svc_info.sq_node = 0;
qmi->svc_info.sq_port = 0;
-
- qcom_slim_ngd_enable(ctrl, false);
}
static struct qmi_ops qcom_slim_ngd_qmi_svc_event_ops = {
@@ -1327,12 +1350,81 @@ static const struct of_device_id qcom_slim_ngd_dt_match[] = {
},{
.compatible = "qcom,slim-ngd-v2.1.0",
.data = &ngd_v1_5_offset_info,
+ },{
+ .compatible = "qcom,slim-ngd-v2.2.0",
+ .data = &ngd_v1_5_offset_info,
},
{}
};
MODULE_DEVICE_TABLE(of, qcom_slim_ngd_dt_match);
+static void qcom_slim_ngd_down(struct qcom_slim_ngd_ctrl *ctrl)
+{
+ mutex_lock(&ctrl->ssr_lock);
+ device_for_each_child(ctrl->ctrl.dev, NULL,
+ qcom_slim_ngd_update_device_status);
+ qcom_slim_ngd_enable(ctrl, false);
+ mutex_unlock(&ctrl->ssr_lock);
+}
+
+static void qcom_slim_ngd_up_worker(struct work_struct *work)
+{
+ struct qcom_slim_ngd_ctrl *ctrl;
+
+ ctrl = container_of(work, struct qcom_slim_ngd_ctrl, ngd_up_work);
+
+ /* Make sure qmi service is up before continuing */
+ wait_for_completion_interruptible(&ctrl->qmi_up);
+
+ mutex_lock(&ctrl->ssr_lock);
+ qcom_slim_ngd_enable(ctrl, true);
+ mutex_unlock(&ctrl->ssr_lock);
+}
+
+static int qcom_slim_ngd_ssr_pdr_notify(struct qcom_slim_ngd_ctrl *ctrl,
+ unsigned long action)
+{
+ switch (action) {
+ case QCOM_SSR_BEFORE_SHUTDOWN:
+ case SERVREG_SERVICE_STATE_DOWN:
+ /* Make sure the last dma xfer is finished */
+ mutex_lock(&ctrl->tx_lock);
+ if (ctrl->state != QCOM_SLIM_NGD_CTRL_DOWN) {
+ pm_runtime_get_noresume(ctrl->dev);
+ ctrl->state = QCOM_SLIM_NGD_CTRL_DOWN;
+ qcom_slim_ngd_down(ctrl);
+ qcom_slim_ngd_exit_dma(ctrl);
+ }
+ mutex_unlock(&ctrl->tx_lock);
+ break;
+ case QCOM_SSR_AFTER_POWERUP:
+ case SERVREG_SERVICE_STATE_UP:
+ schedule_work(&ctrl->ngd_up_work);
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int qcom_slim_ngd_ssr_notify(struct notifier_block *nb,
+ unsigned long action,
+ void *data)
+{
+ struct qcom_slim_ngd_ctrl *ctrl = container_of(nb,
+ struct qcom_slim_ngd_ctrl, nb);
+
+ return qcom_slim_ngd_ssr_pdr_notify(ctrl, action);
+}
+
+static void slim_pd_status(int state, char *svc_path, void *priv)
+{
+ struct qcom_slim_ngd_ctrl *ctrl = (struct qcom_slim_ngd_ctrl *)priv;
+
+ qcom_slim_ngd_ssr_pdr_notify(ctrl, state);
+}
static int of_qcom_slim_ngd_register(struct device *parent,
struct qcom_slim_ngd_ctrl *ctrl)
{
@@ -1397,6 +1489,7 @@ static int qcom_slim_ngd_probe(struct platform_device *pdev)
}
INIT_WORK(&ctrl->m_work, qcom_slim_ngd_master_worker);
+ INIT_WORK(&ctrl->ngd_up_work, qcom_slim_ngd_up_worker);
ctrl->mwq = create_singlethread_workqueue("ngd_master");
if (!ctrl->mwq) {
dev_err(&pdev->dev, "Failed to start master worker\n");
@@ -1419,6 +1512,7 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev)
struct qcom_slim_ngd_ctrl *ctrl;
struct resource *res;
int ret;
+ struct pdr_service *pds;
ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
@@ -1444,6 +1538,11 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev)
return ret;
}
+ ctrl->nb.notifier_call = qcom_slim_ngd_ssr_notify;
+ ctrl->notifier = qcom_register_ssr_notifier("lpass", &ctrl->nb);
+ if (IS_ERR(ctrl->notifier))
+ return PTR_ERR(ctrl->notifier);
+
ctrl->dev = dev;
ctrl->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
ctrl->framer.superfreq =
@@ -1457,9 +1556,24 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev)
ctrl->ctrl.wakeup = NULL;
ctrl->state = QCOM_SLIM_NGD_CTRL_DOWN;
+ mutex_init(&ctrl->tx_lock);
+ mutex_init(&ctrl->ssr_lock);
spin_lock_init(&ctrl->tx_buf_lock);
init_completion(&ctrl->reconf);
init_completion(&ctrl->qmi.qmi_comp);
+ init_completion(&ctrl->qmi_up);
+
+ ctrl->pdr = pdr_handle_alloc(slim_pd_status, ctrl);
+ if (IS_ERR(ctrl->pdr)) {
+ dev_err(dev, "Failed to init PDR handle\n");
+ return PTR_ERR(ctrl->pdr);
+ }
+
+ pds = pdr_add_lookup(ctrl->pdr, "avs/audio", "msm/adsp/audio_pd");
+ if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) {
+ dev_err(dev, "pdr add lookup failed: %d\n", ret);
+ return PTR_ERR(pds);
+ }
platform_driver_register(&qcom_slim_ngd_driver);
return of_qcom_slim_ngd_register(dev, ctrl);
@@ -1477,6 +1591,8 @@ static int qcom_slim_ngd_remove(struct platform_device *pdev)
struct qcom_slim_ngd_ctrl *ctrl = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
+ pdr_handle_release(ctrl->pdr);
+ qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb);
qcom_slim_ngd_enable(ctrl, false);
qcom_slim_ngd_exit_dma(ctrl);
qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi);
diff --git a/include/dt-bindings/clock/qcom,sm8250-lpass-aoncc.h b/include/dt-bindings/clock/qcom,sm8250-lpass-aoncc.h
new file mode 100644
index 000000000000..f5a1cfac8612
--- /dev/null
+++ b/include/dt-bindings/clock/qcom,sm8250-lpass-aoncc.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _DT_BINDINGS_CLK_LPASS_AONCC_SM8250_H
+#define _DT_BINDINGS_CLK_LPASS_AONCC_SM8250_H
+
+/* from AOCC */
+#define LPASS_CDC_VA_MCLK 0
+#define LPASS_CDC_TX_NPL 1
+#define LPASS_CDC_TX_MCLK 2
+
+#endif /* _DT_BINDINGS_CLK_LPASS_AONCC_SM8250_H */
diff --git a/include/dt-bindings/clock/qcom,sm8250-lpass-audiocc.h b/include/dt-bindings/clock/qcom,sm8250-lpass-audiocc.h
new file mode 100644
index 000000000000..a1aa6cb5d840
--- /dev/null
+++ b/include/dt-bindings/clock/qcom,sm8250-lpass-audiocc.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _DT_BINDINGS_CLK_LPASS_AUDIOCC_SM8250_H
+#define _DT_BINDINGS_CLK_LPASS_AUDIOCC_SM8250_H
+
+/* From AudioCC */
+#define LPASS_CDC_WSA_NPL 0
+#define LPASS_CDC_WSA_MCLK 1
+#define LPASS_CDC_RX_MCLK 2
+#define LPASS_CDC_RX_NPL 3
+#define LPASS_CDC_RX_MCLK_MCLK2 4
+
+#endif /* _DT_BINDINGS_CLK_LPASS_AUDIOCC_SM8250_H */
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 34c6dd04b85a..4e45fb5e9817 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1784,4 +1784,12 @@ config SND_SOC_TPA6130A2
tristate "Texas Instruments TPA6130A2 headphone amplifier"
depends on I2C
+config SND_SOC_LPASS_WSA_MACRO
+ depends on COMMON_CLK
+ tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)"
+
+config SND_SOC_LPASS_VA_MACRO
+ depends on COMMON_CLK
+ tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)"
+
endmenu
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 11ce98c25d6c..5d68da2b1182 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -103,6 +103,8 @@ snd-soc-l3-objs := l3.o
snd-soc-lm4857-objs := lm4857.o
snd-soc-lm49453-objs := lm49453.o
snd-soc-lochnagar-sc-objs := lochnagar-sc.o
+snd-soc-lpass-wsa-macro-objs := lpass-wsa-macro.o
+snd-soc-lpass-va-macro-objs := lpass-va-macro.o
snd-soc-madera-objs := madera.o
snd-soc-max9759-objs := max9759.o
snd-soc-max9768-objs := max9768.o
@@ -613,3 +615,5 @@ obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o
obj-$(CONFIG_SND_SOC_SIMPLE_AMPLIFIER) += snd-soc-simple-amplifier.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
+obj-$(CONFIG_SND_SOC_LPASS_WSA_MACRO) += snd-soc-lpass-wsa-macro.o
+obj-$(CONFIG_SND_SOC_LPASS_VA_MACRO) += snd-soc-lpass-va-macro.o
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
new file mode 100644
index 000000000000..35ce4f136d38
--- /dev/null
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -0,0 +1,1501 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_clk.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+/* VA macro registers */
+#define CDC_VA_CLK_RST_CTRL_MCLK_CONTROL (0x0000)
+#define CDC_VA_MCLK_CONTROL_EN BIT(0)
+#define CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004)
+#define CDC_VA_FS_CONTROL_EN BIT(0)
+#define CDC_VA_CLK_RST_CTRL_SWR_CONTROL (0x0008)
+#define CDC_VA_TOP_CSR_TOP_CFG0 (0x0080)
+#define CDC_VA_FS_BROADCAST_EN BIT(1)
+#define CDC_VA_TOP_CSR_DMIC0_CTL (0x0084)
+#define CDC_VA_TOP_CSR_DMIC1_CTL (0x0088)
+#define CDC_VA_TOP_CSR_DMIC2_CTL (0x008C)
+#define CDC_VA_TOP_CSR_DMIC3_CTL (0x0090)
+#define CDC_VA_DMIC_CLK_SEL_MASK GENMASK(3, 1)
+#define CDC_VA_DMIC_CLK_SEL_SHFT 1
+#define CDC_VA_DMIC_CLK_SEL_DIV0 0x0
+#define CDC_VA_DMIC_CLK_SEL_DIV1 0x2
+#define CDC_VA_DMIC_CLK_SEL_DIV2 0x4
+#define CDC_VA_DMIC_CLK_SEL_DIV3 0x6
+#define CDC_VA_DMIC_CLK_SEL_DIV4 0x8
+#define CDC_VA_DMIC_CLK_SEL_DIV5 0xa
+#define CDC_VA_TOP_CSR_DMIC_CFG (0x0094)
+#define CDC_VA_RESET_ALL_DMICS_MASK BIT(7)
+#define CDC_VA_RESET_ALL_DMICS_RESET BIT(7)
+#define CDC_VA_RESET_ALL_DMICS_DISABLE 0
+#define CDC_VA_DMIC3_FREQ_CHANGE_MASK BIT(3)
+#define CDC_VA_DMIC3_FREQ_CHANGE_EN BIT(3)
+#define CDC_VA_DMIC2_FREQ_CHANGE_MASK BIT(2)
+#define CDC_VA_DMIC2_FREQ_CHANGE_EN BIT(2)
+#define CDC_VA_DMIC1_FREQ_CHANGE_MASK BIT(1)
+#define CDC_VA_DMIC1_FREQ_CHANGE_EN BIT(1)
+#define CDC_VA_DMIC0_FREQ_CHANGE_MASK BIT(0)
+#define CDC_VA_DMIC0_FREQ_CHANGE_EN BIT(0)
+#define CDC_VA_DMIC_FREQ_CHANGE_DISABLE 0
+#define CDC_VA_TOP_CSR_DEBUG_BUS (0x009C)
+#define CDC_VA_TOP_CSR_DEBUG_EN (0x00A0)
+#define CDC_VA_TOP_CSR_TX_I2S_CTL (0x00A4)
+#define CDC_VA_TOP_CSR_I2S_CLK (0x00A8)
+#define CDC_VA_TOP_CSR_I2S_RESET (0x00AC)
+#define CDC_VA_TOP_CSR_CORE_ID_0 (0x00C0)
+#define CDC_VA_TOP_CSR_CORE_ID_1 (0x00C4)
+#define CDC_VA_TOP_CSR_CORE_ID_2 (0x00C8)
+#define CDC_VA_TOP_CSR_CORE_ID_3 (0x00CC)
+#define CDC_VA_TOP_CSR_SWR_MIC_CTL0 (0x00D0)
+#define CDC_VA_TOP_CSR_SWR_MIC_CTL1 (0x00D4)
+#define CDC_VA_TOP_CSR_SWR_MIC_CTL2 (0x00D8)
+#define CDC_VA_TOP_CSR_SWR_CTRL (0x00DC)
+#define CDC_VA_INP_MUX_ADC_MUX0_CFG0 (0x0100)
+#define CDC_VA_INP_MUX_ADC_MUX0_CFG1 (0x0104)
+#define CDC_VA_INP_MUX_ADC_MUX1_CFG0 (0x0108)
+#define CDC_VA_INP_MUX_ADC_MUX1_CFG1 (0x010C)
+#define CDC_VA_INP_MUX_ADC_MUX2_CFG0 (0x0110)
+#define CDC_VA_INP_MUX_ADC_MUX2_CFG1 (0x0114)
+#define CDC_VA_INP_MUX_ADC_MUX3_CFG0 (0x0118)
+#define CDC_VA_INP_MUX_ADC_MUX3_CFG1 (0x011C)
+#define CDC_VA_TX0_TX_PATH_CTL (0x0400)
+#define CDC_VA_TX_PATH_CLK_EN_MASK BIT(5)
+#define CDC_VA_TX_PATH_CLK_EN BIT(5)
+#define CDC_VA_TX_PATH_CLK_DISABLE 0
+#define CDC_VA_TX_PATH_PGA_MUTE_EN_MASK BIT(4)
+#define CDC_VA_TX_PATH_PGA_MUTE_EN BIT(4)
+#define CDC_VA_TX_PATH_PGA_MUTE_DISABLE 0
+#define CDC_VA_TX0_TX_PATH_CFG0 (0x0404)
+#define CDC_VA_ADC_MODE_MASK GENMASK(2, 1)
+#define CDC_VA_ADC_MODE_SHIFT 1
+#define TX_HPF_CUT_OFF_FREQ_MASK GENMASK(6, 5)
+#define CF_MIN_3DB_4HZ 0x0
+#define CF_MIN_3DB_75HZ 0x1
+#define CF_MIN_3DB_150HZ 0x2
+#define CDC_VA_TX0_TX_PATH_CFG1 (0x0408)
+#define CDC_VA_TX0_TX_VOL_CTL (0x040C)
+#define CDC_VA_TX0_TX_PATH_SEC0 (0x0410)
+#define CDC_VA_TX0_TX_PATH_SEC1 (0x0414)
+#define CDC_VA_TX0_TX_PATH_SEC2 (0x0418)
+#define CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK BIT(1)
+#define CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_REQ BIT(1)
+#define CDC_VA_TX_HPF_ZERO_GATE_MASK BIT(0)
+#define CDC_VA_TX_HPF_ZERO_NO_GATE BIT(0)
+#define CDC_VA_TX_HPF_ZERO_GATE 0
+#define CDC_VA_TX0_TX_PATH_SEC3 (0x041C)
+#define CDC_VA_TX0_TX_PATH_SEC4 (0x0420)
+#define CDC_VA_TX0_TX_PATH_SEC5 (0x0424)
+#define CDC_VA_TX0_TX_PATH_SEC6 (0x0428)
+#define CDC_VA_TX0_TX_PATH_SEC7 (0x042C)
+#define CDC_VA_TX1_TX_PATH_CTL (0x0480)
+#define CDC_VA_TX1_TX_PATH_CFG0 (0x0484)
+#define CDC_VA_TX1_TX_PATH_CFG1 (0x0488)
+#define CDC_VA_TX1_TX_VOL_CTL (0x048C)
+#define CDC_VA_TX1_TX_PATH_SEC0 (0x0490)
+#define CDC_VA_TX1_TX_PATH_SEC1 (0x0494)
+#define CDC_VA_TX1_TX_PATH_SEC2 (0x0498)
+#define CDC_VA_TX1_TX_PATH_SEC3 (0x049C)
+#define CDC_VA_TX1_TX_PATH_SEC4 (0x04A0)
+#define CDC_VA_TX1_TX_PATH_SEC5 (0x04A4)
+#define CDC_VA_TX1_TX_PATH_SEC6 (0x04A8)
+#define CDC_VA_TX2_TX_PATH_CTL (0x0500)
+#define CDC_VA_TX2_TX_PATH_CFG0 (0x0504)
+#define CDC_VA_TX2_TX_PATH_CFG1 (0x0508)
+#define CDC_VA_TX2_TX_VOL_CTL (0x050C)
+#define CDC_VA_TX2_TX_PATH_SEC0 (0x0510)
+#define CDC_VA_TX2_TX_PATH_SEC1 (0x0514)
+#define CDC_VA_TX2_TX_PATH_SEC2 (0x0518)
+#define CDC_VA_TX2_TX_PATH_SEC3 (0x051C)
+#define CDC_VA_TX2_TX_PATH_SEC4 (0x0520)
+#define CDC_VA_TX2_TX_PATH_SEC5 (0x0524)
+#define CDC_VA_TX2_TX_PATH_SEC6 (0x0528)
+#define CDC_VA_TX3_TX_PATH_CTL (0x0580)
+#define CDC_VA_TX3_TX_PATH_CFG0 (0x0584)
+#define CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK BIT(7)
+#define CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC BIT(7)
+#define CDC_VA_TX_PATH_ADC_DMIC_SEL_ADC 0
+#define CDC_VA_TX3_TX_PATH_CFG1 (0x0588)
+#define CDC_VA_TX3_TX_VOL_CTL (0x058C)
+#define CDC_VA_TX3_TX_PATH_SEC0 (0x0590)
+#define CDC_VA_TX3_TX_PATH_SEC1 (0x0594)
+#define CDC_VA_TX3_TX_PATH_SEC2 (0x0598)
+#define CDC_VA_TX3_TX_PATH_SEC3 (0x059C)
+#define CDC_VA_TX3_TX_PATH_SEC4 (0x05A0)
+#define CDC_VA_TX3_TX_PATH_SEC5 (0x05A4)
+#define CDC_VA_TX3_TX_PATH_SEC6 (0x05A8)
+
+#define VA_MAX_OFFSET (0x07A8)
+
+#define VA_MACRO_NUM_DECIMATORS 4
+#define VA_MACRO_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)
+#define VA_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define VA_MACRO_MCLK_FREQ 9600000
+#define VA_MACRO_TX_PATH_OFFSET 0x80
+#define VA_MACRO_SWR_MIC_MUX_SEL_MASK 0xF
+#define VA_MACRO_ADC_MUX_CFG_OFFSET 0x8
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+enum {
+ VA_MACRO_AIF_INVALID = 0,
+ VA_MACRO_AIF1_CAP,
+ VA_MACRO_AIF2_CAP,
+ VA_MACRO_AIF3_CAP,
+ VA_MACRO_MAX_DAIS,
+};
+
+enum {
+ VA_MACRO_DEC0,
+ VA_MACRO_DEC1,
+ VA_MACRO_DEC2,
+ VA_MACRO_DEC3,
+ VA_MACRO_DEC4,
+ VA_MACRO_DEC5,
+ VA_MACRO_DEC6,
+ VA_MACRO_DEC7,
+ VA_MACRO_DEC_MAX,
+};
+
+enum {
+ VA_MACRO_CLK_DIV_2,
+ VA_MACRO_CLK_DIV_3,
+ VA_MACRO_CLK_DIV_4,
+ VA_MACRO_CLK_DIV_6,
+ VA_MACRO_CLK_DIV_8,
+ VA_MACRO_CLK_DIV_16,
+};
+
+#define VA_NUM_CLKS_MAX 3
+
+struct va_macro {
+ struct device *dev;
+ unsigned long active_ch_mask[VA_MACRO_MAX_DAIS];
+ unsigned long active_ch_cnt[VA_MACRO_MAX_DAIS];
+ unsigned long active_decimator[VA_MACRO_MAX_DAIS];
+ u16 dmic_clk_div;
+
+ int dec_mode[VA_MACRO_NUM_DECIMATORS];
+ struct regmap *regmap;
+ struct clk_bulk_data clks[VA_NUM_CLKS_MAX];
+ struct clk_hw hw;
+
+ s32 dmic_0_1_clk_cnt;
+ s32 dmic_2_3_clk_cnt;
+ s32 dmic_4_5_clk_cnt;
+ s32 dmic_6_7_clk_cnt;
+ u8 dmic_0_1_clk_div;
+ u8 dmic_2_3_clk_div;
+ u8 dmic_4_5_clk_div;
+ u8 dmic_6_7_clk_div;
+};
+
+#define to_va_macro(_hw) container_of(_hw, struct va_macro, hw)
+
+static bool va_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_VA_TOP_CSR_CORE_ID_0:
+ case CDC_VA_TOP_CSR_CORE_ID_1:
+ case CDC_VA_TOP_CSR_CORE_ID_2:
+ case CDC_VA_TOP_CSR_CORE_ID_3:
+ case CDC_VA_TOP_CSR_DMIC0_CTL:
+ case CDC_VA_TOP_CSR_DMIC1_CTL:
+ case CDC_VA_TOP_CSR_DMIC2_CTL:
+ case CDC_VA_TOP_CSR_DMIC3_CTL:
+ return true;
+ }
+ return false;
+}
+
+static const struct reg_default va_defaults[] = {
+ /* VA macro */
+ { CDC_VA_CLK_RST_CTRL_MCLK_CONTROL, 0x00},
+ { CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00},
+ { CDC_VA_CLK_RST_CTRL_SWR_CONTROL, 0x00},
+ { CDC_VA_TOP_CSR_TOP_CFG0, 0x00},
+ { CDC_VA_TOP_CSR_DMIC0_CTL, 0x00},
+ { CDC_VA_TOP_CSR_DMIC1_CTL, 0x00},
+ { CDC_VA_TOP_CSR_DMIC2_CTL, 0x00},
+ { CDC_VA_TOP_CSR_DMIC3_CTL, 0x00},
+ { CDC_VA_TOP_CSR_DMIC_CFG, 0x80},
+ { CDC_VA_TOP_CSR_DEBUG_BUS, 0x00},
+ { CDC_VA_TOP_CSR_DEBUG_EN, 0x00},
+ { CDC_VA_TOP_CSR_TX_I2S_CTL, 0x0C},
+ { CDC_VA_TOP_CSR_I2S_CLK, 0x00},
+ { CDC_VA_TOP_CSR_I2S_RESET, 0x00},
+ { CDC_VA_TOP_CSR_CORE_ID_0, 0x00},
+ { CDC_VA_TOP_CSR_CORE_ID_1, 0x00},
+ { CDC_VA_TOP_CSR_CORE_ID_2, 0x00},
+ { CDC_VA_TOP_CSR_CORE_ID_3, 0x00},
+ { CDC_VA_TOP_CSR_SWR_MIC_CTL0, 0xEE},
+ { CDC_VA_TOP_CSR_SWR_MIC_CTL1, 0xEE},
+ { CDC_VA_TOP_CSR_SWR_MIC_CTL2, 0xEE},
+ { CDC_VA_TOP_CSR_SWR_CTRL, 0x06},
+
+ /* VA core */
+ { CDC_VA_INP_MUX_ADC_MUX0_CFG0, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX0_CFG1, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX1_CFG0, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX1_CFG1, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX2_CFG0, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX2_CFG1, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX3_CFG0, 0x00},
+ { CDC_VA_INP_MUX_ADC_MUX3_CFG1, 0x00},
+ { CDC_VA_TX0_TX_PATH_CTL, 0x04},
+ { CDC_VA_TX0_TX_PATH_CFG0, 0x10},
+ { CDC_VA_TX0_TX_PATH_CFG1, 0x0B},
+ { CDC_VA_TX0_TX_VOL_CTL, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC0, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC1, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC2, 0x01},
+ { CDC_VA_TX0_TX_PATH_SEC3, 0x3C},
+ { CDC_VA_TX0_TX_PATH_SEC4, 0x20},
+ { CDC_VA_TX0_TX_PATH_SEC5, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC6, 0x00},
+ { CDC_VA_TX0_TX_PATH_SEC7, 0x25},
+ { CDC_VA_TX1_TX_PATH_CTL, 0x04},
+ { CDC_VA_TX1_TX_PATH_CFG0, 0x10},
+ { CDC_VA_TX1_TX_PATH_CFG1, 0x0B},
+ { CDC_VA_TX1_TX_VOL_CTL, 0x00},
+ { CDC_VA_TX1_TX_PATH_SEC0, 0x00},
+ { CDC_VA_TX1_TX_PATH_SEC1, 0x00},
+ { CDC_VA_TX1_TX_PATH_SEC2, 0x01},
+ { CDC_VA_TX1_TX_PATH_SEC3, 0x3C},
+ { CDC_VA_TX1_TX_PATH_SEC4, 0x20},
+ { CDC_VA_TX1_TX_PATH_SEC5, 0x00},
+ { CDC_VA_TX1_TX_PATH_SEC6, 0x00},
+ { CDC_VA_TX2_TX_PATH_CTL, 0x04},
+ { CDC_VA_TX2_TX_PATH_CFG0, 0x10},
+ { CDC_VA_TX2_TX_PATH_CFG1, 0x0B},
+ { CDC_VA_TX2_TX_VOL_CTL, 0x00},
+ { CDC_VA_TX2_TX_PATH_SEC0, 0x00},
+ { CDC_VA_TX2_TX_PATH_SEC1, 0x00},
+ { CDC_VA_TX2_TX_PATH_SEC2, 0x01},
+ { CDC_VA_TX2_TX_PATH_SEC3, 0x3C},
+ { CDC_VA_TX2_TX_PATH_SEC4, 0x20},
+ { CDC_VA_TX2_TX_PATH_SEC5, 0x00},
+ { CDC_VA_TX2_TX_PATH_SEC6, 0x00},
+ { CDC_VA_TX3_TX_PATH_CTL, 0x04},
+ { CDC_VA_TX3_TX_PATH_CFG0, 0x10},
+ { CDC_VA_TX3_TX_PATH_CFG1, 0x0B},
+ { CDC_VA_TX3_TX_VOL_CTL, 0x00},
+ { CDC_VA_TX3_TX_PATH_SEC0, 0x00},
+ { CDC_VA_TX3_TX_PATH_SEC1, 0x00},
+ { CDC_VA_TX3_TX_PATH_SEC2, 0x01},
+ { CDC_VA_TX3_TX_PATH_SEC3, 0x3C},
+ { CDC_VA_TX3_TX_PATH_SEC4, 0x20},
+ { CDC_VA_TX3_TX_PATH_SEC5, 0x00},
+ { CDC_VA_TX3_TX_PATH_SEC6, 0x00},
+};
+
+static bool va_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_VA_CLK_RST_CTRL_MCLK_CONTROL:
+ case CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL:
+ case CDC_VA_CLK_RST_CTRL_SWR_CONTROL:
+ case CDC_VA_TOP_CSR_TOP_CFG0:
+ case CDC_VA_TOP_CSR_DMIC0_CTL:
+ case CDC_VA_TOP_CSR_DMIC1_CTL:
+ case CDC_VA_TOP_CSR_DMIC2_CTL:
+ case CDC_VA_TOP_CSR_DMIC3_CTL:
+ case CDC_VA_TOP_CSR_DMIC_CFG:
+ case CDC_VA_TOP_CSR_DEBUG_BUS:
+ case CDC_VA_TOP_CSR_DEBUG_EN:
+ case CDC_VA_TOP_CSR_TX_I2S_CTL:
+ case CDC_VA_TOP_CSR_I2S_CLK:
+ case CDC_VA_TOP_CSR_I2S_RESET:
+ case CDC_VA_INP_MUX_ADC_MUX0_CFG0:
+ case CDC_VA_INP_MUX_ADC_MUX0_CFG1:
+ case CDC_VA_INP_MUX_ADC_MUX1_CFG0:
+ case CDC_VA_INP_MUX_ADC_MUX1_CFG1:
+ case CDC_VA_INP_MUX_ADC_MUX2_CFG0:
+ case CDC_VA_INP_MUX_ADC_MUX2_CFG1:
+ case CDC_VA_INP_MUX_ADC_MUX3_CFG0:
+ case CDC_VA_INP_MUX_ADC_MUX3_CFG1:
+ case CDC_VA_TX0_TX_PATH_CTL:
+ case CDC_VA_TX0_TX_PATH_CFG0:
+ case CDC_VA_TX0_TX_PATH_CFG1:
+ case CDC_VA_TX0_TX_VOL_CTL:
+ case CDC_VA_TX0_TX_PATH_SEC0:
+ case CDC_VA_TX0_TX_PATH_SEC1:
+ case CDC_VA_TX0_TX_PATH_SEC2:
+ case CDC_VA_TX0_TX_PATH_SEC3:
+ case CDC_VA_TX0_TX_PATH_SEC4:
+ case CDC_VA_TX0_TX_PATH_SEC5:
+ case CDC_VA_TX0_TX_PATH_SEC6:
+ case CDC_VA_TX0_TX_PATH_SEC7:
+ case CDC_VA_TX1_TX_PATH_CTL:
+ case CDC_VA_TX1_TX_PATH_CFG0:
+ case CDC_VA_TX1_TX_PATH_CFG1:
+ case CDC_VA_TX1_TX_VOL_CTL:
+ case CDC_VA_TX1_TX_PATH_SEC0:
+ case CDC_VA_TX1_TX_PATH_SEC1:
+ case CDC_VA_TX1_TX_PATH_SEC2:
+ case CDC_VA_TX1_TX_PATH_SEC3:
+ case CDC_VA_TX1_TX_PATH_SEC4:
+ case CDC_VA_TX1_TX_PATH_SEC5:
+ case CDC_VA_TX1_TX_PATH_SEC6:
+ case CDC_VA_TX2_TX_PATH_CTL:
+ case CDC_VA_TX2_TX_PATH_CFG0:
+ case CDC_VA_TX2_TX_PATH_CFG1:
+ case CDC_VA_TX2_TX_VOL_CTL:
+ case CDC_VA_TX2_TX_PATH_SEC0:
+ case CDC_VA_TX2_TX_PATH_SEC1:
+ case CDC_VA_TX2_TX_PATH_SEC2:
+ case CDC_VA_TX2_TX_PATH_SEC3:
+ case CDC_VA_TX2_TX_PATH_SEC4:
+ case CDC_VA_TX2_TX_PATH_SEC5:
+ case CDC_VA_TX2_TX_PATH_SEC6:
+ case CDC_VA_TX3_TX_PATH_CTL:
+ case CDC_VA_TX3_TX_PATH_CFG0:
+ case CDC_VA_TX3_TX_PATH_CFG1:
+ case CDC_VA_TX3_TX_VOL_CTL:
+ case CDC_VA_TX3_TX_PATH_SEC0:
+ case CDC_VA_TX3_TX_PATH_SEC1:
+ case CDC_VA_TX3_TX_PATH_SEC2:
+ case CDC_VA_TX3_TX_PATH_SEC3:
+ case CDC_VA_TX3_TX_PATH_SEC4:
+ case CDC_VA_TX3_TX_PATH_SEC5:
+ case CDC_VA_TX3_TX_PATH_SEC6:
+ return true;
+ }
+
+ return false;
+}
+
+static bool va_is_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_VA_TOP_CSR_CORE_ID_0:
+ case CDC_VA_TOP_CSR_CORE_ID_1:
+ case CDC_VA_TOP_CSR_CORE_ID_2:
+ case CDC_VA_TOP_CSR_CORE_ID_3:
+ return true;
+ }
+
+ return va_is_rw_register(dev, reg);
+}
+
+static const struct regmap_config va_regmap_config = {
+ .name = "va_macro",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = va_defaults,
+ .num_reg_defaults = ARRAY_SIZE(va_defaults),
+ .max_register = VA_MAX_OFFSET,
+ .volatile_reg = va_is_volatile_register,
+ .readable_reg = va_is_readable_register,
+ .writeable_reg = va_is_rw_register,
+};
+
+static int va_clk_rsc_fs_gen_request(struct va_macro *va, bool enable)
+{
+ struct regmap *regmap = va->regmap;
+
+ if (enable) {
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_VA_MCLK_CONTROL_EN,
+ CDC_VA_MCLK_CONTROL_EN);
+
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_VA_FS_CONTROL_EN,
+ CDC_VA_FS_CONTROL_EN);
+
+ regmap_update_bits(regmap, CDC_VA_TOP_CSR_TOP_CFG0,
+ CDC_VA_FS_BROADCAST_EN,
+ CDC_VA_FS_BROADCAST_EN);
+ } else {
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_VA_MCLK_CONTROL_EN, 0x0);
+
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_VA_FS_CONTROL_EN, 0x0);
+
+ regmap_update_bits(regmap, CDC_VA_TOP_CSR_TOP_CFG0,
+ CDC_VA_FS_BROADCAST_EN, 0x0);
+ }
+
+ return 0;
+}
+
+static int va_macro_mclk_enable(struct va_macro *va, bool mclk_enable)
+{
+ struct regmap *regmap = va->regmap;
+
+ if (mclk_enable) {
+ va_clk_rsc_fs_gen_request(va, true);
+ regcache_mark_dirty(regmap);
+ regcache_sync_region(regmap, 0x0, VA_MAX_OFFSET);
+ } else {
+ va_clk_rsc_fs_gen_request(va, false);
+ }
+
+ return 0;
+}
+
+static int va_macro_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct va_macro *va = snd_soc_component_get_drvdata(comp);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return va_macro_mclk_enable(va, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return va_macro_mclk_enable(va, false);
+ }
+
+ return 0;
+}
+
+static int va_macro_put_dec_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget =
+ snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+ u16 mic_sel_reg;
+
+ val = ucontrol->value.enumerated.item[0];
+
+ switch (e->reg) {
+ case CDC_VA_INP_MUX_ADC_MUX0_CFG0:
+ mic_sel_reg = CDC_VA_TX0_TX_PATH_CFG0;
+ break;
+ case CDC_VA_INP_MUX_ADC_MUX1_CFG0:
+ mic_sel_reg = CDC_VA_TX1_TX_PATH_CFG0;
+ break;
+ case CDC_VA_INP_MUX_ADC_MUX2_CFG0:
+ mic_sel_reg = CDC_VA_TX2_TX_PATH_CFG0;
+ break;
+ case CDC_VA_INP_MUX_ADC_MUX3_CFG0:
+ mic_sel_reg = CDC_VA_TX3_TX_PATH_CFG0;
+ break;
+ default:
+ dev_err(component->dev, "%s: e->reg: 0x%x not expected\n",
+ __func__, e->reg);
+ return -EINVAL;
+ }
+
+ if (val != 0)
+ snd_soc_component_update_bits(component, mic_sel_reg,
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK,
+ CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC);
+
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static int va_macro_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget =
+ snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ u32 dai_id = widget->shift;
+ u32 dec_id = mc->shift;
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ if (test_bit(dec_id, &va->active_ch_mask[dai_id]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int va_macro_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget =
+ snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct snd_soc_dapm_update *update = NULL;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ u32 dai_id = widget->shift;
+ u32 dec_id = mc->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ if (enable) {
+ set_bit(dec_id, &va->active_ch_mask[dai_id]);
+ va->active_ch_cnt[dai_id]++;
+ va->active_decimator[dai_id] = dec_id;
+ } else {
+ clear_bit(dec_id, &va->active_ch_mask[dai_id]);
+ va->active_ch_cnt[dai_id]--;
+ va->active_decimator[dai_id] = -1;
+ }
+
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update);
+
+ return 0;
+}
+
+static int va_dmic_clk_enable(struct snd_soc_component *component,
+ u32 dmic, bool enable)
+{
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+ u8 dmic_clk_en = 0x01;
+ u16 dmic_clk_reg;
+ s32 *dmic_clk_cnt;
+ u8 *dmic_clk_div;
+ u8 freq_change_mask;
+ u8 clk_div = 0;
+
+ switch (dmic) {
+ case 0:
+ case 1:
+ dmic_clk_cnt = &(va->dmic_0_1_clk_cnt);
+ dmic_clk_div = &(va->dmic_0_1_clk_div);
+ dmic_clk_reg = CDC_VA_TOP_CSR_DMIC0_CTL;
+ freq_change_mask = CDC_VA_DMIC0_FREQ_CHANGE_MASK;
+ break;
+ case 2:
+ case 3:
+ dmic_clk_cnt = &(va->dmic_2_3_clk_cnt);
+ dmic_clk_div = &(va->dmic_2_3_clk_div);
+ dmic_clk_reg = CDC_VA_TOP_CSR_DMIC1_CTL;
+ freq_change_mask = CDC_VA_DMIC1_FREQ_CHANGE_MASK;
+ break;
+ case 4:
+ case 5:
+ dmic_clk_cnt = &(va->dmic_4_5_clk_cnt);
+ dmic_clk_div = &(va->dmic_4_5_clk_div);
+ dmic_clk_reg = CDC_VA_TOP_CSR_DMIC2_CTL;
+ freq_change_mask = CDC_VA_DMIC2_FREQ_CHANGE_MASK;
+ break;
+ case 6:
+ case 7:
+ dmic_clk_cnt = &(va->dmic_6_7_clk_cnt);
+ dmic_clk_div = &(va->dmic_6_7_clk_div);
+ dmic_clk_reg = CDC_VA_TOP_CSR_DMIC3_CTL;
+ freq_change_mask = CDC_VA_DMIC3_FREQ_CHANGE_MASK;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid DMIC Selection\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (enable) {
+ clk_div = va->dmic_clk_div;
+ (*dmic_clk_cnt)++;
+ if (*dmic_clk_cnt == 1) {
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ CDC_VA_RESET_ALL_DMICS_MASK,
+ CDC_VA_RESET_ALL_DMICS_DISABLE);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_CLK_SEL_MASK,
+ clk_div << CDC_VA_DMIC_CLK_SEL_SHFT);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ dmic_clk_en, dmic_clk_en);
+ } else {
+ if (*dmic_clk_div > clk_div) {
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ freq_change_mask,
+ freq_change_mask);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_CLK_SEL_MASK,
+ clk_div << CDC_VA_DMIC_CLK_SEL_SHFT);
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ freq_change_mask,
+ CDC_VA_DMIC_FREQ_CHANGE_DISABLE);
+ } else {
+ clk_div = *dmic_clk_div;
+ }
+ }
+ *dmic_clk_div = clk_div;
+ } else {
+ (*dmic_clk_cnt)--;
+ if (*dmic_clk_cnt == 0) {
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ dmic_clk_en, 0);
+ clk_div = 0;
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_CLK_SEL_MASK,
+ clk_div << CDC_VA_DMIC_CLK_SEL_SHFT);
+ } else {
+ clk_div = va->dmic_clk_div;
+ if (*dmic_clk_div > clk_div) {
+ clk_div = va->dmic_clk_div;
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ freq_change_mask,
+ freq_change_mask);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ CDC_VA_DMIC_CLK_SEL_MASK,
+ clk_div << CDC_VA_DMIC_CLK_SEL_SHFT);
+ snd_soc_component_update_bits(component,
+ CDC_VA_TOP_CSR_DMIC_CFG,
+ freq_change_mask,
+ CDC_VA_DMIC_FREQ_CHANGE_DISABLE);
+ } else {
+ clk_div = *dmic_clk_div;
+ }
+ }
+ *dmic_clk_div = clk_div;
+ }
+
+ return 0;
+}
+
+static int va_macro_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ unsigned int dmic = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ va_dmic_clk_enable(comp, dmic, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ va_dmic_clk_enable(comp, dmic, false);
+ break;
+ }
+
+ return 0;
+}
+
+static int va_macro_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ unsigned int decimator;
+ u16 tx_vol_ctl_reg, dec_cfg_reg, hpf_gate_reg;
+ u16 tx_gain_ctl_reg;
+ u8 hpf_cut_off_freq;
+
+ struct va_macro *va = snd_soc_component_get_drvdata(comp);
+
+ decimator = w->shift;
+
+ tx_vol_ctl_reg = CDC_VA_TX0_TX_PATH_CTL +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ hpf_gate_reg = CDC_VA_TX0_TX_PATH_SEC2 +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ dec_cfg_reg = CDC_VA_TX0_TX_PATH_CFG0 +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ tx_gain_ctl_reg = CDC_VA_TX0_TX_VOL_CTL +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(comp,
+ dec_cfg_reg, CDC_VA_ADC_MODE_MASK,
+ va->dec_mode[decimator] << CDC_VA_ADC_MODE_SHIFT);
+ /* Enable TX PGA Mute */
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Enable TX CLK */
+ snd_soc_component_update_bits(comp, tx_vol_ctl_reg,
+ CDC_VA_TX_PATH_CLK_EN_MASK,
+ CDC_VA_TX_PATH_CLK_EN);
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ CDC_VA_TX_HPF_ZERO_GATE_MASK,
+ CDC_VA_TX_HPF_ZERO_GATE);
+
+ usleep_range(1000, 1010);
+ hpf_cut_off_freq = (snd_soc_component_read(comp, dec_cfg_reg) &
+ TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
+
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+ snd_soc_component_update_bits(comp, dec_cfg_reg,
+ TX_HPF_CUT_OFF_FREQ_MASK,
+ CF_MIN_3DB_150HZ << 5);
+
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK,
+ CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_REQ);
+
+ /*
+ * Minimum 1 clk cycle delay is required as per HW spec
+ */
+ usleep_range(1000, 1010);
+
+ snd_soc_component_update_bits(comp,
+ hpf_gate_reg,
+ CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK,
+ 0x0);
+ }
+
+
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(comp, hpf_gate_reg,
+ CDC_VA_TX_HPF_ZERO_GATE_MASK,
+ CDC_VA_TX_HPF_ZERO_NO_GATE);
+ /*
+ * 6ms delay is required as per HW spec
+ */
+ usleep_range(6000, 6010);
+ /* apply gain after decimator is enabled */
+ snd_soc_component_write(comp, tx_gain_ctl_reg,
+ snd_soc_component_read(comp, tx_gain_ctl_reg));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable TX CLK */
+ snd_soc_component_update_bits(comp, tx_vol_ctl_reg,
+ CDC_VA_TX_PATH_CLK_EN_MASK,
+ CDC_VA_TX_PATH_CLK_DISABLE);
+ break;
+ }
+ return 0;
+}
+
+static int va_macro_dec_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct va_macro *va = snd_soc_component_get_drvdata(comp);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+
+ ucontrol->value.integer.value[0] = va->dec_mode[path];
+
+ return 0;
+}
+
+static int va_macro_dec_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ int value = ucontrol->value.integer.value[0];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int path = e->shift_l;
+ struct va_macro *va = snd_soc_component_get_drvdata(comp);
+
+ va->dec_mode[path] = value;
+
+ return 0;
+}
+
+static int va_macro_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int tx_fs_rate = -EINVAL;
+ struct snd_soc_component *component = dai->component;
+ u32 decimator, sample_rate;
+ u16 tx_fs_reg = 0;
+ struct device *va_dev = component->dev;
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ sample_rate = params_rate(params);
+ switch (sample_rate) {
+ case 8000:
+ tx_fs_rate = 0;
+ break;
+ case 16000:
+ tx_fs_rate = 1;
+ break;
+ case 32000:
+ tx_fs_rate = 3;
+ break;
+ case 48000:
+ tx_fs_rate = 4;
+ break;
+ case 96000:
+ tx_fs_rate = 5;
+ break;
+ case 192000:
+ tx_fs_rate = 6;
+ break;
+ case 384000:
+ tx_fs_rate = 7;
+ break;
+ default:
+ dev_err(va_dev, "%s: Invalid TX sample rate: %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ for_each_set_bit(decimator, &va->active_ch_mask[dai->id],
+ VA_MACRO_DEC_MAX) {
+ if (decimator >= 0) {
+ tx_fs_reg = CDC_VA_TX0_TX_PATH_CTL +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ snd_soc_component_update_bits(component, tx_fs_reg,
+ 0x0F, tx_fs_rate);
+ } else {
+ dev_err(va_dev,
+ "%s: ERROR: Invalid decimator: %d\n",
+ __func__, decimator);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int va_macro_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct snd_soc_component *component = dai->component;
+ struct device *va_dev = component->dev;
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ switch (dai->id) {
+ case VA_MACRO_AIF1_CAP:
+ case VA_MACRO_AIF2_CAP:
+ case VA_MACRO_AIF3_CAP:
+ *tx_slot = va->active_ch_mask[dai->id];
+ *tx_num = va->active_ch_cnt[dai->id];
+ break;
+ default:
+ dev_err(va_dev, "%s: Invalid AIF\n", __func__);
+ break;
+ }
+ return 0;
+}
+
+static int va_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+ u16 tx_vol_ctl_reg, decimator;
+
+ decimator = va->active_decimator[dai->id];
+
+ tx_vol_ctl_reg = CDC_VA_TX0_TX_PATH_CTL +
+ VA_MACRO_TX_PATH_OFFSET * decimator;
+ if (mute)
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ CDC_VA_TX_PATH_PGA_MUTE_EN_MASK,
+ CDC_VA_TX_PATH_PGA_MUTE_EN);
+ else
+ snd_soc_component_update_bits(component, tx_vol_ctl_reg,
+ CDC_VA_TX_PATH_PGA_MUTE_EN_MASK,
+ CDC_VA_TX_PATH_PGA_MUTE_DISABLE);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops va_macro_dai_ops = {
+ .hw_params = va_macro_hw_params,
+ .get_channel_map = va_macro_get_channel_map,
+ .mute_stream = va_macro_digital_mute,
+};
+
+static struct snd_soc_dai_driver va_macro_dais[] = {
+ {
+ .name = "va_macro_tx1",
+ .id = VA_MACRO_AIF1_CAP,
+ .capture = {
+ .stream_name = "VA_AIF1 Capture",
+ .rates = VA_MACRO_RATES,
+ .formats = VA_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &va_macro_dai_ops,
+ },
+ {
+ .name = "va_macro_tx2",
+ .id = VA_MACRO_AIF2_CAP,
+ .capture = {
+ .stream_name = "VA_AIF2 Capture",
+ .rates = VA_MACRO_RATES,
+ .formats = VA_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &va_macro_dai_ops,
+ },
+ {
+ .name = "va_macro_tx3",
+ .id = VA_MACRO_AIF3_CAP,
+ .capture = {
+ .stream_name = "VA_AIF3 Capture",
+ .rates = VA_MACRO_RATES,
+ .formats = VA_MACRO_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &va_macro_dai_ops,
+ },
+};
+
+static const char * const adc_mux_text[] = {
+ "VA_DMIC", "SWR_MIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(va_dec0_enum, CDC_VA_INP_MUX_ADC_MUX0_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(va_dec1_enum, CDC_VA_INP_MUX_ADC_MUX1_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(va_dec2_enum, CDC_VA_INP_MUX_ADC_MUX2_CFG1,
+ 0, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(va_dec3_enum, CDC_VA_INP_MUX_ADC_MUX3_CFG1,
+ 0, adc_mux_text);
+
+static const struct snd_kcontrol_new va_dec0_mux = SOC_DAPM_ENUM("va_dec0",
+ va_dec0_enum);
+static const struct snd_kcontrol_new va_dec1_mux = SOC_DAPM_ENUM("va_dec1",
+ va_dec1_enum);
+static const struct snd_kcontrol_new va_dec2_mux = SOC_DAPM_ENUM("va_dec2",
+ va_dec2_enum);
+static const struct snd_kcontrol_new va_dec3_mux = SOC_DAPM_ENUM("va_dec3",
+ va_dec3_enum);
+
+static const char * const dmic_mux_text[] = {
+ "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3",
+ "DMIC4", "DMIC5", "DMIC6", "DMIC7"
+};
+
+static SOC_ENUM_SINGLE_DECL(va_dmic0_enum, CDC_VA_INP_MUX_ADC_MUX0_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(va_dmic1_enum, CDC_VA_INP_MUX_ADC_MUX1_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(va_dmic2_enum, CDC_VA_INP_MUX_ADC_MUX2_CFG0,
+ 4, dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(va_dmic3_enum, CDC_VA_INP_MUX_ADC_MUX3_CFG0,
+ 4, dmic_mux_text);
+
+static const struct snd_kcontrol_new va_dmic0_mux = SOC_DAPM_ENUM_EXT("va_dmic0",
+ va_dmic0_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_dmic1_mux = SOC_DAPM_ENUM_EXT("va_dmic1",
+ va_dmic1_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_dmic2_mux = SOC_DAPM_ENUM_EXT("va_dmic2",
+ va_dmic2_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_dmic3_mux = SOC_DAPM_ENUM_EXT("va_dmic3",
+ va_dmic3_enum, snd_soc_dapm_get_enum_double,
+ va_macro_put_dec_enum);
+
+static const struct snd_kcontrol_new va_aif1_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new va_aif2_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new va_aif3_cap_mixer[] = {
+ SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+ SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0,
+ va_macro_tx_mixer_get, va_macro_tx_mixer_put),
+};
+
+static const struct snd_soc_dapm_widget va_macro_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("VA_AIF1 CAP", "VA_AIF1 Capture", 0,
+ SND_SOC_NOPM, VA_MACRO_AIF1_CAP, 0),
+
+ SND_SOC_DAPM_AIF_OUT("VA_AIF2 CAP", "VA_AIF2 Capture", 0,
+ SND_SOC_NOPM, VA_MACRO_AIF2_CAP, 0),
+
+ SND_SOC_DAPM_AIF_OUT("VA_AIF3 CAP", "VA_AIF3 Capture", 0,
+ SND_SOC_NOPM, VA_MACRO_AIF3_CAP, 0),
+
+ SND_SOC_DAPM_MIXER("VA_AIF1_CAP Mixer", SND_SOC_NOPM,
+ VA_MACRO_AIF1_CAP, 0,
+ va_aif1_cap_mixer, ARRAY_SIZE(va_aif1_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("VA_AIF2_CAP Mixer", SND_SOC_NOPM,
+ VA_MACRO_AIF2_CAP, 0,
+ va_aif2_cap_mixer, ARRAY_SIZE(va_aif2_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("VA_AIF3_CAP Mixer", SND_SOC_NOPM,
+ VA_MACRO_AIF3_CAP, 0,
+ va_aif3_cap_mixer, ARRAY_SIZE(va_aif3_cap_mixer)),
+
+ SND_SOC_DAPM_MUX("VA DMIC MUX0", SND_SOC_NOPM, 0, 0, &va_dmic0_mux),
+ SND_SOC_DAPM_MUX("VA DMIC MUX1", SND_SOC_NOPM, 0, 0, &va_dmic1_mux),
+ SND_SOC_DAPM_MUX("VA DMIC MUX2", SND_SOC_NOPM, 0, 0, &va_dmic2_mux),
+ SND_SOC_DAPM_MUX("VA DMIC MUX3", SND_SOC_NOPM, 0, 0, &va_dmic3_mux),
+
+ SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-micb", 0, 0),
+ SND_SOC_DAPM_INPUT("DMIC0 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC1 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC2 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC3 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC4 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC5 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC6 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC7 Pin"),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC0", NULL, SND_SOC_NOPM, 0, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC1", NULL, SND_SOC_NOPM, 1, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC2", NULL, SND_SOC_NOPM, 2, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC3", NULL, SND_SOC_NOPM, 3, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC4", NULL, SND_SOC_NOPM, 4, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC5", NULL, SND_SOC_NOPM, 5, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC6", NULL, SND_SOC_NOPM, 6, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("VA DMIC7", NULL, SND_SOC_NOPM, 7, 0,
+ va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("VA SWR_ADC0"),
+ SND_SOC_DAPM_INPUT("VA SWR_ADC1"),
+ SND_SOC_DAPM_INPUT("VA SWR_ADC2"),
+ SND_SOC_DAPM_INPUT("VA SWR_ADC3"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC0"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC1"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC2"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC3"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC4"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC5"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC6"),
+ SND_SOC_DAPM_INPUT("VA SWR_MIC7"),
+
+ SND_SOC_DAPM_MUX_E("VA DEC0 MUX", SND_SOC_NOPM, VA_MACRO_DEC0, 0,
+ &va_dec0_mux, va_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("VA DEC1 MUX", SND_SOC_NOPM, VA_MACRO_DEC1, 0,
+ &va_dec1_mux, va_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("VA DEC2 MUX", SND_SOC_NOPM, VA_MACRO_DEC2, 0,
+ &va_dec2_mux, va_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("VA DEC3 MUX", SND_SOC_NOPM, VA_MACRO_DEC3, 0,
+ &va_dec3_mux, va_macro_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("VA_MCLK", -1, SND_SOC_NOPM, 0, 0,
+ va_macro_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route va_audio_map[] = {
+ {"VA_AIF1 CAP", NULL, "VA_MCLK"},
+ {"VA_AIF2 CAP", NULL, "VA_MCLK"},
+ {"VA_AIF3 CAP", NULL, "VA_MCLK"},
+
+ {"VA_AIF1 CAP", NULL, "VA_AIF1_CAP Mixer"},
+ {"VA_AIF2 CAP", NULL, "VA_AIF2_CAP Mixer"},
+ {"VA_AIF3 CAP", NULL, "VA_AIF3_CAP Mixer"},
+
+ {"VA_AIF1_CAP Mixer", "DEC0", "VA DEC0 MUX"},
+ {"VA_AIF1_CAP Mixer", "DEC1", "VA DEC1 MUX"},
+ {"VA_AIF1_CAP Mixer", "DEC2", "VA DEC2 MUX"},
+ {"VA_AIF1_CAP Mixer", "DEC3", "VA DEC3 MUX"},
+
+ {"VA_AIF2_CAP Mixer", "DEC0", "VA DEC0 MUX"},
+ {"VA_AIF2_CAP Mixer", "DEC1", "VA DEC1 MUX"},
+ {"VA_AIF2_CAP Mixer", "DEC2", "VA DEC2 MUX"},
+ {"VA_AIF2_CAP Mixer", "DEC3", "VA DEC3 MUX"},
+
+ {"VA_AIF3_CAP Mixer", "DEC0", "VA DEC0 MUX"},
+ {"VA_AIF3_CAP Mixer", "DEC1", "VA DEC1 MUX"},
+ {"VA_AIF3_CAP Mixer", "DEC2", "VA DEC2 MUX"},
+ {"VA_AIF3_CAP Mixer", "DEC3", "VA DEC3 MUX"},
+
+ {"VA DEC0 MUX", "VA_DMIC", "VA DMIC MUX0"},
+ {"VA DMIC MUX0", "DMIC0", "VA DMIC0"},
+ {"VA DMIC MUX0", "DMIC1", "VA DMIC1"},
+ {"VA DMIC MUX0", "DMIC2", "VA DMIC2"},
+ {"VA DMIC MUX0", "DMIC3", "VA DMIC3"},
+ {"VA DMIC MUX0", "DMIC4", "VA DMIC4"},
+ {"VA DMIC MUX0", "DMIC5", "VA DMIC5"},
+ {"VA DMIC MUX0", "DMIC6", "VA DMIC6"},
+ {"VA DMIC MUX0", "DMIC7", "VA DMIC7"},
+
+ {"VA DEC1 MUX", "VA_DMIC", "VA DMIC MUX1"},
+ {"VA DMIC MUX1", "DMIC0", "VA DMIC0"},
+ {"VA DMIC MUX1", "DMIC1", "VA DMIC1"},
+ {"VA DMIC MUX1", "DMIC2", "VA DMIC2"},
+ {"VA DMIC MUX1", "DMIC3", "VA DMIC3"},
+ {"VA DMIC MUX1", "DMIC4", "VA DMIC4"},
+ {"VA DMIC MUX1", "DMIC5", "VA DMIC5"},
+ {"VA DMIC MUX1", "DMIC6", "VA DMIC6"},
+ {"VA DMIC MUX1", "DMIC7", "VA DMIC7"},
+
+ {"VA DEC2 MUX", "VA_DMIC", "VA DMIC MUX2"},
+ {"VA DMIC MUX2", "DMIC0", "VA DMIC0"},
+ {"VA DMIC MUX2", "DMIC1", "VA DMIC1"},
+ {"VA DMIC MUX2", "DMIC2", "VA DMIC2"},
+ {"VA DMIC MUX2", "DMIC3", "VA DMIC3"},
+ {"VA DMIC MUX2", "DMIC4", "VA DMIC4"},
+ {"VA DMIC MUX2", "DMIC5", "VA DMIC5"},
+ {"VA DMIC MUX2", "DMIC6", "VA DMIC6"},
+ {"VA DMIC MUX2", "DMIC7", "VA DMIC7"},
+
+ {"VA DEC3 MUX", "VA_DMIC", "VA DMIC MUX3"},
+ {"VA DMIC MUX3", "DMIC0", "VA DMIC0"},
+ {"VA DMIC MUX3", "DMIC1", "VA DMIC1"},
+ {"VA DMIC MUX3", "DMIC2", "VA DMIC2"},
+ {"VA DMIC MUX3", "DMIC3", "VA DMIC3"},
+ {"VA DMIC MUX3", "DMIC4", "VA DMIC4"},
+ {"VA DMIC MUX3", "DMIC5", "VA DMIC5"},
+ {"VA DMIC MUX3", "DMIC6", "VA DMIC6"},
+ {"VA DMIC MUX3", "DMIC7", "VA DMIC7"},
+
+ { "VA DMIC0", NULL, "DMIC0 Pin" },
+ { "VA DMIC1", NULL, "DMIC1 Pin" },
+ { "VA DMIC2", NULL, "DMIC2 Pin" },
+ { "VA DMIC3", NULL, "DMIC3 Pin" },
+ { "VA DMIC4", NULL, "DMIC4 Pin" },
+ { "VA DMIC5", NULL, "DMIC5 Pin" },
+ { "VA DMIC6", NULL, "DMIC6 Pin" },
+ { "VA DMIC7", NULL, "DMIC7 Pin" },
+};
+
+static const char * const dec_mode_mux_text[] = {
+ "ADC_DEFAULT", "ADC_LOW_PWR", "ADC_HIGH_PERF",
+};
+
+static const struct soc_enum dec_mode_mux_enum[] = {
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(dec_mode_mux_text),
+ dec_mode_mux_text),
+};
+
+static const struct snd_kcontrol_new va_macro_snd_controls[] = {
+ SOC_SINGLE_S8_TLV("VA_DEC0 Volume", CDC_VA_TX0_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("VA_DEC1 Volume", CDC_VA_TX1_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("VA_DEC2 Volume", CDC_VA_TX2_TX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("VA_DEC3 Volume", CDC_VA_TX3_TX_VOL_CTL,
+ -84, 40, digital_gain),
+
+ SOC_ENUM_EXT("VA_DEC0 MODE", dec_mode_mux_enum[0],
+ va_macro_dec_mode_get, va_macro_dec_mode_put),
+ SOC_ENUM_EXT("VA_DEC1 MODE", dec_mode_mux_enum[1],
+ va_macro_dec_mode_get, va_macro_dec_mode_put),
+ SOC_ENUM_EXT("VA_DEC2 MODE", dec_mode_mux_enum[2],
+ va_macro_dec_mode_get, va_macro_dec_mode_put),
+ SOC_ENUM_EXT("VA_DEC3 MODE", dec_mode_mux_enum[3],
+ va_macro_dec_mode_get, va_macro_dec_mode_put),
+};
+
+static int va_macro_component_probe(struct snd_soc_component *component)
+{
+ struct va_macro *va = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_init_regmap(component, va->regmap);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver va_macro_component_drv = {
+ .name = "VA MACRO",
+ .probe = va_macro_component_probe,
+ .controls = va_macro_snd_controls,
+ .num_controls = ARRAY_SIZE(va_macro_snd_controls),
+ .dapm_widgets = va_macro_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(va_macro_dapm_widgets),
+ .dapm_routes = va_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(va_audio_map),
+};
+
+static int fsgen_gate_enable(struct clk_hw *hw)
+{
+ return va_macro_mclk_enable(to_va_macro(hw), true);
+}
+
+static void fsgen_gate_disable(struct clk_hw *hw)
+{
+ va_macro_mclk_enable(to_va_macro(hw), false);
+}
+
+static int fsgen_gate_is_enabled(struct clk_hw *hw)
+{
+ struct va_macro *va = to_va_macro(hw);
+ int val;
+
+ regmap_read(va->regmap, CDC_VA_TOP_CSR_TOP_CFG0, &val);
+
+ return !!(val & CDC_VA_FS_BROADCAST_EN);
+}
+
+static const struct clk_ops fsgen_gate_ops = {
+ .prepare = fsgen_gate_enable,
+ .unprepare = fsgen_gate_disable,
+ .is_enabled = fsgen_gate_is_enabled,
+};
+
+static int va_macro_register_fsgen_output(struct va_macro *va)
+{
+ struct clk *parent = va->clks[2].clk;
+ struct device *dev = va->dev;
+ struct device_node *np = dev->of_node;
+ const char *parent_clk_name;
+ const char *clk_name = "fsgen";
+ struct clk_init_data init;
+ int ret;
+
+ parent_clk_name = __clk_get_name(parent);
+
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = &fsgen_gate_ops;
+ init.flags = 0;
+ init.parent_names = &parent_clk_name;
+ init.num_parents = 1;
+ va->hw.init = &init;
+ ret = devm_clk_hw_register(va->dev, &va->hw);
+ if (ret)
+ return ret;
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, va->hw.clk);
+}
+
+static int va_macro_validate_dmic_sample_rate(u32 dmic_sample_rate,
+ struct va_macro *va)
+{
+ u32 div_factor;
+ u32 mclk_rate = VA_MACRO_MCLK_FREQ;
+
+ if (!dmic_sample_rate || mclk_rate % dmic_sample_rate != 0)
+ goto undefined_rate;
+
+ div_factor = mclk_rate / dmic_sample_rate;
+
+ switch (div_factor) {
+ case 2:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_2;
+ break;
+ case 3:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_3;
+ break;
+ case 4:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_4;
+ break;
+ case 6:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_6;
+ break;
+ case 8:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_8;
+ break;
+ case 16:
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_16;
+ break;
+ default:
+ /* Any other DIV factor is invalid */
+ goto undefined_rate;
+ }
+
+ return dmic_sample_rate;
+
+undefined_rate:
+ dev_err(va->dev, "%s: Invalid rate %d, for mclk %d\n",
+ __func__, dmic_sample_rate, mclk_rate);
+ dmic_sample_rate = 0;
+
+ return dmic_sample_rate;
+}
+
+static int va_macro_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct va_macro *va;
+ void __iomem *base;
+ u32 sample_rate = 0;
+ int ret;
+
+ va = devm_kzalloc(dev, sizeof(*va), GFP_KERNEL);
+ if (!va)
+ return -ENOMEM;
+
+ va->dev = dev;
+ va->clks[0].id = "macro";
+ va->clks[1].id = "dcodec";
+ va->clks[2].id = "mclk";
+
+ ret = devm_clk_bulk_get(dev, VA_NUM_CLKS_MAX, va->clks);
+ if (ret) {
+ dev_err(dev, "Error getting VA Clocks (%d)\n", ret);
+ return ret;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "qcom,dmic-sample-rate",
+ &sample_rate);
+ if (ret) {
+ dev_err(dev, "qcom,dmic-sample-rate dt entry missing\n");
+ va->dmic_clk_div = VA_MACRO_CLK_DIV_2;
+ } else {
+ ret = va_macro_validate_dmic_sample_rate(sample_rate, va);
+ if (!ret)
+ return -EINVAL;
+ }
+
+ /* mclk rate */
+ clk_set_rate(va->clks[1].clk, VA_MACRO_MCLK_FREQ);
+ ret = clk_bulk_prepare_enable(VA_NUM_CLKS_MAX, va->clks);
+ if (ret)
+ return ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto err;
+ }
+
+ va->regmap = devm_regmap_init_mmio(dev, base, &va_regmap_config);
+ if (IS_ERR(va->regmap)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ dev_set_drvdata(dev, va);
+ ret = va_macro_register_fsgen_output(va);
+ if (ret)
+ goto err;
+
+ ret = devm_snd_soc_register_component(dev, &va_macro_component_drv,
+ va_macro_dais,
+ ARRAY_SIZE(va_macro_dais));
+ if (ret)
+ goto soc_err;
+
+ return ret;
+
+soc_err:
+ of_clk_del_provider(pdev->dev.of_node);
+err:
+ clk_bulk_disable_unprepare(VA_NUM_CLKS_MAX, va->clks);
+
+ return ret;
+}
+
+static int va_macro_remove(struct platform_device *pdev)
+{
+ struct va_macro *va = dev_get_drvdata(&pdev->dev);
+
+ of_clk_del_provider(pdev->dev.of_node);
+ clk_bulk_disable_unprepare(VA_NUM_CLKS_MAX, va->clks);
+
+ return 0;
+}
+
+static const struct of_device_id va_macro_dt_match[] = {
+ { .compatible = "qcom,sm8250-lpass-va-macro" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, va_macro_dt_match);
+
+static struct platform_driver va_macro_driver = {
+ .driver = {
+ .name = "va_macro",
+ .of_match_table = va_macro_dt_match,
+ .suppress_bind_attrs = true,
+ },
+ .probe = va_macro_probe,
+ .remove = va_macro_remove,
+};
+
+module_platform_driver(va_macro_driver);
+MODULE_DESCRIPTION("VA macro driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
new file mode 100644
index 000000000000..d3de35e80cf0
--- /dev/null
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -0,0 +1,2472 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of_clk.h>
+#include <linux/clk-provider.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/of_platform.h>
+#include <sound/tlv.h>
+#include "lpass-wsa-macro.h"
+
+#define CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL (0x0000)
+#define CDC_WSA_MCLK_EN_MASK BIT(0)
+#define CDC_WSA_MCLK_ENABLE BIT(0)
+#define CDC_WSA_MCLK_DISABLE 0
+#define CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004)
+#define CDC_WSA_FS_CNT_EN_MASK BIT(0)
+#define CDC_WSA_FS_CNT_ENABLE BIT(0)
+#define CDC_WSA_FS_CNT_DISABLE 0
+#define CDC_WSA_CLK_RST_CTRL_SWR_CONTROL (0x0008)
+#define CDC_WSA_TOP_TOP_CFG0 (0x0080)
+#define CDC_WSA_TOP_TOP_CFG1 (0x0084)
+#define CDC_WSA_TOP_FREQ_MCLK (0x0088)
+#define CDC_WSA_TOP_DEBUG_BUS_SEL (0x008C)
+#define CDC_WSA_TOP_DEBUG_EN0 (0x0090)
+#define CDC_WSA_TOP_DEBUG_EN1 (0x0094)
+#define CDC_WSA_TOP_DEBUG_DSM_LB (0x0098)
+#define CDC_WSA_TOP_RX_I2S_CTL (0x009C)
+#define CDC_WSA_TOP_TX_I2S_CTL (0x00A0)
+#define CDC_WSA_TOP_I2S_CLK (0x00A4)
+#define CDC_WSA_TOP_I2S_RESET (0x00A8)
+#define CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 (0x0100)
+#define CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK GENMASK(5, 3)
+#define CDC_WSA_RX_INTX_2_SEL_MASK GENMASK(2, 0)
+#define CDC_WSA_RX_INP_MUX_RX_INT0_CFG1 (0x0104)
+#define CDC_WSA_RX_INP_MUX_RX_INT1_CFG0 (0x0108)
+#define CDC_WSA_RX_INP_MUX_RX_INT1_CFG1 (0x010C)
+#define CDC_WSA_RX_INP_MUX_RX_MIX_CFG0 (0x0110)
+#define CDC_WSA_RX_MIX_TX1_SEL_MASK GENMASK(5, 3)
+#define CDC_WSA_RX_MIX_TX1_SEL_SHFT 3
+#define CDC_WSA_RX_MIX_TX0_SEL_MASK GENMASK(2, 0)
+#define CDC_WSA_RX_INP_MUX_RX_EC_CFG0 (0x0114)
+#define CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0 (0x0118)
+#define CDC_WSA_TX0_SPKR_PROT_PATH_CTL (0x0244)
+#define CDC_WSA_TX_SPKR_PROT_RESET_MASK BIT(5)
+#define CDC_WSA_TX_SPKR_PROT_RESET BIT(5)
+#define CDC_WSA_TX_SPKR_PROT_NO_RESET 0
+#define CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK BIT(4)
+#define CDC_WSA_TX_SPKR_PROT_CLK_ENABLE BIT(4)
+#define CDC_WSA_TX_SPKR_PROT_CLK_DISABLE 0
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK GENMASK(3, 0)
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K 0
+#define CDC_WSA_TX0_SPKR_PROT_PATH_CFG0 (0x0248)
+#define CDC_WSA_TX1_SPKR_PROT_PATH_CTL (0x0264)
+#define CDC_WSA_TX1_SPKR_PROT_PATH_CFG0 (0x0268)
+#define CDC_WSA_TX2_SPKR_PROT_PATH_CTL (0x0284)
+#define CDC_WSA_TX2_SPKR_PROT_PATH_CFG0 (0x0288)
+#define CDC_WSA_TX3_SPKR_PROT_PATH_CTL (0x02A4)
+#define CDC_WSA_TX3_SPKR_PROT_PATH_CFG0 (0x02A8)
+#define CDC_WSA_INTR_CTRL_CFG (0x0340)
+#define CDC_WSA_INTR_CTRL_CLR_COMMIT (0x0344)
+#define CDC_WSA_INTR_CTRL_PIN1_MASK0 (0x0360)
+#define CDC_WSA_INTR_CTRL_PIN1_STATUS0 (0x0368)
+#define CDC_WSA_INTR_CTRL_PIN1_CLEAR0 (0x0370)
+#define CDC_WSA_INTR_CTRL_PIN2_MASK0 (0x0380)
+#define CDC_WSA_INTR_CTRL_PIN2_STATUS0 (0x0388)
+#define CDC_WSA_INTR_CTRL_PIN2_CLEAR0 (0x0390)
+#define CDC_WSA_INTR_CTRL_LEVEL0 (0x03C0)
+#define CDC_WSA_INTR_CTRL_BYPASS0 (0x03C8)
+#define CDC_WSA_INTR_CTRL_SET0 (0x03D0)
+#define CDC_WSA_RX0_RX_PATH_CTL (0x0400)
+#define CDC_WSA_RX_PATH_CLK_EN_MASK BIT(5)
+#define CDC_WSA_RX_PATH_CLK_ENABLE BIT(5)
+#define CDC_WSA_RX_PATH_CLK_DISABLE 0
+#define CDC_WSA_RX_PATH_PGA_MUTE_EN_MASK BIT(4)
+#define CDC_WSA_RX_PATH_PGA_MUTE_ENABLE BIT(4)
+#define CDC_WSA_RX_PATH_PGA_MUTE_DISABLE 0
+#define CDC_WSA_RX0_RX_PATH_CFG0 (0x0404)
+#define CDC_WSA_RX_PATH_COMP_EN_MASK BIT(1)
+#define CDC_WSA_RX_PATH_COMP_ENABLE BIT(1)
+#define CDC_WSA_RX_PATH_HD2_EN_MASK BIT(2)
+#define CDC_WSA_RX_PATH_HD2_ENABLE BIT(2)
+#define CDC_WSA_RX_PATH_SPKR_RATE_MASK BIT(3)
+#define CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072 BIT(3)
+#define CDC_WSA_RX0_RX_PATH_CFG1 (0x0408)
+#define CDC_WSA_RX_PATH_SMART_BST_EN_MASK BIT(0)
+#define CDC_WSA_RX_PATH_SMART_BST_ENABLE BIT(0)
+#define CDC_WSA_RX_PATH_SMART_BST_DISABLE 0
+#define CDC_WSA_RX0_RX_PATH_CFG2 (0x040C)
+#define CDC_WSA_RX0_RX_PATH_CFG3 (0x0410)
+#define CDC_WSA_RX_DC_DCOEFF_MASK GENMASK(1, 0)
+#define CDC_WSA_RX0_RX_VOL_CTL (0x0414)
+#define CDC_WSA_RX0_RX_PATH_MIX_CTL (0x0418)
+#define CDC_WSA_RX_PATH_MIX_CLK_EN_MASK BIT(5)
+#define CDC_WSA_RX_PATH_MIX_CLK_ENABLE BIT(5)
+#define CDC_WSA_RX_PATH_MIX_CLK_DISABLE 0
+#define CDC_WSA_RX0_RX_PATH_MIX_CFG (0x041C)
+#define CDC_WSA_RX0_RX_VOL_MIX_CTL (0x0420)
+#define CDC_WSA_RX0_RX_PATH_SEC0 (0x0424)
+#define CDC_WSA_RX0_RX_PATH_SEC1 (0x0428)
+#define CDC_WSA_RX_PGA_HALF_DB_MASK BIT(0)
+#define CDC_WSA_RX_PGA_HALF_DB_ENABLE BIT(0)
+#define CDC_WSA_RX_PGA_HALF_DB_DISABLE 0
+#define CDC_WSA_RX0_RX_PATH_SEC2 (0x042C)
+#define CDC_WSA_RX0_RX_PATH_SEC3 (0x0430)
+#define CDC_WSA_RX_PATH_HD2_SCALE_MASK GENMASK(1, 0)
+#define CDC_WSA_RX_PATH_HD2_ALPHA_MASK GENMASK(5, 2)
+#define CDC_WSA_RX0_RX_PATH_SEC5 (0x0438)
+#define CDC_WSA_RX0_RX_PATH_SEC6 (0x043C)
+#define CDC_WSA_RX0_RX_PATH_SEC7 (0x0440)
+#define CDC_WSA_RX0_RX_PATH_MIX_SEC0 (0x0444)
+#define CDC_WSA_RX0_RX_PATH_MIX_SEC1 (0x0448)
+#define CDC_WSA_RX0_RX_PATH_DSMDEM_CTL (0x044C)
+#define CDC_WSA_RX_DSMDEM_CLK_EN_MASK BIT(0)
+#define CDC_WSA_RX_DSMDEM_CLK_ENABLE BIT(0)
+#define CDC_WSA_RX1_RX_PATH_CTL (0x0480)
+#define CDC_WSA_RX1_RX_PATH_CFG0 (0x0484)
+#define CDC_WSA_RX1_RX_PATH_CFG1 (0x0488)
+#define CDC_WSA_RX1_RX_PATH_CFG2 (0x048C)
+#define CDC_WSA_RX1_RX_PATH_CFG3 (0x0490)
+#define CDC_WSA_RX1_RX_VOL_CTL (0x0494)
+#define CDC_WSA_RX1_RX_PATH_MIX_CTL (0x0498)
+#define CDC_WSA_RX1_RX_PATH_MIX_CFG (0x049C)
+#define CDC_WSA_RX1_RX_VOL_MIX_CTL (0x04A0)
+#define CDC_WSA_RX1_RX_PATH_SEC0 (0x04A4)
+#define CDC_WSA_RX1_RX_PATH_SEC1 (0x04A8)
+#define CDC_WSA_RX1_RX_PATH_SEC2 (0x04AC)
+#define CDC_WSA_RX1_RX_PATH_SEC3 (0x04B0)
+#define CDC_WSA_RX1_RX_PATH_SEC5 (0x04B8)
+#define CDC_WSA_RX1_RX_PATH_SEC6 (0x04BC)
+#define CDC_WSA_RX1_RX_PATH_SEC7 (0x04C0)
+#define CDC_WSA_RX1_RX_PATH_MIX_SEC0 (0x04C4)
+#define CDC_WSA_RX1_RX_PATH_MIX_SEC1 (0x04C8)
+#define CDC_WSA_RX1_RX_PATH_DSMDEM_CTL (0x04CC)
+#define CDC_WSA_BOOST0_BOOST_PATH_CTL (0x0500)
+#define CDC_WSA_BOOST_PATH_CLK_EN_MASK BIT(4)
+#define CDC_WSA_BOOST_PATH_CLK_ENABLE BIT(4)
+#define CDC_WSA_BOOST_PATH_CLK_DISABLE 0
+#define CDC_WSA_BOOST0_BOOST_CTL (0x0504)
+#define CDC_WSA_BOOST0_BOOST_CFG1 (0x0508)
+#define CDC_WSA_BOOST0_BOOST_CFG2 (0x050C)
+#define CDC_WSA_BOOST1_BOOST_PATH_CTL (0x0540)
+#define CDC_WSA_BOOST1_BOOST_CTL (0x0544)
+#define CDC_WSA_BOOST1_BOOST_CFG1 (0x0548)
+#define CDC_WSA_BOOST1_BOOST_CFG2 (0x054C)
+#define CDC_WSA_COMPANDER0_CTL0 (0x0580)
+#define CDC_WSA_COMPANDER_CLK_EN_MASK BIT(0)
+#define CDC_WSA_COMPANDER_CLK_ENABLE BIT(0)
+#define CDC_WSA_COMPANDER_SOFT_RST_MASK BIT(1)
+#define CDC_WSA_COMPANDER_SOFT_RST_ENABLE BIT(1)
+#define CDC_WSA_COMPANDER_HALT_MASK BIT(2)
+#define CDC_WSA_COMPANDER_HALT BIT(2)
+#define CDC_WSA_COMPANDER0_CTL1 (0x0584)
+#define CDC_WSA_COMPANDER0_CTL2 (0x0588)
+#define CDC_WSA_COMPANDER0_CTL3 (0x058C)
+#define CDC_WSA_COMPANDER0_CTL4 (0x0590)
+#define CDC_WSA_COMPANDER0_CTL5 (0x0594)
+#define CDC_WSA_COMPANDER0_CTL6 (0x0598)
+#define CDC_WSA_COMPANDER0_CTL7 (0x059C)
+#define CDC_WSA_COMPANDER1_CTL0 (0x05C0)
+#define CDC_WSA_COMPANDER1_CTL1 (0x05C4)
+#define CDC_WSA_COMPANDER1_CTL2 (0x05C8)
+#define CDC_WSA_COMPANDER1_CTL3 (0x05CC)
+#define CDC_WSA_COMPANDER1_CTL4 (0x05D0)
+#define CDC_WSA_COMPANDER1_CTL5 (0x05D4)
+#define CDC_WSA_COMPANDER1_CTL6 (0x05D8)
+#define CDC_WSA_COMPANDER1_CTL7 (0x05DC)
+#define CDC_WSA_SOFTCLIP0_CRC (0x0600)
+#define CDC_WSA_SOFTCLIP_CLK_EN_MASK BIT(0)
+#define CDC_WSA_SOFTCLIP_CLK_ENABLE BIT(0)
+#define CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0604)
+#define CDC_WSA_SOFTCLIP_EN_MASK BIT(0)
+#define CDC_WSA_SOFTCLIP_ENABLE BIT(0)
+#define CDC_WSA_SOFTCLIP1_CRC (0x0640)
+#define CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0644)
+#define CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL (0x0680)
+#define CDC_WSA_EC_HQ_EC_CLK_EN_MASK BIT(0)
+#define CDC_WSA_EC_HQ_EC_CLK_ENABLE BIT(0)
+#define CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 (0x0684)
+#define CDC_WSA_EC_HQ_EC_REF_PCM_RATE_MASK GENMASK(4, 1)
+#define CDC_WSA_EC_HQ_EC_REF_PCM_RATE_48K BIT(3)
+#define CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL (0x06C0)
+#define CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0 (0x06C4)
+#define CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL (0x0700)
+#define CDC_WSA_SPLINE_ASRC0_CTL0 (0x0704)
+#define CDC_WSA_SPLINE_ASRC0_CTL1 (0x0708)
+#define CDC_WSA_SPLINE_ASRC0_FIFO_CTL (0x070C)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB (0x0710)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB (0x0714)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB (0x0718)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB (0x071C)
+#define CDC_WSA_SPLINE_ASRC0_STATUS_FIFO (0x0720)
+#define CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL (0x0740)
+#define CDC_WSA_SPLINE_ASRC1_CTL0 (0x0744)
+#define CDC_WSA_SPLINE_ASRC1_CTL1 (0x0748)
+#define CDC_WSA_SPLINE_ASRC1_FIFO_CTL (0x074C)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB (0x0750)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB (0x0754)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB (0x0758)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB (0x075C)
+#define CDC_WSA_SPLINE_ASRC1_STATUS_FIFO (0x0760)
+#define WSA_MAX_OFFSET (0x0760)
+
+#define WSA_MACRO_RX_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)
+#define WSA_MACRO_RX_MIX_RATES (SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define WSA_MACRO_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define WSA_MACRO_ECHO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_48000)
+#define WSA_MACRO_ECHO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define NUM_INTERPOLATORS 2
+#define WSA_NUM_CLKS_MAX 5
+#define WSA_MACRO_MCLK_FREQ 19200000
+#define WSA_MACRO_MUX_INP_SHFT 0x3
+#define WSA_MACRO_MUX_INP_MASK1 0x07
+#define WSA_MACRO_MUX_INP_MASK2 0x38
+#define WSA_MACRO_MUX_CFG_OFFSET 0x8
+#define WSA_MACRO_MUX_CFG1_OFFSET 0x4
+#define WSA_MACRO_RX_COMP_OFFSET 0x40
+#define WSA_MACRO_RX_SOFTCLIP_OFFSET 0x40
+#define WSA_MACRO_RX_PATH_OFFSET 0x80
+#define WSA_MACRO_RX_PATH_CFG3_OFFSET 0x10
+#define WSA_MACRO_RX_PATH_DSMDEM_OFFSET 0x4C
+#define WSA_MACRO_FS_RATE_MASK 0x0F
+#define WSA_MACRO_EC_MIX_TX0_MASK 0x03
+#define WSA_MACRO_EC_MIX_TX1_MASK 0x18
+#define WSA_MACRO_MAX_DMA_CH_PER_PORT 0x2
+
+enum {
+ WSA_MACRO_GAIN_OFFSET_M1P5_DB,
+ WSA_MACRO_GAIN_OFFSET_0_DB,
+};
+enum {
+ WSA_MACRO_RX0 = 0,
+ WSA_MACRO_RX1,
+ WSA_MACRO_RX_MIX,
+ WSA_MACRO_RX_MIX0 = WSA_MACRO_RX_MIX,
+ WSA_MACRO_RX_MIX1,
+ WSA_MACRO_RX_MAX,
+};
+
+enum {
+ WSA_MACRO_TX0 = 0,
+ WSA_MACRO_TX1,
+ WSA_MACRO_TX_MAX,
+};
+
+enum {
+ WSA_MACRO_EC0_MUX = 0,
+ WSA_MACRO_EC1_MUX,
+ WSA_MACRO_EC_MUX_MAX,
+};
+
+enum {
+ WSA_MACRO_COMP1, /* SPK_L */
+ WSA_MACRO_COMP2, /* SPK_R */
+ WSA_MACRO_COMP_MAX
+};
+
+enum {
+ WSA_MACRO_SOFTCLIP0, /* RX0 */
+ WSA_MACRO_SOFTCLIP1, /* RX1 */
+ WSA_MACRO_SOFTCLIP_MAX
+};
+
+enum {
+ INTn_1_INP_SEL_ZERO = 0,
+ INTn_1_INP_SEL_RX0,
+ INTn_1_INP_SEL_RX1,
+ INTn_1_INP_SEL_RX2,
+ INTn_1_INP_SEL_RX3,
+ INTn_1_INP_SEL_DEC0,
+ INTn_1_INP_SEL_DEC1,
+};
+
+enum {
+ INTn_2_INP_SEL_ZERO = 0,
+ INTn_2_INP_SEL_RX0,
+ INTn_2_INP_SEL_RX1,
+ INTn_2_INP_SEL_RX2,
+ INTn_2_INP_SEL_RX3,
+};
+
+struct interp_sample_rate {
+ int sample_rate;
+ int rate_val;
+};
+
+static struct interp_sample_rate int_prim_sample_rate_val[] = {
+ {8000, 0x0}, /* 8K */
+ {16000, 0x1}, /* 16K */
+ {24000, -EINVAL},/* 24K */
+ {32000, 0x3}, /* 32K */
+ {48000, 0x4}, /* 48K */
+ {96000, 0x5}, /* 96K */
+ {192000, 0x6}, /* 192K */
+ {384000, 0x7}, /* 384K */
+ {44100, 0x8}, /* 44.1K */
+};
+
+static struct interp_sample_rate int_mix_sample_rate_val[] = {
+ {48000, 0x4}, /* 48K */
+ {96000, 0x5}, /* 96K */
+ {192000, 0x6}, /* 192K */
+};
+
+enum {
+ WSA_MACRO_AIF_INVALID = 0,
+ WSA_MACRO_AIF1_PB,
+ WSA_MACRO_AIF_MIX1_PB,
+ WSA_MACRO_AIF_VI,
+ WSA_MACRO_AIF_ECHO,
+ WSA_MACRO_MAX_DAIS,
+};
+
+struct wsa_macro {
+ struct device *dev;
+ int comp_enabled[WSA_MACRO_COMP_MAX];
+ int ec_hq[WSA_MACRO_RX1 + 1];
+ u16 prim_int_users[WSA_MACRO_RX1 + 1];
+ u16 wsa_mclk_users;
+ bool reset_swr;
+ unsigned long active_ch_mask[WSA_MACRO_MAX_DAIS];
+ unsigned long active_ch_cnt[WSA_MACRO_MAX_DAIS];
+ int rx_port_value[WSA_MACRO_RX_MAX];
+ int ear_spkr_gain;
+ int spkr_gain_offset;
+ int spkr_mode;
+ int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX];
+ int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX];
+ struct regmap *regmap;
+ struct clk_bulk_data clks[WSA_NUM_CLKS_MAX];
+ struct clk_hw hw;
+};
+#define to_wsa_macro(_hw) container_of(_hw, struct wsa_macro, hw)
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+static const char *const rx_text[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "DEC0", "DEC1"
+};
+
+static const char *const rx_mix_text[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1"
+};
+
+static const char *const rx_mix_ec_text[] = {
+ "ZERO", "RX_MIX_TX0", "RX_MIX_TX1"
+};
+
+static const char *const rx_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF_MIX1_PB"
+};
+
+static const char *const rx_sidetone_mix_text[] = {
+ "ZERO", "SRC0"
+};
+
+static const char * const wsa_macro_ear_spkr_pa_gain_text[] = {
+ "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB",
+ "G_4_DB", "G_5_DB", "G_6_DB"
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_ear_spkr_pa_gain_enum,
+ wsa_macro_ear_spkr_pa_gain_text);
+
+/* RX INT0 */
+static const struct soc_enum rx0_prim_inp0_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 0, 7, rx_text);
+
+static const struct soc_enum rx0_prim_inp1_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 3, 7, rx_text);
+
+static const struct soc_enum rx0_prim_inp2_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 3, 7, rx_text);
+
+static const struct soc_enum rx0_mix_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 0, 5, rx_mix_text);
+
+static const struct soc_enum rx0_sidetone_mix_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_sidetone_mix_text);
+
+static const struct snd_kcontrol_new rx0_prim_inp0_mux =
+ SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx0_prim_inp1_mux =
+ SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx0_prim_inp2_mux =
+ SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx0_mix_mux =
+ SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum);
+
+static const struct snd_kcontrol_new rx0_sidetone_mix_mux =
+ SOC_DAPM_ENUM("WSA_RX0 SIDETONE MIX Mux", rx0_sidetone_mix_enum);
+
+/* RX INT1 */
+static const struct soc_enum rx1_prim_inp0_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 0, 7, rx_text);
+
+static const struct soc_enum rx1_prim_inp1_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 3, 7, rx_text);
+
+static const struct soc_enum rx1_prim_inp2_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 3, 7, rx_text);
+
+static const struct soc_enum rx1_mix_chain_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 0, 5, rx_mix_text);
+
+static const struct snd_kcontrol_new rx1_prim_inp0_mux =
+ SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx1_prim_inp1_mux =
+ SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx1_prim_inp2_mux =
+ SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx1_mix_mux =
+ SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum);
+
+static const struct soc_enum rx_mix_ec0_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0,
+ 0, 3, rx_mix_ec_text);
+
+static const struct soc_enum rx_mix_ec1_enum =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0,
+ 3, 3, rx_mix_ec_text);
+
+static const struct snd_kcontrol_new rx_mix_ec0_mux =
+ SOC_DAPM_ENUM("WSA RX_MIX EC0_Mux", rx_mix_ec0_enum);
+
+static const struct snd_kcontrol_new rx_mix_ec1_mux =
+ SOC_DAPM_ENUM("WSA RX_MIX EC1_Mux", rx_mix_ec1_enum);
+
+static const struct reg_default wsa_defaults[] = {
+ /* WSA Macro */
+ { CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, 0x00},
+ { CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00},
+ { CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, 0x00},
+ { CDC_WSA_TOP_TOP_CFG0, 0x00},
+ { CDC_WSA_TOP_TOP_CFG1, 0x00},
+ { CDC_WSA_TOP_FREQ_MCLK, 0x00},
+ { CDC_WSA_TOP_DEBUG_BUS_SEL, 0x00},
+ { CDC_WSA_TOP_DEBUG_EN0, 0x00},
+ { CDC_WSA_TOP_DEBUG_EN1, 0x00},
+ { CDC_WSA_TOP_DEBUG_DSM_LB, 0x88},
+ { CDC_WSA_TOP_RX_I2S_CTL, 0x0C},
+ { CDC_WSA_TOP_TX_I2S_CTL, 0x0C},
+ { CDC_WSA_TOP_I2S_CLK, 0x02},
+ { CDC_WSA_TOP_I2S_RESET, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, 0x00},
+ { CDC_WSA_RX_INP_MUX_RX_EC_CFG0, 0x00},
+ { CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, 0x00},
+ { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x02},
+ { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x00},
+ { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x02},
+ { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x00},
+ { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x02},
+ { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x00},
+ { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x02},
+ { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x00},
+ { CDC_WSA_INTR_CTRL_CFG, 0x00},
+ { CDC_WSA_INTR_CTRL_CLR_COMMIT, 0x00},
+ { CDC_WSA_INTR_CTRL_PIN1_MASK0, 0xFF},
+ { CDC_WSA_INTR_CTRL_PIN1_STATUS0, 0x00},
+ { CDC_WSA_INTR_CTRL_PIN1_CLEAR0, 0x00},
+ { CDC_WSA_INTR_CTRL_PIN2_MASK0, 0xFF},
+ { CDC_WSA_INTR_CTRL_PIN2_STATUS0, 0x00},
+ { CDC_WSA_INTR_CTRL_PIN2_CLEAR0, 0x00},
+ { CDC_WSA_INTR_CTRL_LEVEL0, 0x00},
+ { CDC_WSA_INTR_CTRL_BYPASS0, 0x00},
+ { CDC_WSA_INTR_CTRL_SET0, 0x00},
+ { CDC_WSA_RX0_RX_PATH_CTL, 0x04},
+ { CDC_WSA_RX0_RX_PATH_CFG0, 0x00},
+ { CDC_WSA_RX0_RX_PATH_CFG1, 0x64},
+ { CDC_WSA_RX0_RX_PATH_CFG2, 0x8F},
+ { CDC_WSA_RX0_RX_PATH_CFG3, 0x00},
+ { CDC_WSA_RX0_RX_VOL_CTL, 0x00},
+ { CDC_WSA_RX0_RX_PATH_MIX_CTL, 0x04},
+ { CDC_WSA_RX0_RX_PATH_MIX_CFG, 0x7E},
+ { CDC_WSA_RX0_RX_VOL_MIX_CTL, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC0, 0x04},
+ { CDC_WSA_RX0_RX_PATH_SEC1, 0x08},
+ { CDC_WSA_RX0_RX_PATH_SEC2, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC3, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC5, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC6, 0x00},
+ { CDC_WSA_RX0_RX_PATH_SEC7, 0x00},
+ { CDC_WSA_RX0_RX_PATH_MIX_SEC0, 0x08},
+ { CDC_WSA_RX0_RX_PATH_MIX_SEC1, 0x00},
+ { CDC_WSA_RX0_RX_PATH_DSMDEM_CTL, 0x00},
+ { CDC_WSA_RX1_RX_PATH_CFG0, 0x00},
+ { CDC_WSA_RX1_RX_PATH_CFG1, 0x64},
+ { CDC_WSA_RX1_RX_PATH_CFG2, 0x8F},
+ { CDC_WSA_RX1_RX_PATH_CFG3, 0x00},
+ { CDC_WSA_RX1_RX_VOL_CTL, 0x00},
+ { CDC_WSA_RX1_RX_PATH_MIX_CTL, 0x04},
+ { CDC_WSA_RX1_RX_PATH_MIX_CFG, 0x7E},
+ { CDC_WSA_RX1_RX_VOL_MIX_CTL, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC0, 0x04},
+ { CDC_WSA_RX1_RX_PATH_SEC1, 0x08},
+ { CDC_WSA_RX1_RX_PATH_SEC2, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC3, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC5, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC6, 0x00},
+ { CDC_WSA_RX1_RX_PATH_SEC7, 0x00},
+ { CDC_WSA_RX1_RX_PATH_MIX_SEC0, 0x08},
+ { CDC_WSA_RX1_RX_PATH_MIX_SEC1, 0x00},
+ { CDC_WSA_RX1_RX_PATH_DSMDEM_CTL, 0x00},
+ { CDC_WSA_BOOST0_BOOST_PATH_CTL, 0x00},
+ { CDC_WSA_BOOST0_BOOST_CTL, 0xD0},
+ { CDC_WSA_BOOST0_BOOST_CFG1, 0x89},
+ { CDC_WSA_BOOST0_BOOST_CFG2, 0x04},
+ { CDC_WSA_BOOST1_BOOST_PATH_CTL, 0x00},
+ { CDC_WSA_BOOST1_BOOST_CTL, 0xD0},
+ { CDC_WSA_BOOST1_BOOST_CFG1, 0x89},
+ { CDC_WSA_BOOST1_BOOST_CFG2, 0x04},
+ { CDC_WSA_COMPANDER0_CTL0, 0x60},
+ { CDC_WSA_COMPANDER0_CTL1, 0xDB},
+ { CDC_WSA_COMPANDER0_CTL2, 0xFF},
+ { CDC_WSA_COMPANDER0_CTL3, 0x35},
+ { CDC_WSA_COMPANDER0_CTL4, 0xFF},
+ { CDC_WSA_COMPANDER0_CTL5, 0x00},
+ { CDC_WSA_COMPANDER0_CTL6, 0x01},
+ { CDC_WSA_COMPANDER0_CTL7, 0x28},
+ { CDC_WSA_COMPANDER1_CTL0, 0x60},
+ { CDC_WSA_COMPANDER1_CTL1, 0xDB},
+ { CDC_WSA_COMPANDER1_CTL2, 0xFF},
+ { CDC_WSA_COMPANDER1_CTL3, 0x35},
+ { CDC_WSA_COMPANDER1_CTL4, 0xFF},
+ { CDC_WSA_COMPANDER1_CTL5, 0x00},
+ { CDC_WSA_COMPANDER1_CTL6, 0x01},
+ { CDC_WSA_COMPANDER1_CTL7, 0x28},
+ { CDC_WSA_SOFTCLIP0_CRC, 0x00},
+ { CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38},
+ { CDC_WSA_SOFTCLIP1_CRC, 0x00},
+ { CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38},
+ { CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL, 0x00},
+ { CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0, 0x01},
+ { CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL, 0x00},
+ { CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0, 0x01},
+ { CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_CTL0, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_CTL1, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_FIFO_CTL, 0xA8},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC0_STATUS_FIFO, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_CTL0, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_CTL1, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_FIFO_CTL, 0xA8},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00},
+ { CDC_WSA_SPLINE_ASRC1_STATUS_FIFO, 0x00},
+};
+
+static bool wsa_is_wronly_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_INTR_CTRL_CLR_COMMIT:
+ case CDC_WSA_INTR_CTRL_PIN1_CLEAR0:
+ case CDC_WSA_INTR_CTRL_PIN2_CLEAR0:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL:
+ case CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL:
+ case CDC_WSA_CLK_RST_CTRL_SWR_CONTROL:
+ case CDC_WSA_TOP_TOP_CFG0:
+ case CDC_WSA_TOP_TOP_CFG1:
+ case CDC_WSA_TOP_FREQ_MCLK:
+ case CDC_WSA_TOP_DEBUG_BUS_SEL:
+ case CDC_WSA_TOP_DEBUG_EN0:
+ case CDC_WSA_TOP_DEBUG_EN1:
+ case CDC_WSA_TOP_DEBUG_DSM_LB:
+ case CDC_WSA_TOP_RX_I2S_CTL:
+ case CDC_WSA_TOP_TX_I2S_CTL:
+ case CDC_WSA_TOP_I2S_CLK:
+ case CDC_WSA_TOP_I2S_RESET:
+ case CDC_WSA_RX_INP_MUX_RX_INT0_CFG0:
+ case CDC_WSA_RX_INP_MUX_RX_INT0_CFG1:
+ case CDC_WSA_RX_INP_MUX_RX_INT1_CFG0:
+ case CDC_WSA_RX_INP_MUX_RX_INT1_CFG1:
+ case CDC_WSA_RX_INP_MUX_RX_MIX_CFG0:
+ case CDC_WSA_RX_INP_MUX_RX_EC_CFG0:
+ case CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0:
+ case CDC_WSA_TX0_SPKR_PROT_PATH_CTL:
+ case CDC_WSA_TX0_SPKR_PROT_PATH_CFG0:
+ case CDC_WSA_TX1_SPKR_PROT_PATH_CTL:
+ case CDC_WSA_TX1_SPKR_PROT_PATH_CFG0:
+ case CDC_WSA_TX2_SPKR_PROT_PATH_CTL:
+ case CDC_WSA_TX2_SPKR_PROT_PATH_CFG0:
+ case CDC_WSA_TX3_SPKR_PROT_PATH_CTL:
+ case CDC_WSA_TX3_SPKR_PROT_PATH_CFG0:
+ case CDC_WSA_INTR_CTRL_CFG:
+ case CDC_WSA_INTR_CTRL_PIN1_MASK0:
+ case CDC_WSA_INTR_CTRL_PIN2_MASK0:
+ case CDC_WSA_INTR_CTRL_LEVEL0:
+ case CDC_WSA_INTR_CTRL_BYPASS0:
+ case CDC_WSA_INTR_CTRL_SET0:
+ case CDC_WSA_RX0_RX_PATH_CTL:
+ case CDC_WSA_RX0_RX_PATH_CFG0:
+ case CDC_WSA_RX0_RX_PATH_CFG1:
+ case CDC_WSA_RX0_RX_PATH_CFG2:
+ case CDC_WSA_RX0_RX_PATH_CFG3:
+ case CDC_WSA_RX0_RX_VOL_CTL:
+ case CDC_WSA_RX0_RX_PATH_MIX_CTL:
+ case CDC_WSA_RX0_RX_PATH_MIX_CFG:
+ case CDC_WSA_RX0_RX_VOL_MIX_CTL:
+ case CDC_WSA_RX0_RX_PATH_SEC0:
+ case CDC_WSA_RX0_RX_PATH_SEC1:
+ case CDC_WSA_RX0_RX_PATH_SEC2:
+ case CDC_WSA_RX0_RX_PATH_SEC3:
+ case CDC_WSA_RX0_RX_PATH_SEC5:
+ case CDC_WSA_RX0_RX_PATH_SEC6:
+ case CDC_WSA_RX0_RX_PATH_SEC7:
+ case CDC_WSA_RX0_RX_PATH_MIX_SEC0:
+ case CDC_WSA_RX0_RX_PATH_MIX_SEC1:
+ case CDC_WSA_RX0_RX_PATH_DSMDEM_CTL:
+ case CDC_WSA_RX1_RX_PATH_CTL:
+ case CDC_WSA_RX1_RX_PATH_CFG0:
+ case CDC_WSA_RX1_RX_PATH_CFG1:
+ case CDC_WSA_RX1_RX_PATH_CFG2:
+ case CDC_WSA_RX1_RX_PATH_CFG3:
+ case CDC_WSA_RX1_RX_VOL_CTL:
+ case CDC_WSA_RX1_RX_PATH_MIX_CTL:
+ case CDC_WSA_RX1_RX_PATH_MIX_CFG:
+ case CDC_WSA_RX1_RX_VOL_MIX_CTL:
+ case CDC_WSA_RX1_RX_PATH_SEC0:
+ case CDC_WSA_RX1_RX_PATH_SEC1:
+ case CDC_WSA_RX1_RX_PATH_SEC2:
+ case CDC_WSA_RX1_RX_PATH_SEC3:
+ case CDC_WSA_RX1_RX_PATH_SEC5:
+ case CDC_WSA_RX1_RX_PATH_SEC6:
+ case CDC_WSA_RX1_RX_PATH_SEC7:
+ case CDC_WSA_RX1_RX_PATH_MIX_SEC0:
+ case CDC_WSA_RX1_RX_PATH_MIX_SEC1:
+ case CDC_WSA_RX1_RX_PATH_DSMDEM_CTL:
+ case CDC_WSA_BOOST0_BOOST_PATH_CTL:
+ case CDC_WSA_BOOST0_BOOST_CTL:
+ case CDC_WSA_BOOST0_BOOST_CFG1:
+ case CDC_WSA_BOOST0_BOOST_CFG2:
+ case CDC_WSA_BOOST1_BOOST_PATH_CTL:
+ case CDC_WSA_BOOST1_BOOST_CTL:
+ case CDC_WSA_BOOST1_BOOST_CFG1:
+ case CDC_WSA_BOOST1_BOOST_CFG2:
+ case CDC_WSA_COMPANDER0_CTL0:
+ case CDC_WSA_COMPANDER0_CTL1:
+ case CDC_WSA_COMPANDER0_CTL2:
+ case CDC_WSA_COMPANDER0_CTL3:
+ case CDC_WSA_COMPANDER0_CTL4:
+ case CDC_WSA_COMPANDER0_CTL5:
+ case CDC_WSA_COMPANDER0_CTL7:
+ case CDC_WSA_COMPANDER1_CTL0:
+ case CDC_WSA_COMPANDER1_CTL1:
+ case CDC_WSA_COMPANDER1_CTL2:
+ case CDC_WSA_COMPANDER1_CTL3:
+ case CDC_WSA_COMPANDER1_CTL4:
+ case CDC_WSA_COMPANDER1_CTL5:
+ case CDC_WSA_COMPANDER1_CTL7:
+ case CDC_WSA_SOFTCLIP0_CRC:
+ case CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL:
+ case CDC_WSA_SOFTCLIP1_CRC:
+ case CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL:
+ case CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL:
+ case CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0:
+ case CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL:
+ case CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0:
+ case CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL:
+ case CDC_WSA_SPLINE_ASRC0_CTL0:
+ case CDC_WSA_SPLINE_ASRC0_CTL1:
+ case CDC_WSA_SPLINE_ASRC0_FIFO_CTL:
+ case CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL:
+ case CDC_WSA_SPLINE_ASRC1_CTL0:
+ case CDC_WSA_SPLINE_ASRC1_CTL1:
+ case CDC_WSA_SPLINE_ASRC1_FIFO_CTL:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_writeable_register(struct device *dev, unsigned int reg)
+{
+ bool ret;
+
+ ret = wsa_is_rw_register(dev, reg);
+ if (!ret)
+ return wsa_is_wronly_register(dev, reg);
+
+ return ret;
+}
+
+static bool wsa_is_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_INTR_CTRL_CLR_COMMIT:
+ case CDC_WSA_INTR_CTRL_PIN1_CLEAR0:
+ case CDC_WSA_INTR_CTRL_PIN2_CLEAR0:
+ case CDC_WSA_INTR_CTRL_PIN1_STATUS0:
+ case CDC_WSA_INTR_CTRL_PIN2_STATUS0:
+ case CDC_WSA_COMPANDER0_CTL6:
+ case CDC_WSA_COMPANDER1_CTL6:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FIFO:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FIFO:
+ return true;
+ }
+
+ return wsa_is_rw_register(dev, reg);
+}
+
+static bool wsa_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ /* Update volatile list for rx/tx macros */
+ switch (reg) {
+ case CDC_WSA_INTR_CTRL_PIN1_STATUS0:
+ case CDC_WSA_INTR_CTRL_PIN2_STATUS0:
+ case CDC_WSA_COMPANDER0_CTL6:
+ case CDC_WSA_COMPANDER1_CTL6:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC0_STATUS_FIFO:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB:
+ case CDC_WSA_SPLINE_ASRC1_STATUS_FIFO:
+ return true;
+ }
+ return false;
+}
+
+const struct regmap_config wsa_regmap_config = {
+ .name = "wsa_macro",
+ .reg_bits = 16,
+ .val_bits = 32, /* 8 but with 32 bit read/write */
+ .reg_stride = 4,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = wsa_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wsa_defaults),
+ .max_register = WSA_MAX_OFFSET,
+ .writeable_reg = wsa_is_writeable_register,
+ .volatile_reg = wsa_is_volatile_register,
+ .readable_reg = wsa_is_readable_register,
+};
+
+/**
+ * wsa_macro_set_spkr_mode - Configures speaker compander and smartboost
+ * settings based on speaker mode.
+ *
+ * @component: codec instance
+ * @mode: Indicates speaker configuration mode.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode)
+{
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa->spkr_mode = mode;
+
+ switch (mode) {
+ case WSA_MACRO_SPKR_MODE_1:
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x44);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x44);
+ break;
+ default:
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x80);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x80);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x01);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x01);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x58);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x58);
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(wsa_macro_set_spkr_mode);
+
+static int wsa_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai,
+ u8 int_prim_fs_rate_reg_val,
+ u32 sample_rate)
+{
+ u8 int_1_mix1_inp;
+ u32 j, port = 0;
+ u16 int_mux_cfg0, int_mux_cfg1;
+ u16 int_fs_reg;
+ u8 int_mux_cfg0_val, int_mux_cfg1_val;
+ u8 inp0_sel, inp1_sel, inp2_sel;
+ struct snd_soc_component *component = dai->component;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ for_each_set_bit(port, &wsa->active_ch_mask[dai->id], WSA_MACRO_RX_MAX) {
+ int_1_mix1_inp = port;
+ if ((int_1_mix1_inp < WSA_MACRO_RX0) || (int_1_mix1_inp > WSA_MACRO_RX_MIX1)) {
+ dev_err(component->dev, "%s: Invalid RX port, Dai ID is %d\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ int_mux_cfg0 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG0;
+
+ /*
+ * Loop through all interpolator MUX inputs and find out
+ * to which interpolator input, the cdc_dma rx port
+ * is connected
+ */
+ for (j = 0; j < NUM_INTERPOLATORS; j++) {
+ int_mux_cfg1 = int_mux_cfg0 + WSA_MACRO_MUX_CFG1_OFFSET;
+
+ int_mux_cfg0_val = snd_soc_component_read(component,
+ int_mux_cfg0);
+ int_mux_cfg1_val = snd_soc_component_read(component,
+ int_mux_cfg1);
+ inp0_sel = int_mux_cfg0_val & WSA_MACRO_MUX_INP_MASK1;
+ inp1_sel = (int_mux_cfg0_val >>
+ WSA_MACRO_MUX_INP_SHFT) &
+ WSA_MACRO_MUX_INP_MASK1;
+ inp2_sel = (int_mux_cfg1_val >>
+ WSA_MACRO_MUX_INP_SHFT) &
+ WSA_MACRO_MUX_INP_MASK1;
+ if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) ||
+ (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) ||
+ (inp2_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0)) {
+ int_fs_reg = CDC_WSA_RX0_RX_PATH_CTL +
+ WSA_MACRO_RX_PATH_OFFSET * j;
+ /* sample_rate is in Hz */
+ snd_soc_component_update_bits(component,
+ int_fs_reg,
+ WSA_MACRO_FS_RATE_MASK,
+ int_prim_fs_rate_reg_val);
+ }
+ int_mux_cfg0 += WSA_MACRO_MUX_CFG_OFFSET;
+ }
+ }
+
+ return 0;
+}
+
+static int wsa_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai,
+ u8 int_mix_fs_rate_reg_val,
+ u32 sample_rate)
+{
+ u8 int_2_inp;
+ u32 j, port = 0;
+ u16 int_mux_cfg1, int_fs_reg;
+ u8 int_mux_cfg1_val;
+ struct snd_soc_component *component = dai->component;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ for_each_set_bit(port, &wsa->active_ch_mask[dai->id], WSA_MACRO_RX_MAX) {
+ int_2_inp = port;
+ if ((int_2_inp < WSA_MACRO_RX0) || (int_2_inp > WSA_MACRO_RX_MIX1)) {
+ dev_err(component->dev, "%s: Invalid RX port, Dai ID is %d\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ int_mux_cfg1 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG1;
+ for (j = 0; j < NUM_INTERPOLATORS; j++) {
+ int_mux_cfg1_val = snd_soc_component_read(component,
+ int_mux_cfg1) &
+ WSA_MACRO_MUX_INP_MASK1;
+ if (int_mux_cfg1_val == int_2_inp + INTn_2_INP_SEL_RX0) {
+ int_fs_reg = CDC_WSA_RX0_RX_PATH_MIX_CTL +
+ WSA_MACRO_RX_PATH_OFFSET * j;
+
+ snd_soc_component_update_bits(component,
+ int_fs_reg,
+ WSA_MACRO_FS_RATE_MASK,
+ int_mix_fs_rate_reg_val);
+ }
+ int_mux_cfg1 += WSA_MACRO_MUX_CFG_OFFSET;
+ }
+ }
+ return 0;
+}
+
+static int wsa_macro_set_interpolator_rate(struct snd_soc_dai *dai,
+ u32 sample_rate)
+{
+ int rate_val = 0;
+ int i, ret;
+
+ /* set mixing path rate */
+ for (i = 0; i < ARRAY_SIZE(int_mix_sample_rate_val); i++) {
+ if (sample_rate ==
+ int_mix_sample_rate_val[i].sample_rate) {
+ rate_val =
+ int_mix_sample_rate_val[i].rate_val;
+ break;
+ }
+ }
+ if ((i == ARRAY_SIZE(int_mix_sample_rate_val)) ||
+ (rate_val < 0))
+ goto prim_rate;
+ ret = wsa_macro_set_mix_interpolator_rate(dai,
+ (u8) rate_val, sample_rate);
+prim_rate:
+ /* set primary path sample rate */
+ for (i = 0; i < ARRAY_SIZE(int_prim_sample_rate_val); i++) {
+ if (sample_rate ==
+ int_prim_sample_rate_val[i].sample_rate) {
+ rate_val =
+ int_prim_sample_rate_val[i].rate_val;
+ break;
+ }
+ }
+ if ((i == ARRAY_SIZE(int_prim_sample_rate_val)) ||
+ (rate_val < 0))
+ return -EINVAL;
+ ret = wsa_macro_set_prim_interpolator_rate(dai,
+ (u8) rate_val, sample_rate);
+ return ret;
+}
+
+static int wsa_macro_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ int ret;
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = wsa_macro_set_interpolator_rate(dai, params_rate(params));
+ if (ret) {
+ dev_err(component->dev,
+ "%s: cannot set sample rate: %u\n",
+ __func__, params_rate(params));
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wsa_macro_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u16 val, mask = 0, cnt = 0, temp;
+
+ switch (dai->id) {
+ case WSA_MACRO_AIF_VI:
+ *tx_slot = wsa->active_ch_mask[dai->id];
+ *tx_num = wsa->active_ch_cnt[dai->id];
+ break;
+ case WSA_MACRO_AIF1_PB:
+ case WSA_MACRO_AIF_MIX1_PB:
+ for_each_set_bit(temp, &wsa->active_ch_mask[dai->id],
+ WSA_MACRO_RX_MAX) {
+ mask |= (1 << temp);
+ if (++cnt == WSA_MACRO_MAX_DMA_CH_PER_PORT)
+ break;
+ }
+ if (mask & 0x0C)
+ mask = mask >> 0x2;
+ *rx_slot = mask;
+ *rx_num = cnt;
+ break;
+ case WSA_MACRO_AIF_ECHO:
+ val = snd_soc_component_read(component,
+ CDC_WSA_RX_INP_MUX_RX_MIX_CFG0);
+ if (val & WSA_MACRO_EC_MIX_TX1_MASK) {
+ mask |= 0x2;
+ cnt++;
+ }
+ if (val & WSA_MACRO_EC_MIX_TX0_MASK) {
+ mask |= 0x1;
+ cnt++;
+ }
+ *tx_slot = mask;
+ *tx_num = cnt;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid AIF\n", __func__);
+ break;
+ }
+ return 0;
+}
+
+static struct snd_soc_dai_ops wsa_macro_dai_ops = {
+ .hw_params = wsa_macro_hw_params,
+ .get_channel_map = wsa_macro_get_channel_map,
+};
+
+static struct snd_soc_dai_driver wsa_macro_dai[] = {
+ {
+ .name = "wsa_macro_rx1",
+ .id = WSA_MACRO_AIF1_PB,
+ .playback = {
+ .stream_name = "WSA_AIF1 Playback",
+ .rates = WSA_MACRO_RX_RATES,
+ .formats = WSA_MACRO_RX_FORMATS,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wsa_macro_dai_ops,
+ },
+ {
+ .name = "wsa_macro_rx_mix",
+ .id = WSA_MACRO_AIF_MIX1_PB,
+ .playback = {
+ .stream_name = "WSA_AIF_MIX1 Playback",
+ .rates = WSA_MACRO_RX_MIX_RATES,
+ .formats = WSA_MACRO_RX_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wsa_macro_dai_ops,
+ },
+ {
+ .name = "wsa_macro_vifeedback",
+ .id = WSA_MACRO_AIF_VI,
+ .capture = {
+ .stream_name = "WSA_AIF_VI Capture",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+ .formats = WSA_MACRO_RX_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &wsa_macro_dai_ops,
+ },
+ {
+ .name = "wsa_macro_echo",
+ .id = WSA_MACRO_AIF_ECHO,
+ .capture = {
+ .stream_name = "WSA_AIF_ECHO Capture",
+ .rates = WSA_MACRO_ECHO_RATES,
+ .formats = WSA_MACRO_ECHO_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wsa_macro_dai_ops,
+ },
+};
+
+static void wsa_macro_mclk_enable(struct wsa_macro *wsa, bool mclk_enable)
+{
+ struct regmap *regmap = wsa->regmap;
+
+ if (mclk_enable) {
+ if (wsa->wsa_mclk_users == 0) {
+ regcache_mark_dirty(regmap);
+ regcache_sync_region(regmap, 0x0, WSA_MAX_OFFSET);
+ /* 9.6MHz MCLK, set value 0x00 if other frequency */
+ regmap_update_bits(regmap, CDC_WSA_TOP_FREQ_MCLK, 0x01, 0x01);
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_WSA_MCLK_EN_MASK,
+ CDC_WSA_MCLK_ENABLE);
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_WSA_FS_CNT_EN_MASK,
+ CDC_WSA_FS_CNT_ENABLE);
+ }
+ wsa->wsa_mclk_users++;
+ } else {
+ if (wsa->wsa_mclk_users <= 0) {
+ dev_err(wsa->dev, "clock already disabled\n");
+ wsa->wsa_mclk_users = 0;
+ return;
+ }
+ wsa->wsa_mclk_users--;
+ if (wsa->wsa_mclk_users == 0) {
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL,
+ CDC_WSA_FS_CNT_EN_MASK,
+ CDC_WSA_FS_CNT_DISABLE);
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL,
+ CDC_WSA_MCLK_EN_MASK,
+ CDC_WSA_MCLK_DISABLE);
+ }
+ }
+}
+
+static int wsa_macro_mclk_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 wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa_macro_mclk_enable(wsa, event == SND_SOC_DAPM_PRE_PMU);
+ return 0;
+}
+
+static int wsa_macro_enable_vi_feedback(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 wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u32 tx_reg0, tx_reg1;
+
+ if (test_bit(WSA_MACRO_TX0, &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) {
+ tx_reg0 = CDC_WSA_TX0_SPKR_PROT_PATH_CTL;
+ tx_reg1 = CDC_WSA_TX1_SPKR_PROT_PATH_CTL;
+ } else if (test_bit(WSA_MACRO_TX1, &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) {
+ tx_reg0 = CDC_WSA_TX2_SPKR_PROT_PATH_CTL;
+ tx_reg1 = CDC_WSA_TX3_SPKR_PROT_PATH_CTL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Enable V&I sensing */
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_RESET);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_RESET);
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK,
+ CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK,
+ CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K);
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK,
+ CDC_WSA_TX_SPKR_PROT_CLK_ENABLE);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK,
+ CDC_WSA_TX_SPKR_PROT_CLK_ENABLE);
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_NO_RESET);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_NO_RESET);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable V&I sensing */
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_RESET);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_RESET_MASK,
+ CDC_WSA_TX_SPKR_PROT_RESET);
+ snd_soc_component_update_bits(component, tx_reg0,
+ CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK,
+ CDC_WSA_TX_SPKR_PROT_CLK_DISABLE);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK,
+ CDC_WSA_TX_SPKR_PROT_CLK_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_enable_mix_path(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 gain_reg;
+ int offset_val = 0;
+ int val = 0;
+
+ switch (w->reg) {
+ case CDC_WSA_RX0_RX_PATH_MIX_CTL:
+ gain_reg = CDC_WSA_RX0_RX_VOL_MIX_CTL;
+ break;
+ case CDC_WSA_RX1_RX_PATH_MIX_CTL:
+ gain_reg = CDC_WSA_RX1_RX_VOL_MIX_CTL;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = snd_soc_component_read(component, gain_reg);
+ val += offset_val;
+ snd_soc_component_write(component, gain_reg, val);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, w->reg,
+ CDC_WSA_RX_PATH_MIX_CLK_EN_MASK,
+ CDC_WSA_RX_PATH_MIX_CLK_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static void wsa_macro_hd2_control(struct snd_soc_component *component,
+ u16 reg, int event)
+{
+ u16 hd2_scale_reg;
+ u16 hd2_enable_reg = 0;
+
+ if (reg == CDC_WSA_RX0_RX_PATH_CTL) {
+ hd2_scale_reg = CDC_WSA_RX0_RX_PATH_SEC3;
+ hd2_enable_reg = CDC_WSA_RX0_RX_PATH_CFG0;
+ }
+ if (reg == CDC_WSA_RX1_RX_PATH_CTL) {
+ hd2_scale_reg = CDC_WSA_RX1_RX_PATH_SEC3;
+ hd2_enable_reg = CDC_WSA_RX1_RX_PATH_CFG0;
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_WSA_RX_PATH_HD2_ALPHA_MASK,
+ 0x10);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_WSA_RX_PATH_HD2_SCALE_MASK,
+ 0x1);
+ snd_soc_component_update_bits(component, hd2_enable_reg,
+ CDC_WSA_RX_PATH_HD2_EN_MASK,
+ CDC_WSA_RX_PATH_HD2_ENABLE);
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, hd2_enable_reg,
+ CDC_WSA_RX_PATH_HD2_EN_MASK, 0);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_WSA_RX_PATH_HD2_SCALE_MASK,
+ 0);
+ snd_soc_component_update_bits(component, hd2_scale_reg,
+ CDC_WSA_RX_PATH_HD2_ALPHA_MASK,
+ 0);
+ }
+}
+
+static int wsa_macro_config_compander(struct snd_soc_component *component,
+ int comp, int event)
+{
+ u16 comp_ctl0_reg, rx_path_cfg0_reg;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ if (!wsa->comp_enabled[comp])
+ return 0;
+
+ comp_ctl0_reg = CDC_WSA_COMPANDER0_CTL0 +
+ (comp * WSA_MACRO_RX_COMP_OFFSET);
+ rx_path_cfg0_reg = CDC_WSA_RX0_RX_PATH_CFG0 +
+ (comp * WSA_MACRO_RX_PATH_OFFSET);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Compander Clock */
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_CLK_EN_MASK,
+ CDC_WSA_COMPANDER_CLK_ENABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_SOFT_RST_MASK,
+ CDC_WSA_COMPANDER_SOFT_RST_ENABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_SOFT_RST_MASK,
+ 0);
+ snd_soc_component_update_bits(component, rx_path_cfg0_reg,
+ CDC_WSA_RX_PATH_COMP_EN_MASK,
+ CDC_WSA_RX_PATH_COMP_ENABLE);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_HALT_MASK,
+ CDC_WSA_COMPANDER_HALT);
+ snd_soc_component_update_bits(component, rx_path_cfg0_reg,
+ CDC_WSA_RX_PATH_COMP_EN_MASK, 0);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_SOFT_RST_MASK,
+ CDC_WSA_COMPANDER_SOFT_RST_ENABLE);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_SOFT_RST_MASK,
+ 0);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_CLK_EN_MASK, 0);
+ snd_soc_component_update_bits(component, comp_ctl0_reg,
+ CDC_WSA_COMPANDER_HALT_MASK, 0);
+ }
+
+ return 0;
+}
+
+static void wsa_macro_enable_softclip_clk(struct snd_soc_component *component,
+ struct wsa_macro *wsa,
+ int path,
+ bool enable)
+{
+ u16 softclip_clk_reg = CDC_WSA_SOFTCLIP0_CRC +
+ (path * WSA_MACRO_RX_SOFTCLIP_OFFSET);
+ u8 softclip_mux_mask = (1 << path);
+ u8 softclip_mux_value = (1 << path);
+
+ if (enable) {
+ if (wsa->softclip_clk_users[path] == 0) {
+ snd_soc_component_update_bits(component,
+ softclip_clk_reg,
+ CDC_WSA_SOFTCLIP_CLK_EN_MASK,
+ CDC_WSA_SOFTCLIP_CLK_ENABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0,
+ softclip_mux_mask, softclip_mux_value);
+ }
+ wsa->softclip_clk_users[path]++;
+ } else {
+ wsa->softclip_clk_users[path]--;
+ if (wsa->softclip_clk_users[path] == 0) {
+ snd_soc_component_update_bits(component,
+ softclip_clk_reg,
+ CDC_WSA_SOFTCLIP_CLK_EN_MASK,
+ 0);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0,
+ softclip_mux_mask, 0x00);
+ }
+ }
+}
+
+static int wsa_macro_config_softclip(struct snd_soc_component *component,
+ int path, int event)
+{
+ u16 softclip_ctrl_reg = 0;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ int softclip_path = 0;
+
+ if (path == WSA_MACRO_COMP1)
+ softclip_path = WSA_MACRO_SOFTCLIP0;
+ else if (path == WSA_MACRO_COMP2)
+ softclip_path = WSA_MACRO_SOFTCLIP1;
+
+ if (!wsa->is_softclip_on[softclip_path])
+ return 0;
+
+ softclip_ctrl_reg = CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL +
+ (softclip_path * WSA_MACRO_RX_SOFTCLIP_OFFSET);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Softclip clock and mux */
+ wsa_macro_enable_softclip_clk(component, wsa, softclip_path,
+ true);
+ /* Enable Softclip control */
+ snd_soc_component_update_bits(component, softclip_ctrl_reg,
+ CDC_WSA_SOFTCLIP_EN_MASK,
+ CDC_WSA_SOFTCLIP_ENABLE);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, softclip_ctrl_reg,
+ CDC_WSA_SOFTCLIP_EN_MASK, 0);
+ wsa_macro_enable_softclip_clk(component, wsa, softclip_path,
+ false);
+ }
+
+ return 0;
+}
+
+static bool wsa_macro_adie_lb(struct snd_soc_component *component,
+ int interp_idx)
+{
+ u16 int_mux_cfg0 = 0, int_mux_cfg1 = 0;
+ u8 int_mux_cfg0_val = 0, int_mux_cfg1_val = 0;
+ u8 int_n_inp0 = 0, int_n_inp1 = 0, int_n_inp2 = 0;
+
+ int_mux_cfg0 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 + interp_idx * 8;
+ int_mux_cfg1 = int_mux_cfg0 + 4;
+ int_mux_cfg0_val = snd_soc_component_read(component, int_mux_cfg0);
+ int_mux_cfg1_val = snd_soc_component_read(component, int_mux_cfg1);
+
+ int_n_inp0 = int_mux_cfg0_val & 0x0F;
+ if (int_n_inp0 == INTn_1_INP_SEL_DEC0 ||
+ int_n_inp0 == INTn_1_INP_SEL_DEC1)
+ return true;
+
+ int_n_inp1 = int_mux_cfg0_val >> 4;
+ if (int_n_inp1 == INTn_1_INP_SEL_DEC0 ||
+ int_n_inp1 == INTn_1_INP_SEL_DEC1)
+ return true;
+
+ int_n_inp2 = int_mux_cfg1_val >> 4;
+ if (int_n_inp2 == INTn_1_INP_SEL_DEC0 ||
+ int_n_inp2 == INTn_1_INP_SEL_DEC1)
+ return true;
+
+ return false;
+}
+
+static int wsa_macro_enable_main_path(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 reg = 0;
+
+ reg = CDC_WSA_RX0_RX_PATH_CTL + WSA_MACRO_RX_PATH_OFFSET * w->shift;
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (wsa_macro_adie_lb(component, w->shift)) {
+ snd_soc_component_update_bits(component, reg,
+ CDC_WSA_RX_PATH_CLK_EN_MASK,
+ CDC_WSA_RX_PATH_CLK_ENABLE);
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wsa_macro_interp_get_primary_reg(u16 reg, u16 *ind)
+{
+ u16 prim_int_reg = 0;
+
+ switch (reg) {
+ case CDC_WSA_RX0_RX_PATH_CTL:
+ case CDC_WSA_RX0_RX_PATH_MIX_CTL:
+ prim_int_reg = CDC_WSA_RX0_RX_PATH_CTL;
+ *ind = 0;
+ break;
+ case CDC_WSA_RX1_RX_PATH_CTL:
+ case CDC_WSA_RX1_RX_PATH_MIX_CTL:
+ prim_int_reg = CDC_WSA_RX1_RX_PATH_CTL;
+ *ind = 1;
+ break;
+ }
+
+ return prim_int_reg;
+}
+
+static int wsa_macro_enable_prim_interpolator(struct snd_soc_component *component,
+ u16 reg, int event)
+{
+ u16 prim_int_reg;
+ u16 ind = 0;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ prim_int_reg = wsa_macro_interp_get_primary_reg(reg, &ind);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wsa->prim_int_users[ind]++;
+ if (wsa->prim_int_users[ind] == 1) {
+ snd_soc_component_update_bits(component,
+ prim_int_reg + WSA_MACRO_RX_PATH_CFG3_OFFSET,
+ CDC_WSA_RX_DC_DCOEFF_MASK,
+ 0x3);
+ snd_soc_component_update_bits(component, prim_int_reg,
+ CDC_WSA_RX_PATH_PGA_MUTE_EN_MASK,
+ CDC_WSA_RX_PATH_PGA_MUTE_ENABLE);
+ wsa_macro_hd2_control(component, prim_int_reg, event);
+ snd_soc_component_update_bits(component,
+ prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET,
+ CDC_WSA_RX_DSMDEM_CLK_EN_MASK,
+ CDC_WSA_RX_DSMDEM_CLK_ENABLE);
+ }
+ if ((reg != prim_int_reg) &&
+ ((snd_soc_component_read(
+ component, prim_int_reg)) & 0x10))
+ snd_soc_component_update_bits(component, reg,
+ 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wsa->prim_int_users[ind]--;
+ if (wsa->prim_int_users[ind] == 0) {
+ snd_soc_component_update_bits(component,
+ prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET,
+ CDC_WSA_RX_DSMDEM_CLK_EN_MASK, 0);
+ wsa_macro_hd2_control(component, prim_int_reg, event);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_config_ear_spkr_gain(struct snd_soc_component *component,
+ struct wsa_macro *wsa,
+ int event, int gain_reg)
+{
+ int comp_gain_offset, val;
+
+ switch (wsa->spkr_mode) {
+ /* Compander gain in WSA_MACRO_SPKR_MODE1 case is 12 dB */
+ case WSA_MACRO_SPKR_MODE_1:
+ comp_gain_offset = -12;
+ break;
+ /* Default case compander gain is 15 dB */
+ default:
+ comp_gain_offset = -15;
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Apply ear spkr gain only if compander is enabled */
+ if (wsa->comp_enabled[WSA_MACRO_COMP1] &&
+ (gain_reg == CDC_WSA_RX0_RX_VOL_CTL) &&
+ (wsa->ear_spkr_gain != 0)) {
+ /* For example, val is -8(-12+5-1) for 4dB of gain */
+ val = comp_gain_offset + wsa->ear_spkr_gain - 1;
+ snd_soc_component_write(component, gain_reg, val);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * Reset RX0 volume to 0 dB if compander is enabled and
+ * ear_spkr_gain is non-zero.
+ */
+ if (wsa->comp_enabled[WSA_MACRO_COMP1] &&
+ (gain_reg == CDC_WSA_RX0_RX_VOL_CTL) &&
+ (wsa->ear_spkr_gain != 0)) {
+ snd_soc_component_write(component, gain_reg, 0x0);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_enable_interpolator(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 gain_reg;
+ u16 reg;
+ int val;
+ int offset_val = 0;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ if (w->shift == WSA_MACRO_COMP1) {
+ reg = CDC_WSA_RX0_RX_PATH_CTL;
+ gain_reg = CDC_WSA_RX0_RX_VOL_CTL;
+ } else if (w->shift == WSA_MACRO_COMP2) {
+ reg = CDC_WSA_RX1_RX_PATH_CTL;
+ gain_reg = CDC_WSA_RX1_RX_VOL_CTL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Reset if needed */
+ wsa_macro_enable_prim_interpolator(component, reg, event);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ wsa_macro_config_compander(component, w->shift, event);
+ wsa_macro_config_softclip(component, w->shift, event);
+ /* apply gain after int clk is enabled */
+ if ((wsa->spkr_gain_offset == WSA_MACRO_GAIN_OFFSET_M1P5_DB) &&
+ (wsa->comp_enabled[WSA_MACRO_COMP1] ||
+ wsa->comp_enabled[WSA_MACRO_COMP2])) {
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX0_RX_PATH_SEC1,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_ENABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX0_RX_PATH_MIX_SEC0,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_ENABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX1_RX_PATH_SEC1,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_ENABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX1_RX_PATH_MIX_SEC0,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_ENABLE);
+ offset_val = -2;
+ }
+ val = snd_soc_component_read(component, gain_reg);
+ val += offset_val;
+ snd_soc_component_write(component, gain_reg, val);
+ wsa_macro_config_ear_spkr_gain(component, wsa,
+ event, gain_reg);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wsa_macro_config_compander(component, w->shift, event);
+ wsa_macro_config_softclip(component, w->shift, event);
+ wsa_macro_enable_prim_interpolator(component, reg, event);
+ if ((wsa->spkr_gain_offset == WSA_MACRO_GAIN_OFFSET_M1P5_DB) &&
+ (wsa->comp_enabled[WSA_MACRO_COMP1] ||
+ wsa->comp_enabled[WSA_MACRO_COMP2])) {
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX0_RX_PATH_SEC1,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_DISABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX0_RX_PATH_MIX_SEC0,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_DISABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX1_RX_PATH_SEC1,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_DISABLE);
+ snd_soc_component_update_bits(component,
+ CDC_WSA_RX1_RX_PATH_MIX_SEC0,
+ CDC_WSA_RX_PGA_HALF_DB_MASK,
+ CDC_WSA_RX_PGA_HALF_DB_DISABLE);
+ offset_val = 2;
+ val = snd_soc_component_read(component, gain_reg);
+ val += offset_val;
+ snd_soc_component_write(component, gain_reg, val);
+ }
+ wsa_macro_config_ear_spkr_gain(component, wsa,
+ event, gain_reg);
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_spk_boost_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);
+ u16 boost_path_ctl, boost_path_cfg1;
+ u16 reg, reg_mix;
+
+ if (!strcmp(w->name, "WSA_RX INT0 CHAIN")) {
+ boost_path_ctl = CDC_WSA_BOOST0_BOOST_PATH_CTL;
+ boost_path_cfg1 = CDC_WSA_RX0_RX_PATH_CFG1;
+ reg = CDC_WSA_RX0_RX_PATH_CTL;
+ reg_mix = CDC_WSA_RX0_RX_PATH_MIX_CTL;
+ } else if (!strcmp(w->name, "WSA_RX INT1 CHAIN")) {
+ boost_path_ctl = CDC_WSA_BOOST1_BOOST_PATH_CTL;
+ boost_path_cfg1 = CDC_WSA_RX1_RX_PATH_CFG1;
+ reg = CDC_WSA_RX1_RX_PATH_CTL;
+ reg_mix = CDC_WSA_RX1_RX_PATH_MIX_CTL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, boost_path_cfg1,
+ CDC_WSA_RX_PATH_SMART_BST_EN_MASK,
+ CDC_WSA_RX_PATH_SMART_BST_ENABLE);
+ snd_soc_component_update_bits(component, boost_path_ctl,
+ CDC_WSA_BOOST_PATH_CLK_EN_MASK,
+ CDC_WSA_BOOST_PATH_CLK_ENABLE);
+ if ((snd_soc_component_read(component, reg_mix)) & 0x10)
+ snd_soc_component_update_bits(component, reg_mix,
+ 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_component_update_bits(component, reg, 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, boost_path_ctl,
+ CDC_WSA_BOOST_PATH_CLK_EN_MASK,
+ CDC_WSA_BOOST_PATH_CLK_DISABLE);
+ snd_soc_component_update_bits(component, boost_path_cfg1,
+ CDC_WSA_RX_PATH_SMART_BST_EN_MASK,
+ CDC_WSA_RX_PATH_SMART_BST_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa_macro_enable_echo(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 wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u16 val, ec_tx = 0, ec_hq_reg;
+
+ val = snd_soc_component_read(component, CDC_WSA_RX_INP_MUX_RX_MIX_CFG0);
+
+ switch (w->shift) {
+ case WSA_MACRO_EC0_MUX:
+ val = val & CDC_WSA_RX_MIX_TX0_SEL_MASK;
+ ec_tx = val - 1;
+ break;
+ case WSA_MACRO_EC1_MUX:
+ val = val & CDC_WSA_RX_MIX_TX1_SEL_MASK;
+ ec_tx = (val >> CDC_WSA_RX_MIX_TX1_SEL_SHFT) - 1;
+ break;
+ }
+
+ if (wsa->ec_hq[ec_tx]) {
+ ec_hq_reg = CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL + 0x40 * ec_tx;
+ snd_soc_component_update_bits(component, ec_hq_reg,
+ CDC_WSA_EC_HQ_EC_CLK_EN_MASK,
+ CDC_WSA_EC_HQ_EC_CLK_ENABLE);
+ ec_hq_reg = CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 + 0x40 * ec_tx;
+ /* default set to 48k */
+ snd_soc_component_update_bits(component, ec_hq_reg,
+ CDC_WSA_EC_HQ_EC_REF_PCM_RATE_MASK,
+ CDC_WSA_EC_HQ_EC_REF_PCM_RATE_48K);
+ }
+
+ return 0;
+}
+
+static int wsa_macro_get_ec_hq(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ int ec_tx = ((struct soc_mixer_control *)
+ kcontrol->private_value)->shift;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wsa->ec_hq[ec_tx];
+ return 0;
+}
+
+static int wsa_macro_set_ec_hq(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ int ec_tx = ((struct soc_mixer_control *)
+ kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa->ec_hq[ec_tx] = value;
+
+ return 0;
+}
+
+static int wsa_macro_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wsa->comp_enabled[comp];
+ return 0;
+}
+
+static int wsa_macro_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ int comp = ((struct soc_mixer_control *)
+ kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa->comp_enabled[comp] = value;
+
+ return 0;
+}
+
+static int wsa_macro_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wsa->ear_spkr_gain;
+
+ return 0;
+}
+
+static int wsa_macro_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ wsa->ear_spkr_gain = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int wsa_macro_rx_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget =
+ snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] =
+ wsa->rx_port_value[widget->shift];
+ return 0;
+}
+
+static int wsa_macro_rx_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget =
+ snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+ u32 rx_port_value = ucontrol->value.integer.value[0];
+ u32 bit_input = 0;
+ u32 aif_rst;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ aif_rst = wsa->rx_port_value[widget->shift];
+ if (!rx_port_value) {
+ if (aif_rst == 0) {
+ dev_err(component->dev, "%s: AIF reset already\n", __func__);
+ return 0;
+ }
+ if (aif_rst >= WSA_MACRO_RX_MAX) {
+ dev_err(component->dev, "%s: Invalid AIF reset\n", __func__);
+ return 0;
+ }
+ }
+ wsa->rx_port_value[widget->shift] = rx_port_value;
+
+ bit_input = widget->shift;
+
+ switch (rx_port_value) {
+ case 0:
+ if (wsa->active_ch_cnt[aif_rst]) {
+ clear_bit(bit_input,
+ &wsa->active_ch_mask[aif_rst]);
+ wsa->active_ch_cnt[aif_rst]--;
+ }
+ break;
+ case 1:
+ case 2:
+ set_bit(bit_input,
+ &wsa->active_ch_mask[rx_port_value]);
+ wsa->active_ch_cnt[rx_port_value]++;
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: Invalid AIF_ID for WSA RX MUX %d\n",
+ __func__, rx_port_value);
+ return -EINVAL;
+ }
+
+ snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+ rx_port_value, e, update);
+ return 0;
+}
+
+static int wsa_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ int path = ((struct soc_mixer_control *)kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] = wsa->is_softclip_on[path];
+
+ return 0;
+}
+
+static int wsa_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ int path = ((struct soc_mixer_control *) kcontrol->private_value)->shift;
+
+ wsa->is_softclip_on[path] = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wsa_macro_snd_controls[] = {
+ SOC_ENUM_EXT("EAR SPKR PA Gain", wsa_macro_ear_spkr_pa_gain_enum,
+ wsa_macro_ear_spkr_pa_gain_get,
+ wsa_macro_ear_spkr_pa_gain_put),
+ SOC_SINGLE_EXT("WSA_Softclip0 Enable", SND_SOC_NOPM,
+ WSA_MACRO_SOFTCLIP0, 1, 0,
+ wsa_macro_soft_clip_enable_get,
+ wsa_macro_soft_clip_enable_put),
+ SOC_SINGLE_EXT("WSA_Softclip1 Enable", SND_SOC_NOPM,
+ WSA_MACRO_SOFTCLIP1, 1, 0,
+ wsa_macro_soft_clip_enable_get,
+ wsa_macro_soft_clip_enable_put),
+
+ SOC_SINGLE_S8_TLV("WSA_RX0 Digital Volume", CDC_WSA_RX0_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("WSA_RX1 Digital Volume", CDC_WSA_RX1_RX_VOL_CTL,
+ -84, 40, digital_gain),
+
+ SOC_SINGLE("WSA_RX0 Digital Mute", CDC_WSA_RX0_RX_PATH_CTL, 4, 1, 0),
+ SOC_SINGLE("WSA_RX1 Digital Mute", CDC_WSA_RX1_RX_PATH_CTL, 4, 1, 0),
+ SOC_SINGLE("WSA_RX0_MIX Digital Mute", CDC_WSA_RX0_RX_PATH_MIX_CTL, 4,
+ 1, 0),
+ SOC_SINGLE("WSA_RX1_MIX Digital Mute", CDC_WSA_RX1_RX_PATH_MIX_CTL, 4,
+ 1, 0),
+ SOC_SINGLE_EXT("WSA_COMP1 Switch", SND_SOC_NOPM, WSA_MACRO_COMP1, 1, 0,
+ wsa_macro_get_compander, wsa_macro_set_compander),
+ SOC_SINGLE_EXT("WSA_COMP2 Switch", SND_SOC_NOPM, WSA_MACRO_COMP2, 1, 0,
+ wsa_macro_get_compander, wsa_macro_set_compander),
+ SOC_SINGLE_EXT("WSA_RX0 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX0, 1, 0,
+ wsa_macro_get_ec_hq, wsa_macro_set_ec_hq),
+ SOC_SINGLE_EXT("WSA_RX1 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX1, 1, 0,
+ wsa_macro_get_ec_hq, wsa_macro_set_ec_hq),
+};
+
+static const struct soc_enum rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_mux_text), rx_mux_text);
+
+static const struct snd_kcontrol_new rx_mux[WSA_MACRO_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("WSA RX0 Mux", rx_mux_enum,
+ wsa_macro_rx_mux_get, wsa_macro_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("WSA RX1 Mux", rx_mux_enum,
+ wsa_macro_rx_mux_get, wsa_macro_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("WSA RX_MIX0 Mux", rx_mux_enum,
+ wsa_macro_rx_mux_get, wsa_macro_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("WSA RX_MIX1 Mux", rx_mux_enum,
+ wsa_macro_rx_mux_get, wsa_macro_rx_mux_put),
+};
+
+static int wsa_macro_vi_feed_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u32 spk_tx_id = mixer->shift;
+ u32 dai_id = widget->shift;
+
+ if (test_bit(spk_tx_id, &wsa->active_ch_mask[dai_id]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int wsa_macro_vi_feed_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ u32 enable = ucontrol->value.integer.value[0];
+ u32 spk_tx_id = mixer->shift;
+
+ if (enable) {
+ if (spk_tx_id == WSA_MACRO_TX0 &&
+ !test_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) {
+ set_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI]);
+ wsa->active_ch_cnt[WSA_MACRO_AIF_VI]++;
+ }
+ if (spk_tx_id == WSA_MACRO_TX1 &&
+ !test_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) {
+ set_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI]);
+ wsa->active_ch_cnt[WSA_MACRO_AIF_VI]++;
+ }
+ } else {
+ if (spk_tx_id == WSA_MACRO_TX0 &&
+ test_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) {
+ clear_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI]);
+ wsa->active_ch_cnt[WSA_MACRO_AIF_VI]--;
+ }
+ if (spk_tx_id == WSA_MACRO_TX1 &&
+ test_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) {
+ clear_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[WSA_MACRO_AIF_VI]);
+ wsa->active_ch_cnt[WSA_MACRO_AIF_VI]--;
+ }
+ }
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new aif_vi_mixer[] = {
+ SOC_SINGLE_EXT("WSA_SPKR_VI_1", SND_SOC_NOPM, WSA_MACRO_TX0, 1, 0,
+ wsa_macro_vi_feed_mixer_get,
+ wsa_macro_vi_feed_mixer_put),
+ SOC_SINGLE_EXT("WSA_SPKR_VI_2", SND_SOC_NOPM, WSA_MACRO_TX1, 1, 0,
+ wsa_macro_vi_feed_mixer_get,
+ wsa_macro_vi_feed_mixer_put),
+};
+
+static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("WSA AIF1 PB", "WSA_AIF1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("WSA AIF_MIX1 PB", "WSA_AIF_MIX1 Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT_E("WSA AIF_VI", "WSA_AIF_VI Capture", 0,
+ SND_SOC_NOPM, WSA_MACRO_AIF_VI, 0,
+ wsa_macro_enable_vi_feedback,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("WSA AIF_ECHO", "WSA_AIF_ECHO Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MIXER("WSA_AIF_VI Mixer", SND_SOC_NOPM, WSA_MACRO_AIF_VI,
+ 0, aif_vi_mixer, ARRAY_SIZE(aif_vi_mixer)),
+ SND_SOC_DAPM_MUX_E("WSA RX_MIX EC0_MUX", SND_SOC_NOPM,
+ WSA_MACRO_EC0_MUX, 0,
+ &rx_mix_ec0_mux, wsa_macro_enable_echo,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("WSA RX_MIX EC1_MUX", SND_SOC_NOPM,
+ WSA_MACRO_EC1_MUX, 0,
+ &rx_mix_ec1_mux, wsa_macro_enable_echo,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("WSA RX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX0, 0,
+ &rx_mux[WSA_MACRO_RX0]),
+ SND_SOC_DAPM_MUX("WSA RX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX1, 0,
+ &rx_mux[WSA_MACRO_RX1]),
+ SND_SOC_DAPM_MUX("WSA RX_MIX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, 0,
+ &rx_mux[WSA_MACRO_RX_MIX0]),
+ SND_SOC_DAPM_MUX("WSA RX_MIX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, 0,
+ &rx_mux[WSA_MACRO_RX_MIX1]),
+
+ SND_SOC_DAPM_MIXER("WSA RX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA RX_MIX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA RX_MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux),
+ SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", CDC_WSA_RX0_RX_PATH_MIX_CTL,
+ 0, 0, &rx0_mix_mux, wsa_macro_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, &rx1_prim_inp0_mux),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux),
+ SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", CDC_WSA_RX1_RX_PATH_MIX_CTL,
+ 0, 0, &rx1_mix_mux, wsa_macro_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT0 MIX", SND_SOC_NOPM, 0, 0, NULL, 0,
+ wsa_macro_enable_main_path, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT1 MIX", SND_SOC_NOPM, 1, 0, NULL, 0,
+ wsa_macro_enable_main_path, SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_MIXER("WSA_RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("WSA_RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("WSA_RX0 INT0 SIDETONE MIX", CDC_WSA_RX0_RX_PATH_CFG1,
+ 4, 0, &rx0_sidetone_mix_mux),
+
+ SND_SOC_DAPM_INPUT("WSA SRC0_INP"),
+ SND_SOC_DAPM_INPUT("WSA_TX DEC0_INP"),
+ SND_SOC_DAPM_INPUT("WSA_TX DEC1_INP"),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT0 INTERP", SND_SOC_NOPM,
+ WSA_MACRO_COMP1, 0, NULL, 0,
+ wsa_macro_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT1 INTERP", SND_SOC_NOPM,
+ WSA_MACRO_COMP2, 0, NULL, 0,
+ wsa_macro_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT0 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, wsa_macro_spk_boost_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("WSA_RX INT1 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, wsa_macro_spk_boost_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("VIINPUT_WSA"),
+ SND_SOC_DAPM_OUTPUT("WSA_SPK1 OUT"),
+ SND_SOC_DAPM_OUTPUT("WSA_SPK2 OUT"),
+
+ SND_SOC_DAPM_SUPPLY("WSA_RX0_CLK", CDC_WSA_RX0_RX_PATH_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("WSA_RX1_CLK", CDC_WSA_RX1_RX_PATH_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("WSA_RX_MIX0_CLK", CDC_WSA_RX0_RX_PATH_MIX_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("WSA_RX_MIX1_CLK", CDC_WSA_RX1_RX_PATH_MIX_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("WSA_MCLK", 0, SND_SOC_NOPM, 0, 0,
+ wsa_macro_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route wsa_audio_map[] = {
+ /* VI Feedback */
+ {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_1", "VIINPUT_WSA"},
+ {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_2", "VIINPUT_WSA"},
+ {"WSA AIF_VI", NULL, "WSA_AIF_VI Mixer"},
+ {"WSA AIF_VI", NULL, "WSA_MCLK"},
+
+ {"WSA RX_MIX EC0_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"},
+ {"WSA RX_MIX EC1_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"},
+ {"WSA RX_MIX EC0_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"},
+ {"WSA RX_MIX EC1_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"},
+ {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC0_MUX"},
+ {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC1_MUX"},
+ {"WSA AIF_ECHO", NULL, "WSA_MCLK"},
+
+ {"WSA AIF1 PB", NULL, "WSA_MCLK"},
+ {"WSA AIF_MIX1 PB", NULL, "WSA_MCLK"},
+
+ {"WSA RX0 MUX", "AIF1_PB", "WSA AIF1 PB"},
+ {"WSA RX1 MUX", "AIF1_PB", "WSA AIF1 PB"},
+ {"WSA RX_MIX0 MUX", "AIF1_PB", "WSA AIF1 PB"},
+ {"WSA RX_MIX1 MUX", "AIF1_PB", "WSA AIF1 PB"},
+
+ {"WSA RX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"},
+ {"WSA RX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"},
+ {"WSA RX_MIX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"},
+ {"WSA RX_MIX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"},
+
+ {"WSA RX0", NULL, "WSA RX0 MUX"},
+ {"WSA RX1", NULL, "WSA RX1 MUX"},
+ {"WSA RX_MIX0", NULL, "WSA RX_MIX0 MUX"},
+ {"WSA RX_MIX1", NULL, "WSA RX_MIX1 MUX"},
+
+ {"WSA RX0", NULL, "WSA_RX0_CLK"},
+ {"WSA RX1", NULL, "WSA_RX1_CLK"},
+ {"WSA RX_MIX0", NULL, "WSA_RX_MIX0_CLK"},
+ {"WSA RX_MIX1", NULL, "WSA_RX_MIX1_CLK"},
+
+ {"WSA_RX0 INP0", "RX0", "WSA RX0"},
+ {"WSA_RX0 INP0", "RX1", "WSA RX1"},
+ {"WSA_RX0 INP0", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX0 INP0", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX0 INP0", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX0 INP0", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP0"},
+
+ {"WSA_RX0 INP1", "RX0", "WSA RX0"},
+ {"WSA_RX0 INP1", "RX1", "WSA RX1"},
+ {"WSA_RX0 INP1", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX0 INP1", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX0 INP1", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX0 INP1", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP1"},
+
+ {"WSA_RX0 INP2", "RX0", "WSA RX0"},
+ {"WSA_RX0 INP2", "RX1", "WSA RX1"},
+ {"WSA_RX0 INP2", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX0 INP2", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX0 INP2", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX0 INP2", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP2"},
+
+ {"WSA_RX0 MIX INP", "RX0", "WSA RX0"},
+ {"WSA_RX0 MIX INP", "RX1", "WSA RX1"},
+ {"WSA_RX0 MIX INP", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX0 MIX INP", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX0 MIX INP"},
+
+ {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX INT0 MIX"},
+ {"WSA_RX INT0 INTERP", NULL, "WSA_RX INT0 SEC MIX"},
+ {"WSA_RX0 INT0 SIDETONE MIX", "SRC0", "WSA SRC0_INP"},
+ {"WSA_RX INT0 INTERP", NULL, "WSA_RX0 INT0 SIDETONE MIX"},
+ {"WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 INTERP"},
+
+ {"WSA_SPK1 OUT", NULL, "WSA_RX INT0 CHAIN"},
+ {"WSA_SPK1 OUT", NULL, "WSA_MCLK"},
+
+ {"WSA_RX1 INP0", "RX0", "WSA RX0"},
+ {"WSA_RX1 INP0", "RX1", "WSA RX1"},
+ {"WSA_RX1 INP0", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX1 INP0", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX1 INP0", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX1 INP0", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP0"},
+
+ {"WSA_RX1 INP1", "RX0", "WSA RX0"},
+ {"WSA_RX1 INP1", "RX1", "WSA RX1"},
+ {"WSA_RX1 INP1", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX1 INP1", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX1 INP1", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX1 INP1", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP1"},
+
+ {"WSA_RX1 INP2", "RX0", "WSA RX0"},
+ {"WSA_RX1 INP2", "RX1", "WSA RX1"},
+ {"WSA_RX1 INP2", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX1 INP2", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX1 INP2", "DEC0", "WSA_TX DEC0_INP"},
+ {"WSA_RX1 INP2", "DEC1", "WSA_TX DEC1_INP"},
+ {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP2"},
+
+ {"WSA_RX1 MIX INP", "RX0", "WSA RX0"},
+ {"WSA_RX1 MIX INP", "RX1", "WSA RX1"},
+ {"WSA_RX1 MIX INP", "RX_MIX0", "WSA RX_MIX0"},
+ {"WSA_RX1 MIX INP", "RX_MIX1", "WSA RX_MIX1"},
+ {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX1 MIX INP"},
+
+ {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX INT1 MIX"},
+ {"WSA_RX INT1 INTERP", NULL, "WSA_RX INT1 SEC MIX"},
+
+ {"WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 INTERP"},
+ {"WSA_SPK2 OUT", NULL, "WSA_RX INT1 CHAIN"},
+ {"WSA_SPK2 OUT", NULL, "WSA_MCLK"},
+};
+
+static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable)
+{
+ struct regmap *regmap = wsa->regmap;
+
+ if (enable) {
+ wsa_macro_mclk_enable(wsa, true);
+ if (wsa->reset_swr)
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ 0x02, 0x02);
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ 0x01, 0x01);
+
+ if (wsa->reset_swr)
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ 0x02, 0x00);
+ wsa->reset_swr = false;
+ } else {
+ regmap_update_bits(regmap,
+ CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ 0x01, 0x00);
+ wsa_macro_mclk_enable(wsa, false);
+ }
+
+ return 0;
+}
+
+static int wsa_macro_component_probe(struct snd_soc_component *comp)
+{
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(comp);
+
+ snd_soc_component_init_regmap(comp, wsa->regmap);
+
+ wsa->spkr_gain_offset = WSA_MACRO_GAIN_OFFSET_M1P5_DB;
+
+ /* set SPKR rate to FS_2P4_3P072 */
+ snd_soc_component_update_bits(comp, CDC_WSA_RX0_RX_PATH_CFG1,
+ CDC_WSA_RX_PATH_SPKR_RATE_MASK,
+ CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072);
+
+ snd_soc_component_update_bits(comp, CDC_WSA_RX1_RX_PATH_CFG1,
+ CDC_WSA_RX_PATH_SPKR_RATE_MASK,
+ CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072);
+
+ wsa_macro_set_spkr_mode(comp, WSA_MACRO_SPKR_MODE_1);
+
+ return 0;
+}
+
+static int swclk_gate_enable(struct clk_hw *hw)
+{
+ return wsa_swrm_clock(to_wsa_macro(hw), true);
+}
+
+static void swclk_gate_disable(struct clk_hw *hw)
+{
+ wsa_swrm_clock(to_wsa_macro(hw), false);
+}
+
+static int swclk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct wsa_macro *wsa = to_wsa_macro(hw);
+ int ret, val;
+
+ regmap_read(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, &val);
+ ret = val & BIT(0);
+
+ return ret;
+}
+
+static unsigned long swclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return parent_rate / 2;
+}
+
+static const struct clk_ops swclk_gate_ops = {
+ .prepare = swclk_gate_enable,
+ .unprepare = swclk_gate_disable,
+ .is_enabled = swclk_gate_is_enabled,
+ .recalc_rate = swclk_recalc_rate,
+
+};
+
+static struct clk *wsa_macro_register_mclk_output(struct wsa_macro *wsa)
+{
+ struct device *dev = wsa->dev;
+ struct device_node *np = dev->of_node;
+ const char *parent_clk_name;
+ const char *clk_name = "mclk";
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ parent_clk_name = __clk_get_name(wsa->clks[2].clk);
+
+ init.name = clk_name;
+ init.ops = &swclk_gate_ops;
+ init.flags = 0;
+ init.parent_names = &parent_clk_name;
+ init.num_parents = 1;
+ wsa->hw.init = &init;
+ hw = &wsa->hw;
+ ret = clk_hw_register(wsa->dev, hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw->clk);
+
+ return NULL;
+}
+
+static const struct snd_soc_component_driver wsa_macro_component_drv = {
+ .name = "WSA MACRO",
+ .probe = wsa_macro_component_probe,
+ .controls = wsa_macro_snd_controls,
+ .num_controls = ARRAY_SIZE(wsa_macro_snd_controls),
+ .dapm_widgets = wsa_macro_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets),
+ .dapm_routes = wsa_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wsa_audio_map),
+};
+
+static int wsa_macro_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct wsa_macro *wsa;
+ void __iomem *base;
+ int ret;
+
+ wsa = devm_kzalloc(dev, sizeof(*wsa), GFP_KERNEL);
+ if (!wsa)
+ return -ENOMEM;
+
+ wsa->clks[0].id = "macro";
+ wsa->clks[1].id = "dcodec";
+ wsa->clks[2].id = "mclk";
+ wsa->clks[3].id = "npl";
+ wsa->clks[4].id = "fsgen";
+
+ ret = devm_clk_bulk_get(dev, WSA_NUM_CLKS_MAX, wsa->clks);
+ if (ret) {
+ dev_err(dev, "Error getting WSA Clocks (%d)\n", ret);
+ return ret;
+ }
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ wsa->regmap = devm_regmap_init_mmio(dev, base, &wsa_regmap_config);
+
+ dev_set_drvdata(dev, wsa);
+
+ wsa->reset_swr = true;
+ wsa->dev = dev;
+
+ /* set MCLK and NPL rates */
+ clk_set_rate(wsa->clks[2].clk, WSA_MACRO_MCLK_FREQ);
+ clk_set_rate(wsa->clks[3].clk, WSA_MACRO_MCLK_FREQ);
+
+ ret = clk_bulk_prepare_enable(WSA_NUM_CLKS_MAX, wsa->clks);
+ if (ret)
+ return ret;
+
+ wsa_macro_register_mclk_output(wsa);
+
+ ret = devm_snd_soc_register_component(dev, &wsa_macro_component_drv,
+ wsa_macro_dai,
+ ARRAY_SIZE(wsa_macro_dai));
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ clk_bulk_disable_unprepare(WSA_NUM_CLKS_MAX, wsa->clks);
+
+ return ret;
+
+}
+
+static int wsa_macro_remove(struct platform_device *pdev)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev);
+
+ of_clk_del_provider(pdev->dev.of_node);
+
+ clk_bulk_disable_unprepare(WSA_NUM_CLKS_MAX, wsa->clks);
+
+ return 0;
+}
+
+static const struct of_device_id wsa_macro_dt_match[] = {
+ {.compatible = "qcom,sm8250-lpass-wsa-macro"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, wsa_macro_dt_match);
+
+static struct platform_driver wsa_macro_driver = {
+ .driver = {
+ .name = "wsa_macro",
+ .of_match_table = wsa_macro_dt_match,
+ },
+ .probe = wsa_macro_probe,
+ .remove = wsa_macro_remove,
+};
+
+module_platform_driver(wsa_macro_driver);
+MODULE_DESCRIPTION("WSA macro driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/lpass-wsa-macro.h b/sound/soc/codecs/lpass-wsa-macro.h
new file mode 100644
index 000000000000..2372c13b0be6
--- /dev/null
+++ b/sound/soc/codecs/lpass-wsa-macro.h
@@ -0,0 +1,15 @@
+#ifndef __LPASS_WSA_MACRO_H__
+#define __LPASS_WSA_MACRO_H__
+
+/*
+ * Selects compander and smart boost settings
+ * for a given speaker mode
+ */
+enum {
+ WSA_MACRO_SPKR_MODE_DEFAULT,
+ WSA_MACRO_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */
+};
+
+int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode);
+
+#endif /* __LPASS_WSA_MACRO_H__ */
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index 40f682f5dab8..3197c701cdc5 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -2112,7 +2112,7 @@ static struct clk *wcd934x_register_mclk_output(struct wcd934x_codec *wcd)
wcd->hw.init = &init;
hw = &wcd->hw;
- ret = clk_hw_register(wcd->dev->parent, hw);
+ ret = devm_clk_hw_register(wcd->dev->parent, hw);
if (ret)
return ERR_PTR(ret);
@@ -5050,6 +5050,13 @@ static int wcd934x_codec_probe(struct platform_device *pdev)
wcd934x_slim_dais,
ARRAY_SIZE(wcd934x_slim_dais));
}
+static int wcd934x_codec_remove(struct platform_device *pdev)
+{
+ of_clk_del_provider(pdev->dev.of_node);
+
+ return 0;
+}
+
static const struct platform_device_id wcd934x_driver_id[] = {
{
@@ -5061,6 +5068,7 @@ MODULE_DEVICE_TABLE(platform, wcd934x_driver_id);
static struct platform_driver wcd934x_codec_driver = {
.probe = &wcd934x_codec_probe,
+ .remove = wcd934x_codec_remove,
.id_table = wcd934x_driver_id,
.driver = {
.name = "wcd934x-codec",
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 2696ffcba880..484cad31da25 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -127,4 +127,15 @@ config SND_SOC_SDM845
SDM845 SoC-based systems.
Say Y if you want to use audio device on this SoCs.
+config SND_SOC_SM8250
+ tristate "SoC Machine driver for SM8250 boards"
+ depends on QCOM_APR && SOUNDWIRE
+ depends on COMMON_CLK
+ select SND_SOC_QDSP6
+ select SND_SOC_QCOM_COMMON
+ help
+ To add support for audio on Qualcomm Technologies Inc.
+ SM8250 SoC-based systems.
+ Say Y if you want to use audio device on this SoCs.
+
endif #SND_SOC_QCOM
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
index 0bd90d74e3db..effa4b3f58fa 100644
--- a/sound/soc/qcom/Makefile
+++ b/sound/soc/qcom/Makefile
@@ -19,12 +19,14 @@ snd-soc-storm-objs := storm.o
snd-soc-apq8016-sbc-objs := apq8016_sbc.o
snd-soc-apq8096-objs := apq8096.o
snd-soc-sdm845-objs := sdm845.o
+snd-soc-sm8250-objs := sm8250.o
snd-soc-qcom-common-objs := common.o
obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o
obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-apq8096.o
obj-$(CONFIG_SND_SOC_SDM845) += snd-soc-sdm845.o
+obj-$(CONFIG_SND_SOC_SM8250) += snd-soc-sm8250.o
obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o
#DSP lib
diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c
index 1a69baefc5ce..2426f5e0e10d 100644
--- a/sound/soc/qcom/apq8096.c
+++ b/sound/soc/qcom/apq8096.c
@@ -5,14 +5,21 @@
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <sound/soc.h>
+#include <sound/jack.h>
#include <sound/soc-dapm.h>
#include <sound/pcm.h>
+#include <uapi/linux/input-event-codes.h>
#include "common.h"
#define SLIM_MAX_TX_PORTS 16
#define SLIM_MAX_RX_PORTS 16
#define WCD9335_DEFAULT_MCLK_RATE 9600000
+struct apq8096_card_data {
+ struct snd_soc_jack jack;
+ bool jack_setup;
+};
+
static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -67,6 +74,7 @@ static struct snd_soc_ops apq8096_ops = {
static int apq8096_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct apq8096_card_data *data = snd_soc_card_get_drvdata(rtd->card);
/*
* Codec SLIMBUS configuration
@@ -79,6 +87,8 @@ static int apq8096_init(struct snd_soc_pcm_runtime *rtd)
unsigned int tx_ch[SLIM_MAX_TX_PORTS] = {128, 129, 130, 131, 132, 133,
134, 135, 136, 137, 138, 139,
140, 141, 142, 143};
+ struct snd_soc_card *card = rtd->card;
+ int rval;
snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
@@ -86,6 +96,38 @@ static int apq8096_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dai_set_sysclk(codec_dai, 0, WCD9335_DEFAULT_MCLK_RATE,
SNDRV_PCM_STREAM_PLAYBACK);
+ if (!data->jack_setup) {
+ struct snd_jack *jack;
+
+ rval = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_HEADPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4,
+ &data->jack, NULL, 0);
+
+ if (rval < 0) {
+ dev_err(card->dev, "Unable to add Headphone Jack\n");
+ return rval;
+ }
+
+ jack = data->jack.jack;
+
+ snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+ data->jack_setup = true;
+ }
+
+ rval = snd_soc_component_set_jack(codec_dai->component,
+ &data->jack, NULL);
+ if (rval != 0 && rval != -ENOTSUPP) {
+ dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+ return rval;
+ }
+
return 0;
}
@@ -105,6 +147,7 @@ static void apq8096_add_be_ops(struct snd_soc_card *card)
static int apq8096_platform_probe(struct platform_device *pdev)
{
+ struct apq8096_card_data *data;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
int ret;
@@ -113,9 +156,14 @@ static int apq8096_platform_probe(struct platform_device *pdev)
if (!card)
return -ENOMEM;
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
card->dev = dev;
card->owner = THIS_MODULE;
dev_set_drvdata(dev, card);
+ snd_soc_card_set_drvdata(card, data);
ret = qcom_snd_parse_of(card);
if (ret)
return ret;
diff --git a/sound/soc/qcom/qdsp6/q6afe-clocks.c b/sound/soc/qcom/qdsp6/q6afe-clocks.c
index 2efc2eaa0424..acfc0c698f6a 100644
--- a/sound/soc/qcom/qdsp6/q6afe-clocks.c
+++ b/sound/soc/qcom/qdsp6/q6afe-clocks.c
@@ -16,6 +16,7 @@
.afe_clk_id = Q6AFE_##id, \
.name = #id, \
.attributes = LPASS_CLK_ATTRIBUTE_COUPLE_NO, \
+ .rate = 19200000, \
.hw.init = &(struct clk_init_data) { \
.ops = &clk_q6afe_ops, \
.name = #id, \
diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c
new file mode 100644
index 000000000000..314e3e566d6d
--- /dev/null
+++ b/sound/soc/qcom/sm8250.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, Linaro Limited
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <linux/soundwire/sdw.h>
+#include "qdsp6/q6afe.h"
+#include "common.h"
+
+#define DRIVER_NAME "sm8250"
+#define MI2S_BCLK_RATE 1536000
+
+struct sm8250_snd_data {
+ bool stream_prepared[AFE_PORT_MAX];
+ struct snd_soc_card *card;
+ struct sdw_stream_runtime *sruntime[AFE_PORT_MAX];
+};
+
+static int sm8250_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ return 0;
+}
+
+static int sm8250_snd_startup(struct snd_pcm_substream *substream)
+{
+ unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS;
+ unsigned int codec_dai_fmt = SND_SOC_DAIFMT_CBS_CFS;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case TERTIARY_MI2S_RX:
+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
+ snd_soc_dai_set_sysclk(cpu_dai,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK);
+ snd_soc_dai_set_fmt(cpu_dai, fmt);
+ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int sm8250_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct sm8250_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
+ struct sdw_stream_runtime *sruntime;
+ int i;
+
+ switch (cpu_dai->id) {
+ case WSA_CODEC_DMA_RX_0:
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ sruntime = snd_soc_dai_get_sdw_stream(codec_dai,
+ substream->stream);
+ if (sruntime != ERR_PTR(-ENOTSUPP))
+ pdata->sruntime[cpu_dai->id] = sruntime;
+ }
+ break;
+ }
+
+ return 0;
+
+}
+
+static int sm8250_snd_wsa_dma_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
+ int ret;
+
+ if (!sruntime)
+ return 0;
+
+ if (data->stream_prepared[cpu_dai->id]) {
+ sdw_disable_stream(sruntime);
+ sdw_deprepare_stream(sruntime);
+ data->stream_prepared[cpu_dai->id] = false;
+ }
+
+ ret = sdw_prepare_stream(sruntime);
+ if (ret)
+ return ret;
+
+ /**
+ * NOTE: there is a strict hw requirement about the ordering of port
+ * enables and actual WSA881x PA enable. PA enable should only happen
+ * after soundwire ports are enabled if not DC on the line is
+ * accumulated resulting in Click/Pop Noise
+ * PA enable/mute are handled as part of codec DAPM and digital mute.
+ */
+
+ ret = sdw_enable_stream(sruntime);
+ if (ret) {
+ sdw_deprepare_stream(sruntime);
+ return ret;
+ }
+ data->stream_prepared[cpu_dai->id] = true;
+
+ return ret;
+}
+
+static int sm8250_snd_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case WSA_CODEC_DMA_RX_0:
+ case WSA_CODEC_DMA_RX_1:
+ return sm8250_snd_wsa_dma_prepare(substream);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int sm8250_snd_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
+
+ switch (cpu_dai->id) {
+ case WSA_CODEC_DMA_RX_0:
+ case WSA_CODEC_DMA_RX_1:
+ if (sruntime && data->stream_prepared[cpu_dai->id]) {
+ sdw_disable_stream(sruntime);
+ sdw_deprepare_stream(sruntime);
+ data->stream_prepared[cpu_dai->id] = false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops sm8250_be_ops = {
+ .startup = sm8250_snd_startup,
+ .hw_params = sm8250_snd_hw_params,
+ .hw_free = sm8250_snd_hw_free,
+ .prepare = sm8250_snd_prepare,
+};
+
+static void sm8250_add_be_ops(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *link;
+ int i;
+
+ for_each_card_prelinks(card, i, link) {
+ if (link->no_pcm == 1) {
+ link->be_hw_params_fixup = sm8250_be_hw_params_fixup;
+ link->ops = &sm8250_be_ops;
+ }
+ }
+}
+
+static int sm8250_platform_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct sm8250_snd_data *data;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ /* Allocate the private data */
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ card->dev = dev;
+ dev_set_drvdata(dev, card);
+ snd_soc_card_set_drvdata(card, data);
+ ret = qcom_snd_parse_of(card);
+ if (ret)
+ return ret;
+
+ card->driver_name = DRIVER_NAME;
+ sm8250_add_be_ops(card);
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct of_device_id snd_sm8250_dt_match[] = {
+ {.compatible = "qcom,sm8250"},
+ {.compatible = "qcom,qrb5165-rb5"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, snd_sm8250_dt_match);
+
+static struct platform_driver snd_sm8250_driver = {
+ .probe = sm8250_platform_probe,
+ .driver = {
+ .name = "snd-sm8250",
+ .of_match_table = snd_sm8250_dt_match,
+ },
+};
+module_platform_driver(snd_sm8250_driver);
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
+MODULE_DESCRIPTION("SM8250 ASoC Machine Driver");
+MODULE_LICENSE("GPL v2");