diff options
author | Linaro CI <ci_notify@linaro.org> | 2019-10-27 17:50:04 +0000 |
---|---|---|
committer | Linaro CI <ci_notify@linaro.org> | 2019-10-27 17:50:04 +0000 |
commit | dde5583f7cca8234dbf859eee81076b54519b87c (patch) | |
tree | 3d66bc51bc1d598928bd019ce338f459db5df63c | |
parent | 6df982219c98433c25ee8c4743ef04abcbfc273e (diff) | |
parent | ef2734c8b201b2ef7d82a5acb8b833e102974925 (diff) |
Merge remote-tracking branch 'audio/tracking-qcomlt-audio' into integration-linux-qcomlt
29 files changed, 9588 insertions, 28 deletions
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,wcd934x-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,wcd934x-pinctrl.yaml new file mode 100644 index 000000000000..c8a36cbc4935 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,wcd934x-pinctrl.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,wcd934x-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: WCD9340/WCD9341 GPIO Pin controller + +maintainers: + - Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + +description: | + Qualcomm Technologies Inc WCD9340/WCD9341 Audio Codec has integrated + gpio controller to control 5 gpios on the chip. + +properties: + compatible: + enum: + - qcom,wcd9340-pinctrl + - qcom,wcd9341-pinctrl + + reg: + maxItems: 1 + + gpio-controller: true + + gpio-ranges: + maxItems: 1 + + '#gpio-cells': + const: 2 + +required: + - compatible + - reg + - gpio-controller + - gpio-ranges + - "#gpio-cells" + + +examples: + - | + wcdpinctrl: wcdpinctrl@42 { + compatible = "qcom,wcd9340-pinctrl"; + reg = <0x042 0x2>; + gpio-controller; + gpio-ranges = <&wcdpinctrl 0 0 5>; + #gpio-cells = <2>; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/qcom,sdm845.txt b/Documentation/devicetree/bindings/sound/qcom,sdm845.txt index 408c4837e6d5..ca8c89e88bfa 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sdm845.txt +++ b/Documentation/devicetree/bindings/sound/qcom,sdm845.txt @@ -5,7 +5,10 @@ This binding describes the SDM845 sound card, which uses qdsp for audio. - compatible: Usage: required Value type: <stringlist> - Definition: must be "qcom,sdm845-sndcard" + Definition: must be one of this + "qcom,sdm845-sndcard" + "qcom,db845c-sndcard" + "lenovo,yoga-c630-sndcard" - audio-routing: Usage: Optional diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml new file mode 100644 index 000000000000..299d6b96c339 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml @@ -0,0 +1,169 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,wcd934x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bindings for Qualcomm WCD9340/WCD9341 Audio Codec + +maintainers: + - Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + +description: | + Qualcomm WCD9340/WCD9341 Codec is a standalone Hi-Fi audio codec IC. + It has in-built Soundwire controller, pin controller, interrupt mux and + supports both I2S/I2C and SLIMbus audio interfaces. + +properties: + compatible: + const: slim217,250 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpios: + description: GPIO spec for reset line to use + maxItems: 1 + + slim-ifc-dev: + description: SLIMBus Interface device phandle + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: extclk + + vdd-buck-supply: + description: A reference to the 1.8V buck supply + + vdd-buck-sido-supply: + description: A reference to the 1.8V SIDO buck supply + + vdd-rx-supply: + description: A reference to the 1.8V rx supply + + vdd-tx-supply: + description: A reference to the 1.8V tx supply + + vdd-vbat-supply: + description: A reference to the vbat supply + + vdd-io-supply: + description: A reference to the 1.8V I/O supply + + vdd-micbias-supply: + description: A reference to the micbias supply + + qcom,micbias1-millivolt: + description: Voltage betwee 1800mv-2850mv for micbias1 output + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + + qcom,micbias2-millivolt: + description: Voltage betwee 1800mv-2850mv for micbias2 output + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + + qcom,micbias3-millivolt: + description: Voltage betwee 1800mv-2850mv for micbias3 output + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + + qcom,micbias4-millivolt: + description: Voltage betwee 1800mv-2850mv for micbias4 output + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + + clock-output-names: + const: mclk + + clock-frequency: + description: Clock frequency of output clk in Hz + + interrupt-controller: true + + '#interrupt-cells': + const: 1 + + '#clock-cells': + const: 0 + + '#sound-dai-cells': + const: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + +patternProperties: + "^.*@[0-9a-f]+$": + type: object + description: | + WCD934x subnode for each slave devices. Bindings of each subnodes + depends on the specific driver providing the functionality and + documented in there respective bindings. + + properties: + reg: + maxItems: 1 + + required: + - reg + +required: + - compatible + - reg + - reset-gpios + - slim-ifc-dev + - interrupts + - interrupt-controller + - clock-frequency + - clock-output-names + - qcom,micbias1-millivolt + - qcom,micbias2-millivolt + - qcom,micbias3-millivolt + - qcom,micbias4-millivolt + - "#interrupt-cells" + - "#clock-cells" + - "#sound-dai-cells" + - "#address-cells" + - "#size-cells" + +examples: + - | + codec@1,0{ + compatible = "slim217,250"; + reg = <1 0>; + reset-gpios = <&tlmm 64 0>; + slim-ifc-dev = <&wcd9340_ifd>; + #sound-dai-cells = <1>; + interrupt-parent = <&tlmm>; + interrupts = <54 4>; + interrupt-controller; + #interrupt-cells = <1>; + #clock-cells = <0>; + clock-frequency = <9600000>; + clock-output-names = "mclk"; + qcom,micbias1-millivolt = <1800>; + qcom,micbias2-millivolt = <1800>; + qcom,micbias3-millivolt = <1800>; + qcom,micbias4-millivolt = <1800>; + clock-names = "extclk"; + clocks = <&rpmhcc 2>; + + #address-cells = <1>; + #size-cells = <1>; + + wcdpinctrl@42 { + reg = <0x42 0x2>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml new file mode 100644 index 000000000000..ea44d03e58ca --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,wsa881x.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,wsa881x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bindings for Qualcomm WSA8810/WSA8815 Class-D Smart Speaker Amplifier + +maintainers: + - Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + +description: | + WSA8810 is a class-D smart speaker amplifier and WSA8815 + is a high-output power class-D smart speaker amplifier. + Their primary operating mode uses a SoundWire digital audio + interface. This binding is for SoundWire interface. + +properties: + compatible: + const: sdw10217201000 + + reg: + maxItems: 1 + + powerdown-gpios: + description: GPIO spec for Powerdown/Shutdown line to use + maxItems: 1 + + '#thermal-sensor-cells': + const: 0 + + '#sound-dai-cells': + const: 0 + +required: + - compatible + - reg + - powerdown-gpios + - "#thermal-sensor-cells" + - "#sound-dai-cells" + +additionalProperties: false + +examples: + - | + soundwire@c2d0000 { + #address-cells = <2>; + #size-cells = <0>; + reg = <0x0c2d0000 0x2000>; + + speaker@0,1 { + compatible = "sdw10217201000"; + reg = <0 1>; + powerdown-gpios = <&wcdpinctrl 2 0>; + #thermal-sensor-cells = <0>; + #sound-dai-cells = <0>; + }; + + speaker@0,2 { + compatible = "sdw10217201000"; + reg = <0 2>; + powerdown-gpios = <&wcdpinctrl 2 0>; + #thermal-sensor-cells = <0>; + #sound-dai-cells = <0>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/soundwire/qcom,sdw.txt b/Documentation/devicetree/bindings/soundwire/qcom,sdw.txt new file mode 100644 index 000000000000..436547f3b155 --- /dev/null +++ b/Documentation/devicetree/bindings/soundwire/qcom,sdw.txt @@ -0,0 +1,167 @@ +Qualcomm SoundWire Controller Bindings + + +This binding describes the Qualcomm SoundWire Controller along with its +board specific bus parameters. + +- compatible: + Usage: required + Value type: <stringlist> + Definition: must be "qcom,soundwire-v<MAJOR>.<MINOR>.<STEP>", + Example: + "qcom,soundwire-v1.3.0" + "qcom,soundwire-v1.5.0" + "qcom,soundwire-v1.6.0" +- reg: + Usage: required + Value type: <prop-encoded-array> + Definition: the base address and size of SoundWire controller + address space. + +- interrupts: + Usage: required + Value type: <prop-encoded-array> + Definition: should specify the SoundWire Controller IRQ + +- clock-names: + Usage: required + Value type: <stringlist> + Definition: should be "iface" for SoundWire Controller interface clock + +- clocks: + Usage: required + Value type: <prop-encoded-array> + Definition: should specify the SoundWire Controller interface clock + +- #sound-dai-cells: + Usage: required + Value type: <u32> + Definition: must be 1 for digital audio interfaces on the controller. + +- qcom,dout-ports: + Usage: required + Value type: <u32> + Definition: must be count of data out ports + +- qcom,din-ports: + Usage: required + Value type: <u32> + Definition: must be count of data in ports + +- qcom,ports-offset1: + Usage: required + Value type: <prop-encoded-array> + Definition: should specify payload transport window offset1 of each + data port. Out ports followed by In ports. + More info in MIPI Alliance SoundWire 1.0 Specifications. + +- qcom,ports-offset2: + Usage: required + Value type: <prop-encoded-array> + Definition: should specify payload transport window offset2 of each + data port. Out ports followed by In ports. + More info in MIPI Alliance SoundWire 1.0 Specifications. + +- qcom,ports-sinterval-low: + Usage: required + Value type: <prop-encoded-array> + Definition: should be sample interval low of each data port. + Out ports followed by In ports. Used for Sample Interval + calculation. + More info in MIPI Alliance SoundWire 1.0 Specifications. + +- qcom,ports-word-length: + Usage: optional + Value type: <prop-encoded-array> + Definition: should be size of payload channel sample. + More info in MIPI Alliance SoundWire 1.0 Specifications. + +- qcom,ports-block-pack-mode: + Usage: optional + Value type: <prop-encoded-array> + Definition: should be 0 or 1 to indicate the block packing mode. + 0 to indicate Blocks are per Channel + 1 to indicate Blocks are per Port. + Out ports followed by In ports. + More info in MIPI Alliance SoundWire 1.0 Specifications. + +- qcom,ports-block-group-count: + Usage: optional + Value type: <prop-encoded-array> + Definition: should be in range 1 to 4 to indicate how many sample + intervals are combined into a payload. + Out ports followed by In ports. + More info in MIPI Alliance SoundWire 1.0 Specifications. + +- qcom,ports-lane-control: + Usage: optional + Value type: <prop-encoded-array> + Definition: should be in range 0 to 7 to identify which data lane + the data port uses. + Out ports followed by In ports. + More info in MIPI Alliance SoundWire 1.0 Specifications. + +- qcom,ports-hstart: + Usage: optional + Value type: <prop-encoded-array> + Definition: should be number identifying lowerst numbered coloum in + SoundWire Frame, i.e. left edge of the Transport sub-frame + for each port. Values between 0 and 15 are valid. + Out ports followed by In ports. + More info in MIPI Alliance SoundWire 1.0 Specifications. + +- qcom,ports-hstop: + Usage: optional + Value type: <prop-encoded-array> + Definition: should be number identifying highest numbered coloum in + SoundWire Frame, i.e. the right edge of the Transport + sub-frame for each port. Values between 0 and 15 are valid. + Out ports followed by In ports. + More info in MIPI Alliance SoundWire 1.0 Specifications. + +- qcom,dports-type: + Usage: optional + Value type: <prop-encoded-array> + Definition: should be one of the following types + 0 for reduced port + 1 for simple ports + 2 for full port + Out ports followed by In ports. + More info in MIPI Alliance SoundWire 1.0 Specifications. + +Note: + More Information on detail of encoding of these fields can be +found in MIPI Alliance SoundWire 1.0 Specifications. + += SoundWire devices +Each subnode of the bus represents SoundWire device attached to it. +The properties of these nodes are defined by the individual bindings. + += EXAMPLE +The following example represents a SoundWire controller on DB845c board +which has controller integrated inside WCD934x codec on SDM845 SoC. + +soundwire: soundwire@c85 { + compatible = "qcom,soundwire-v1.3.0"; + reg = <0xc85 0x20>; + interrupts = <20 IRQ_TYPE_EDGE_RISING>; + clocks = <&wcc>; + clock-names = "iface"; + #sound-dai-cells = <1>; + qcom,dports-type = <0>; + qcom,dout-ports = <6>; + qcom,din-ports = <2>; + qcom,ports-sinterval-low = /bits/ 8 <0x07 0x1F 0x3F 0x7 0x1F 0x3F 0x0F 0x0F>; + qcom,ports-offset1 = /bits/ 8 <0x01 0x02 0x0C 0x6 0x12 0x0D 0x07 0x0A >; + qcom,ports-offset2 = /bits/ 8 <0x00 0x00 0x1F 0x00 0x00 0x1F 0x00 0x00>; + + /* Left Speaker */ + left{ + .... + }; + + /* Right Speaker */ + right{ + .... + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts index ded120d3aef5..d3c841628d66 100644 --- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts +++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts @@ -20,6 +20,11 @@ }; }; +&adsp_pas { + firmware-name = "qcom/c630/qcadsp850.mbn"; + status = "okay"; +}; + &apps_rsc { pm8998-rpmh-regulators { compatible = "qcom,pm8998-rpmh-regulators"; @@ -229,6 +234,11 @@ status = "disabled"; }; +&cdsp_pas { + firmware-name = "qcom/c630/qccdsp850.mbn"; + status = "okay"; +}; + &gcc { protected-clocks = <GCC_QSPI_CORE_CLK>, <GCC_QSPI_CORE_CLK_SRC>, @@ -296,6 +306,10 @@ }; }; +&mss_pil { + firmware-name = "qcom/c630/qcdsp1v2850.mbn", "qcom/c630/qcdsp2850.mbn"; +}; + &qup_i2c12_default { drive-strength = <2>; bias-disable; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ae24d3ea68ea..ab09862b5996 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1967,6 +1967,14 @@ config MFD_STMFX additional drivers must be enabled in order to use the functionality of the device. +config MFD_WCD934X + tristate "Support for WCD9340/WCD9341 Codec" + depends on SLIMBUS + select REGMAP + select REGMAP_SLIMBUS + select REGMAP_IRQ + select MFD_CORE + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c1067ea46204..8059a9c36188 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -58,6 +58,7 @@ endif ifeq ($(CONFIG_MFD_CS47L24),y) obj-$(CONFIG_MFD_ARIZONA) += cs47l24-tables.o endif +obj-$(CONFIG_MFD_WCD934X) += wcd934x.o obj-$(CONFIG_MFD_WM8400) += wm8400-core.o wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o wm831x-objs += wm831x-auxadc.o diff --git a/drivers/mfd/wcd934x.c b/drivers/mfd/wcd934x.c new file mode 100644 index 000000000000..bb4d2a6c89bc --- /dev/null +++ b/drivers/mfd/wcd934x.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019, Linaro Limited + +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/core.h> +#include <linux/mfd/wcd934x/registers.h> +#include <linux/mfd/wcd934x/wcd934x.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slimbus.h> + +static const struct mfd_cell wcd934x_devices[] = { + { /* Audio Codec */ + .name = "wcd934x-codec", + }, { /* Pin controller */ + .name = "wcd934x-pinctrl", + .of_compatible = "qcom,wcd9340-pinctrl", + }, { /* Soundwire Controller */ + .name = "wcd934x-soundwire", + .of_compatible = "qcom,soundwire-v1.3.0", + }, +}; + +static const struct regmap_irq wcd934x_irqs[] = { + /* INTR_REG 0 */ + [WCD934X_IRQ_SLIMBUS] = { + .reg_offset = 0, + .mask = BIT(0), + .type = { + .type_reg_offset = 0, + .types_supported = IRQ_TYPE_EDGE_BOTH, + .type_reg_mask = BIT(0), + .type_level_low_val = BIT(0), + .type_level_high_val = BIT(0), + .type_falling_val = 0, + .type_rising_val = 0, + }, + }, + [WCD934X_IRQ_SOUNDWIRE] = { + .reg_offset = 2, + .mask = BIT(4), + .type = { + .type_reg_offset = 2, + .types_supported = IRQ_TYPE_EDGE_BOTH, + .type_reg_mask = BIT(4), + .type_level_low_val = BIT(4), + .type_level_high_val = BIT(4), + .type_falling_val = 0, + .type_rising_val = 0, + }, + }, +}; + +static const struct regmap_irq_chip wcd934x_regmap_irq_chip = { + .name = "wcd934x_irq", + .status_base = WCD934X_INTR_PIN1_STATUS0, + .mask_base = WCD934X_INTR_PIN1_MASK0, + .ack_base = WCD934X_INTR_PIN1_CLEAR0, + .type_base = WCD934X_INTR_LEVEL0, + .num_type_reg = 4, + .type_in_mask = false, + .num_regs = 4, + .irqs = wcd934x_irqs, + .num_irqs = ARRAY_SIZE(wcd934x_irqs), +}; + +static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD934X_INTR_PIN1_STATUS0...WCD934X_INTR_PIN2_CLEAR3: + case WCD934X_SWR_AHB_BRIDGE_RD_DATA_0: + case WCD934X_SWR_AHB_BRIDGE_RD_DATA_1: + case WCD934X_SWR_AHB_BRIDGE_RD_DATA_2: + case WCD934X_SWR_AHB_BRIDGE_RD_DATA_3: + case WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS: + case WCD934X_ANA_MBHC_RESULT_3: + case WCD934X_ANA_MBHC_RESULT_2: + case WCD934X_ANA_MBHC_RESULT_1: + case WCD934X_ANA_MBHC_MECH: + case WCD934X_ANA_MBHC_ELECT: + case WCD934X_ANA_MBHC_ZDET: + case WCD934X_ANA_MICB2: + case WCD934X_ANA_RCO: + case WCD934X_ANA_BIAS: + return true; + default: + return false; + } + +}; + +static const struct regmap_range_cfg wcd934x_ranges[] = { + { .name = "WCD934X", + .range_min = 0x0, + .range_max = WCD934X_MAX_REGISTER, + .selector_reg = WCD934X_SEL_REGISTER, + .selector_mask = WCD934X_SEL_MASK, + .selector_shift = WCD934X_SEL_SHIFT, + .window_start = WCD934X_WINDOW_START, + .window_len = WCD934X_WINDOW_LENGTH, + }, +}; + +static struct regmap_config wcd934x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = 0xffff, + .can_multi_write = true, + .ranges = wcd934x_ranges, + .num_ranges = ARRAY_SIZE(wcd934x_ranges), + .volatile_reg = wcd934x_is_volatile_register, +}; + +static int wcd934x_parse_resources(struct wcd934x_data *wcd) +{ + struct device *dev = wcd->dev; + struct device_node *np = dev->of_node; + int ret; + + wcd->irq = of_irq_get(wcd->dev->of_node, 0); + if (wcd->irq < 0) { + if (wcd->irq != -EPROBE_DEFER) + dev_err(wcd->dev, "Failed to get IRQ: err = %d\n", + wcd->irq); + return wcd->irq; + } + + wcd->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); + if (wcd->reset_gpio < 0) { + dev_err(dev, "Failed to get reset gpio: err = %d\n", + wcd->reset_gpio); + return wcd->reset_gpio; + } + + wcd->extclk = devm_clk_get(dev, "extclk"); + if (IS_ERR(wcd->extclk)) { + dev_err(dev, "Failed to get extclk"); + return PTR_ERR(wcd->extclk); + } + + wcd->supplies[0].supply = "vdd-buck"; + wcd->supplies[1].supply = "vdd-buck-sido"; + wcd->supplies[2].supply = "vdd-tx"; + wcd->supplies[3].supply = "vdd-rx"; + wcd->supplies[4].supply = "vdd-io"; + + ret = regulator_bulk_get(dev, WCD934X_MAX_SUPPLY, wcd->supplies); + if (ret != 0) { + dev_err(dev, "Failed to get supplies: err = %d\n", ret); + return ret; + } + + return 0; +} + +static int wcd934x_power_on_reset(struct wcd934x_data *wcd) +{ + int ret; + + ret = regulator_bulk_enable(WCD934X_MAX_SUPPLY, wcd->supplies); + if (ret != 0) { + dev_err(wcd->dev, "Failed to get supplies: err = %d\n", ret); + return ret; + } + + /* + * For WCD934X, it takes about 600us for the Vout_A and + * Vout_D to be ready after BUCK_SIDO is powered up. + * SYS_RST_N shouldn't be pulled high during this time + */ + usleep_range(600, 650); + gpio_direction_output(wcd->reset_gpio, 0); + msleep(20); + gpio_set_value(wcd->reset_gpio, 1); + msleep(20); + + return 0; +} + +static int wcd934x_slim_probe(struct slim_device *sdev) +{ + struct device *dev = &sdev->dev; + struct wcd934x_data *wcd; + int ret = 0; + + wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL); + if (!wcd) + return -ENOMEM; + + wcd->dev = dev; + ret = wcd934x_parse_resources(wcd); + if (ret) { + dev_err(dev, "Error parsing DT: err = %d\n", ret); + return ret; + } + + ret = wcd934x_power_on_reset(wcd); + if (ret) { + dev_err(dev, "Error Power resetting device: err = %d\n", ret); + return ret; + } + + wcd->sdev = sdev; + dev_set_drvdata(dev, wcd); + + return 0; +} + +static int wcd934x_bring_up(struct wcd934x_data *wcd) +{ + struct regmap *rm = wcd->regmap; + u16 id_minor, id_major; + int ret; + + ret = regmap_bulk_read(rm, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, + (u8 *)&id_minor, sizeof(u16)); + if (ret) + return -EINVAL; + + ret = regmap_bulk_read(rm, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2, + (u8 *)&id_major, sizeof(u16)); + if (ret) + return -EINVAL; + + dev_info(wcd->dev, "wcd934x chip id major 0x%x, minor 0x%x\n", + id_major, id_minor); + + regmap_write(rm, WCD934X_CODEC_RPM_RST_CTL, 0x01); + regmap_write(rm, WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x19); + regmap_write(rm, WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x15); + /* Add 1msec delay for VOUT to settle */ + usleep_range(1000, 1100); + regmap_write(rm, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5); + regmap_write(rm, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7); + regmap_write(rm, WCD934X_CODEC_RPM_RST_CTL, 0x3); + regmap_write(rm, WCD934X_CODEC_RPM_RST_CTL, 0x7); + regmap_write(rm, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3); + + return 0; + +} + +static int wcd934x_slim_status(struct slim_device *sdev, + enum slim_device_status status) +{ + struct device *dev = &sdev->dev; + struct wcd934x_data *wcd; + int ret; + + wcd = dev_get_drvdata(dev); + + switch (status) { + case SLIM_DEVICE_STATUS_UP: + wcd->regmap = regmap_init_slimbus(sdev, &wcd934x_regmap_config); + if (IS_ERR(wcd->regmap)) { + dev_err(dev, "Error allocating slim regmap\n"); + return PTR_ERR(wcd->regmap); + } + + ret = wcd934x_bring_up(wcd); + if (ret) { + dev_err(dev, "Error WCD934X bringup: err = %d\n", ret); + return ret; + } + + ret = devm_regmap_add_irq_chip(wcd->dev, wcd->regmap, wcd->irq, + IRQF_TRIGGER_HIGH, 0, + &wcd934x_regmap_irq_chip, + &wcd->irq_data); + if (ret) { + dev_err(wcd->dev, "Error adding irq_chip: err = %d\n", + ret); + return ret; + } + + ret = mfd_add_devices(wcd->dev, 0, wcd934x_devices, + ARRAY_SIZE(wcd934x_devices), + NULL, 0, NULL); + if (ret) { + dev_err(wcd->dev, "Error add mfd devices: err = %d\n", + ret); + return ret; + } + break; + case SLIM_DEVICE_STATUS_DOWN: + mfd_remove_devices(wcd->dev); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void wcd934x_slim_remove(struct slim_device *sdev) +{ + struct wcd934x_data *wcd = dev_get_drvdata(&sdev->dev); + + mfd_remove_devices(&sdev->dev); + kfree(wcd); +} + +static const struct slim_device_id wcd934x_slim_id[] = { + {SLIM_MANF_ID_QCOM, SLIM_PROD_CODE_WCD9340, 0x1, 0x0}, + {} +}; + +static struct slim_driver wcd934x_slim_driver = { + .driver = { + .name = "wcd934x-slim", + }, + .probe = wcd934x_slim_probe, + .remove = wcd934x_slim_remove, + .device_status = wcd934x_slim_status, + .id_table = wcd934x_slim_id, +}; + +module_slim_driver(wcd934x_slim_driver); +MODULE_DESCRIPTION("WCD934X slim driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("slim:217:250:*"); diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 32fc2458b8eb..7406df5fb89a 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -195,4 +195,11 @@ config PINCTRL_SM8150 Qualcomm Technologies Inc TLMM block found on the Qualcomm Technologies Inc SM8150 platform. +config PINCTRL_WCD934X + tristate "Qualcomm Technologies Inc WCD9340/WCD9341 pin controller driver" + depends on GPIOLIB && OF + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc Pinctrl block found on the Qualcomm + Technologies Inc WCD9340/WCD9341 Audio Codec. endif diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index f8bb0c265381..8c42ba1bdd69 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_PINCTRL_SC7180) += pinctrl-sc7180.o obj-$(CONFIG_PINCTRL_SDM660) += pinctrl-sdm660.o obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o obj-$(CONFIG_PINCTRL_SM8150) += pinctrl-sm8150.o +obj-$(CONFIG_PINCTRL_WCD934X) += pinctrl-wcd934x-gpio.o diff --git a/drivers/pinctrl/qcom/pinctrl-wcd934x-gpio.c b/drivers/pinctrl/qcom/pinctrl-wcd934x-gpio.c new file mode 100644 index 000000000000..1aff88d0bcb3 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-wcd934x-gpio.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +// Copyright (c) 2019, Linaro Limited + +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> + +#include "../core.h" +#include "../pinctrl-utils.h" + +#define WCD_REG_DIR_CTL_OFFSET 0x42 +#define WCD_REG_VAL_CTL_OFFSET 0x43 +#define WCD_GPIO_PULL_UP 1 +#define WCD_GPIO_PULL_DOWN 2 +#define WCD_GPIO_BIAS_DISABLE 3 +#define WCD_GPIO_STRING_LEN 20 +#define WCD934X_NPINS 5 + +/** + * struct wcd_gpio_pad - keep current GPIO settings + * @offset: offset of gpio. + * @is_valid: Set to false, when GPIO in high Z state. + * @value: value of a pin + * @output_enabled: Set to true if GPIO is output and false if it is input + * @pullup: Constant current which flow through GPIO output buffer. + * @strength: Drive strength of a pin + */ +struct wcd_gpio_pad { + u16 offset; + bool is_valid; + bool value; + bool output_enabled; + unsigned int pullup; + unsigned int strength; +}; + +struct wcd_gpio_priv { + struct device *dev; + struct regmap *map; + struct pinctrl_dev *ctrl; + struct gpio_chip chip; +}; + +static int wcd_gpio_read(struct wcd_gpio_priv *priv_data, + struct wcd_gpio_pad *pad, unsigned int addr) +{ + unsigned int val; + int ret; + + ret = regmap_read(priv_data->map, addr, &val); + if (ret < 0) + dev_err(priv_data->dev, "%s: read 0x%x failed\n", + __func__, addr); + else + ret = (val >> pad->offset); + + return ret; +} + +static int wcd_gpio_write(struct wcd_gpio_priv *priv_data, + struct wcd_gpio_pad *pad, unsigned int addr, + unsigned int val) +{ + int ret; + + ret = regmap_update_bits(priv_data->map, addr, (1 << pad->offset), + val << pad->offset); + if (ret < 0) + dev_err(priv_data->dev, "write 0x%x failed\n", addr); + + return ret; +} + +static int wcd_get_groups_count(struct pinctrl_dev *pctldev) +{ + return pctldev->desc->npins; +} + +static const char *wcd_get_group_name(struct pinctrl_dev *pctldev, + unsigned int pin) +{ + return pctldev->desc->pins[pin].name; +} + +static int wcd_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 wcd_pinctrl_ops = { + .get_groups_count = wcd_get_groups_count, + .get_group_name = wcd_get_group_name, + .get_group_pins = wcd_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int wcd_config_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + unsigned int param = pinconf_to_config_param(*config); + struct wcd_gpio_pad *pad; + unsigned int arg; + + pad = pctldev->desc->pins[pin].drv_data; + + switch (param) { + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = pad->pullup == WCD_GPIO_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_DISABLE: + arg = pad->pullup = WCD_GPIO_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + arg = pad->pullup == WCD_GPIO_PULL_UP; + break; + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + arg = !pad->is_valid; + break; + case PIN_CONFIG_INPUT_ENABLE: + arg = pad->output_enabled; + break; + case PIN_CONFIG_OUTPUT: + arg = pad->value; + break; + default: + return -EINVAL; + } + + *config = pinconf_to_config_packed(param, arg); + return 0; +} + +static int wcd_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int nconfs) +{ + struct wcd_gpio_priv *priv_data = pinctrl_dev_get_drvdata(pctldev); + struct wcd_gpio_pad *pad; + unsigned int param, arg; + int i, ret; + + pad = pctldev->desc->pins[pin].drv_data; + + 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: + pad->pullup = WCD_GPIO_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + pad->pullup = WCD_GPIO_PULL_UP; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + pad->pullup = WCD_GPIO_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + pad->is_valid = false; + break; + case PIN_CONFIG_INPUT_ENABLE: + pad->output_enabled = false; + break; + case PIN_CONFIG_OUTPUT: + pad->output_enabled = true; + pad->value = arg; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + pad->strength = arg; + break; + default: + return -EINVAL; + } + } + + ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL_OFFSET, + pad->output_enabled); + if (ret < 0) + return ret; + + if (pad->output_enabled) + ret = wcd_gpio_write(priv_data, pad, WCD_REG_VAL_CTL_OFFSET, + pad->value); + + return ret; +} + +static const struct pinconf_ops wcd_pinconf_ops = { + .is_generic = true, + .pin_config_group_get = wcd_config_get, + .pin_config_group_set = wcd_config_set, +}; + +static int wcd_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) +{ + struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); + + return wcd_config_set(priv_data->ctrl, pin, &config, 1); +} + +static int wcd_gpio_direction_output(struct gpio_chip *chip, + unsigned int pin, int val) +{ + struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); + + return wcd_config_set(priv_data->ctrl, pin, &config, 1); +} + +static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin) +{ + struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip); + struct wcd_gpio_pad *pad; + int value; + + pad = priv_data->ctrl->desc->pins[pin].drv_data; + + if (!pad->is_valid) + return -EINVAL; + + value = wcd_gpio_read(priv_data, pad, WCD_REG_VAL_CTL_OFFSET); + + return value; +} + +static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) +{ + struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); + + wcd_config_set(priv_data->ctrl, pin, &config, 1); +} + +static const struct gpio_chip wcd_gpio_chip = { + .direction_input = wcd_gpio_direction_input, + .direction_output = wcd_gpio_direction_output, + .get = wcd_gpio_get, + .set = wcd_gpio_set, +}; + +static int wcd_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pinctrl_pin_desc *pindesc; + struct pinctrl_desc *pctrldesc; + struct wcd_gpio_pad *pad, *pads; + struct wcd_gpio_priv *priv_data; + u32 npins = WCD934X_NPINS; + char **name; + int i; + + priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL); + if (!priv_data) + return -ENOMEM; + + priv_data->dev = dev; + priv_data->map = dev_get_regmap(dev->parent, NULL); + if (!priv_data->map) { + dev_err(dev, "%s: failed to get regmap\n", __func__); + return -EINVAL; + } + + pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); + if (!pindesc) + return -ENOMEM; + + pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL); + if (!pads) + return -ENOMEM; + + pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); + if (!pctrldesc) + return -ENOMEM; + + pctrldesc->pctlops = &wcd_pinctrl_ops; + pctrldesc->confops = &wcd_pinconf_ops; + pctrldesc->owner = THIS_MODULE; + pctrldesc->name = dev_name(dev); + pctrldesc->pins = pindesc; + pctrldesc->npins = npins; + + name = devm_kcalloc(dev, npins, sizeof(char *), GFP_KERNEL); + if (!name) + return -ENOMEM; + + for (i = 0; i < npins; i++, pindesc++) { + name[i] = devm_kzalloc(dev, sizeof(char) * WCD_GPIO_STRING_LEN, + GFP_KERNEL); + if (!name[i]) + return -ENOMEM; + + pad = &pads[i]; + pindesc->drv_data = pad; + pindesc->number = i; + snprintf(name[i], (WCD_GPIO_STRING_LEN - 1), "gpio%d", (i+1)); + pindesc->name = name[i]; + pad->offset = i; + pad->is_valid = true; + } + + priv_data->chip = wcd_gpio_chip; + priv_data->chip.parent = dev; + priv_data->chip.base = -1; + priv_data->chip.ngpio = npins; + priv_data->chip.label = dev_name(dev); + priv_data->chip.of_gpio_n_cells = 2; + priv_data->chip.can_sleep = false; + platform_set_drvdata(pdev, priv_data); + + priv_data->ctrl = devm_pinctrl_register(dev, pctrldesc, priv_data); + if (IS_ERR(priv_data->ctrl)) { + dev_err(dev, "%s: failed to register to pinctrl\n", __func__); + return PTR_ERR(priv_data->ctrl); + } + + return gpiochip_add_data(&priv_data->chip, priv_data); +} + +static int wcd_pinctrl_remove(struct platform_device *pdev) +{ + struct wcd_gpio_priv *priv_data = platform_get_drvdata(pdev); + + gpiochip_remove(&priv_data->chip); + + return 0; +} + +static const struct of_device_id wcd_pinctrl_of_match[] = { + { .compatible = "qcom,wcd9340-pinctrl" }, + { .compatible = "qcom,wcd9341-pinctrl" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, wcd_pinctrl_of_match); + +static struct platform_driver wcd_pinctrl_driver = { + .driver = { + .name = "wcd934x-pinctrl", + .of_match_table = wcd_pinctrl_of_match, + }, + .probe = wcd_pinctrl_probe, + .remove = wcd_pinctrl_remove, +}; + +module_platform_driver(wcd_pinctrl_driver); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc WCD GPIO pin control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index 29fbab55c3b3..9c498525f2fd 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -1316,6 +1316,9 @@ static const struct of_device_id qcom_slim_ngd_dt_match[] = { { .compatible = "qcom,slim-ngd-v1.5.0", .data = &ngd_v1_5_offset_info, + },{ + .compatible = "qcom,slim-ngd-v2.1.0", + .data = &ngd_v1_5_offset_info, }, {} }; diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c index 4fcc32420c47..716aae9646dc 100644 --- a/drivers/soc/qcom/apr.c +++ b/drivers/soc/qcom/apr.c @@ -11,9 +11,21 @@ #include <linux/workqueue.h> #include <linux/of_device.h> #include <linux/soc/qcom/apr.h> +#include <linux/soc/qcom/qmi.h> +#include <linux/delay.h> #include <linux/rpmsg.h> #include <linux/of.h> +#define SERVER_TIMEOUT 500 +#define SERVREG_NOTIF_SERVICE 0x42 +#define SERVREG_NOTIF_STATE_UPDATED_IND_MSG 0x22 +#define SERVREG_NOTIF_REGISTER_LISTENER_REQ 0x20 +#define QMI_SERVREG_NOTIF_NAME_LENGTH_V01 64 +#define SERVREG_NOTIF_REGISTER_LISTENER_REQ_MSG_LEN 71 +#define QMI_SERVREG_NOTIF_SET_ACK_RESP_MSG_V01_MAX_MSG_LEN 7 +#define SERVREG_NOTIF_SET_ACK_REQ_MSG_LEN 72 +#define SERVREG_NOTIF_SET_ACK_REQ 0x23 + struct apr { struct rpmsg_endpoint *ch; struct device *dev; @@ -23,7 +35,12 @@ struct apr { int dest_domain_id; struct workqueue_struct *rxwq; struct work_struct rx_work; + struct workqueue_struct *indackwq; struct list_head rx_list; + struct completion comp; + struct completion ind_comp; + struct qmi_handle qmi; + struct sockaddr_qrtr servreg_sock; }; struct apr_rx_buf { @@ -32,6 +49,248 @@ struct apr_rx_buf { uint8_t buf[]; }; +struct ind_req_resp { + int transaction_id; + struct work_struct ind_ack; + struct apr *client_data; +}; + +struct qmi_servreg_notif_state_updated_ind_msg_v01 { + u32 curr_state; + char service_name[QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1]; + uint16_t transaction_id; +}; + +struct qmi_elem_info qmi_servreg_notif_state_updated_ind_msg_v01_ei[] = { + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_servreg_notif_state_updated_ind_msg_v01, curr_state), + }, + { + .data_type = QMI_STRING, + .elem_len = QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_servreg_notif_state_updated_ind_msg_v01, service_name), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .array_type = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct qmi_servreg_notif_state_updated_ind_msg_v01, transaction_id), + }, + {} +}; + +struct qmi_servreg_notif_register_listener_req_msg_v01 { + uint8_t enable; + char service_name[QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1]; +}; + +struct qmi_elem_info qmi_servreg_notif_register_listener_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_servreg_notif_register_listener_req_msg_v01, enable), + }, + { + .data_type = QMI_STRING, + .elem_len = QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_servreg_notif_register_listener_req_msg_v01, service_name), + }, + {} +}; + +struct qmi_servreg_notif_register_listener_resp_msg_v01 { + struct qmi_response_type_v01 resp; + uint8_t curr_state_valid; + u32 curr_state; +}; + +struct qmi_elem_info qmi_servreg_notif_register_listener_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_servreg_notif_register_listener_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_servreg_notif_register_listener_resp_msg_v01, curr_state_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_servreg_notif_register_listener_resp_msg_v01, curr_state), + }, + {} +}; + +struct qmi_servreg_notif_set_ack_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +struct qmi_elem_info qmi_servreg_notif_set_ack_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_servreg_notif_set_ack_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + {} +}; + +struct qmi_servreg_notif_set_ack_req_msg_v01 { + char service_name[QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1]; + uint16_t transaction_id; +}; + +struct qmi_elem_info qmi_servreg_notif_set_ack_req_msg_v01_ei[] = { + { + .data_type = QMI_STRING, + .elem_len = QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_servreg_notif_set_ack_req_msg_v01, service_name), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_servreg_notif_set_ack_req_msg_v01, transaction_id), + }, + {} +}; + +static void servreg_ind_ack(struct work_struct *work) { + struct ind_req_resp *ind_info = container_of(work, struct ind_req_resp, ind_ack); + struct qmi_servreg_notif_set_ack_resp_msg_v01 resp = { { 0, 0 } }; + struct qmi_servreg_notif_set_ack_req_msg_v01 req; + struct apr *apr = ind_info->client_data; + struct qmi_txn txn; + int ret; + + ret = qmi_txn_init(&apr->qmi, &txn, qmi_servreg_notif_set_ack_resp_msg_v01_ei, + &resp); + if (ret < 0) { + pr_err("QMI tx init failed , ret - %d\n", ret); + return; + } + + req.transaction_id = ind_info->transaction_id; + snprintf(req.service_name, ARRAY_SIZE(req.service_name), "%s", + "msm/adsp/audio_pd"); + + ret = qmi_send_request(&apr->qmi, &apr->servreg_sock, + &txn, SERVREG_NOTIF_SET_ACK_REQ, + SERVREG_NOTIF_SET_ACK_REQ_MSG_LEN, + qmi_servreg_notif_set_ack_req_msg_v01_ei, + &req); + if (ret < 0) { + pr_err("QMI send ACK failed, ret - %d\n", ret); + qmi_txn_cancel(&txn); + return; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(SERVER_TIMEOUT)); + if (ret < 0) { + pr_err("QMI qmi txn wait failed, ret - %d\n", ret); + return; + } + + /* Check the response */ + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) + pr_err("QMI request failed 0x%x\n", resp.resp.error); + else + pr_err("QMI ack success\n"); +} + +static void servreg_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, + struct qmi_txn *back_txn, const void *data) +{ + struct qmi_servreg_notif_state_updated_ind_msg_v01 ind_msg = *((struct qmi_servreg_notif_state_updated_ind_msg_v01 *)data); + struct apr *apr = container_of(qmi, struct apr, qmi); + struct ind_req_resp *ind_info = kmalloc(sizeof(*ind_info), GFP_KERNEL); + + pr_err("Indication received from %s, state: 0x%x, trans-id: %d\n", + ind_msg.service_name, ind_msg.curr_state, ind_msg.transaction_id); + + if (ind_msg.curr_state == 0x1FFFFFFF) + complete(&apr->ind_comp); + + ind_info->transaction_id = ind_msg.transaction_id; + ind_info->client_data = apr; + + INIT_WORK(&ind_info->ind_ack, servreg_ind_ack); + queue_work(apr->indackwq, &ind_info->ind_ack); +} + +static struct qmi_msg_handler qmi_indication_handler[] = { + { + .type = QMI_INDICATION, + .msg_id = SERVREG_NOTIF_STATE_UPDATED_IND_MSG, + .ei = qmi_servreg_notif_state_updated_ind_msg_v01_ei, + .decoded_size = sizeof(struct qmi_servreg_notif_state_updated_ind_msg_v01), + .fn = servreg_ind_cb, + }, + {} +}; + +static int servreg_new_server(struct qmi_handle *qmi, struct qmi_service *serv) +{ + struct apr *apr = container_of(qmi, struct apr, qmi); + + pr_err("servreg_new_server\n"); + apr->servreg_sock.sq_family = AF_QIPCRTR; + apr->servreg_sock.sq_node = serv->node; + apr->servreg_sock.sq_port = serv->port; + complete(&apr->comp); + + return 0; +}; + +static void servreg_del_server(struct qmi_handle *qmi, struct qmi_service *serv) +{ + struct apr *apr = container_of(qmi, struct apr, qmi); + reinit_completion(&apr->comp); + reinit_completion(&apr->ind_comp); + pr_err("servreg_del_server\n"); +}; + +static const struct qmi_ops servreg_ops = { + .new_server = servreg_new_server, + .del_server = servreg_del_server, +}; + /** * apr_send_pkt() - Send a apr message from apr device * @@ -320,7 +579,10 @@ static void of_register_apr_devices(struct device *dev) static int apr_probe(struct rpmsg_device *rpdev) { + struct qmi_servreg_notif_register_listener_resp_msg_v01 resp = { { 0, 0 } }; + struct qmi_servreg_notif_register_listener_req_msg_v01 req; struct device *dev = &rpdev->dev; + struct qmi_txn txn; struct apr *apr; int ret; @@ -333,6 +595,8 @@ static int apr_probe(struct rpmsg_device *rpdev) dev_err(dev, "APR Domain ID not specified in DT\n"); return ret; } + init_completion(&apr->comp); + init_completion(&apr->ind_comp); dev_set_drvdata(dev, apr); apr->ch = rpdev->ept; @@ -343,9 +607,81 @@ static int apr_probe(struct rpmsg_device *rpdev) return -ENOMEM; } INIT_WORK(&apr->rx_work, apr_rxwq); + + apr->indackwq = create_singlethread_workqueue("qcom_apr_indack"); + if (!apr->indackwq) { + dev_err(apr->dev, "Failed to start IndAck WQ\n"); + return -ENOMEM; + } + INIT_LIST_HEAD(&apr->rx_list); spin_lock_init(&apr->rx_lock); spin_lock_init(&apr->svcs_lock); + + /* Wait for PD UP notification */ + ret = qmi_handle_init(&apr->qmi, 79, &servreg_ops, qmi_indication_handler); + if (ret < 0) { + pr_err("apr_probe qmi handle init failed\n"); + } + + qmi_add_lookup(&apr->qmi, SERVREG_NOTIF_SERVICE, 1, 74); + + ret = wait_for_completion_timeout(&apr->comp, 10 * HZ); + if (!ret) + pr_err("timed out waiting for new server\n"); + + ret = qmi_txn_init(&apr->qmi, &txn, + qmi_servreg_notif_register_listener_resp_msg_v01_ei, + &resp); + if (ret < 0) { + pr_err("QMI tx init failed , ret - %d\n", ret); + qmi_handle_release(&apr->qmi); + return ret; + } + + req.enable = true; + snprintf(req.service_name, ARRAY_SIZE(req.service_name), "%s", + "msm/adsp/audio_pd"); + + ret = qmi_send_request(&apr->qmi, &apr->servreg_sock, + &txn, SERVREG_NOTIF_REGISTER_LISTENER_REQ, + SERVREG_NOTIF_REGISTER_LISTENER_REQ_MSG_LEN, + qmi_servreg_notif_register_listener_req_msg_v01_ei, + &req); + if (ret < 0) { + pr_err("QMI send req failed, ret - %d\n", ret); + qmi_txn_cancel(&txn); + qmi_handle_release(&apr->qmi); + return ret; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(SERVER_TIMEOUT)); + if (ret < 0) { + pr_err("QMI qmi txn wait failed, ret - %d\n", ret); + qmi_handle_release(&apr->qmi); + return ret; + } + + /* Check the response */ + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + pr_err("QMI request failed 0x%x\n", resp.resp.error); + qmi_handle_release(&apr->qmi); + return -EREMOTEIO; + } else { + pr_err("QMI request succeeded 0x%x\n", resp.resp.error); + pr_err("Serv Reg current state : 0x%x\n", resp.curr_state); + } + + /* HACK */ + if (resp.curr_state != 0x1FFFFFFF) { + ret = wait_for_completion_timeout(&apr->ind_comp, 10 * HZ); + if (!ret) { + pr_err("timed out waiting for PD UP\n"); + qmi_handle_release(&apr->qmi); + return ret; + } + } + idr_init(&apr->svcs_idr); of_register_apr_devices(dev); @@ -365,6 +701,7 @@ static void apr_remove(struct rpmsg_device *rpdev) { struct apr *apr = dev_get_drvdata(&rpdev->dev); + qmi_handle_release(&apr->qmi); device_for_each_child(&rpdev->dev, NULL, apr_remove_device); flush_workqueue(apr->rxwq); destroy_workqueue(apr->rxwq); diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index f518273cfbe3..69f3b0e36b3d 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -30,4 +30,13 @@ config SOUNDWIRE_INTEL enable this config option to get the SoundWire support for that device. +config SOUNDWIRE_QCOM + tristate "Qualcomm SoundWire Master driver" + depends on SLIMBUS + depends on SND_SOC + help + SoundWire Qualcomm Master driver. + If you have an Qualcomm platform which has a SoundWire Master then + enable this config option to get the SoundWire support for that + device endif diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index 563894e5ecaf..e2cdff990e9f 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -21,3 +21,7 @@ obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o soundwire-intel-init-objs := intel_init.o obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel-init.o + +#Qualcomm driver +soundwire-qcom-objs := qcom.o +obj-$(CONFIG_SOUNDWIRE_QCOM) += soundwire-qcom.o diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fc53dbe57f85..a4bc1746289f 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -334,9 +334,11 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret; - ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) - return ret; + if (pm_runtime_enabled(slave->bus->dev)) { + ret = pm_runtime_get_sync(slave->bus->dev); + if (ret < 0) + return ret; + } ret = sdw_transfer(slave->bus, &msg); pm_runtime_put(slave->bus->dev); @@ -362,9 +364,11 @@ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret; - ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) - return ret; + if (pm_runtime_enabled(slave->bus->dev)) { + ret = pm_runtime_get_sync(slave->bus->dev); + if (ret < 0) + return ret; + } ret = sdw_transfer(slave->bus, &msg); pm_runtime_put(slave->bus->dev); @@ -610,7 +614,7 @@ int sdw_configure_dpn_intr(struct sdw_slave *slave, u32 addr; int ret; u8 val = 0; - +return 0; //HACK to not setup interrupts addr = SDW_DPN_INTMASK(port); /* Set/Clear port ready interrupt mask */ @@ -635,7 +639,7 @@ static int sdw_initialize_slave(struct sdw_slave *slave) struct sdw_slave_prop *prop = &slave->prop; int ret; u8 val; - +return 0; //HACK to not setup interrupts /* * Set bus clash, parity and SCP implementation * defined interrupt mask diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c new file mode 100644 index 000000000000..89cebc64386e --- /dev/null +++ b/drivers/soundwire/qcom.c @@ -0,0 +1,935 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019, Linaro Limited + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/slimbus.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "bus.h" + +#define SWRM_COMP_HW_VERSION 0x00 +#define SWRM_COMP_CFG_ADDR 0x04 +#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK BIT(1) +#define SWRM_COMP_CFG_ENABLE_MSK BIT(0) +#define SWRM_COMP_PARAMS 0x100 +#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK GENMASK(4, 0) +#define SWRM_COMP_PARAMS_DIN_PORTS_MASK GENMASK(9, 5) +#define SWRM_INTERRUPT_STATUS 0x200 +#define SWRM_INTERRUPT_STATUS_RMSK GENMASK(16, 0) +#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED BIT(1) +#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS BIT(2) +#define SWRM_INTERRUPT_STATUS_CMD_ERROR BIT(7) +#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED BIT(10) +#define SWRM_INTERRUPT_MASK_ADDR 0x204 +#define SWRM_INTERRUPT_CLEAR 0x208 +#define SWRM_CMD_FIFO_WR_CMD 0x300 +#define SWRM_CMD_FIFO_RD_CMD 0x304 +#define SWRM_CMD_FIFO_CMD 0x308 +#define SWRM_CMD_FIFO_STATUS 0x30C +#define SWRM_CMD_FIFO_CFG_ADDR 0x314 +#define SWRM_RD_WR_CMD_RETRIES 0x7 +#define SWRM_CMD_FIFO_RD_FIFO_ADDR 0x318 +#define SWRM_ENUMERATOR_CFG_ADDR 0x500 +#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m) (0x101C + 0x40 * (m)) +#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT 3 +#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK GENMASK(2, 0) +#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK GENMASK(7, 3) +#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT 0 +#define SWRM_MCP_CFG_ADDR 0x1048 +#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK GENMASK(21, 17) +#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT 0x11 +#define SWRM_DEF_CMD_NO_PINGS 0x1f +#define SWRM_MCP_STATUS 0x104C +#define SWRM_MCP_STATUS_BANK_NUM_MASK BIT(0) +#define SWRM_MCP_SLV_STATUS 0x1090 +#define SWRM_MCP_SLV_STATUS_MASK GENMASK(1, 0) +#define SWRM_DP_PORT_CTRL_BANK(n, m) (0x1124 + 0x100 * (n - 1) + 0x40 * m) +#define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18 +#define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10 +#define SWRM_DP_PORT_CTRL_OFFSET1_SHFT 0x08 +#define SWRM_AHB_BRIDGE_WR_DATA_0 0xc85 +#define SWRM_AHB_BRIDGE_WR_ADDR_0 0xc89 +#define SWRM_AHB_BRIDGE_RD_ADDR_0 0xc8d +#define SWRM_AHB_BRIDGE_RD_DATA_0 0xc91 + +#define SWRM_REG_VAL_PACK(data, dev, id, reg) \ + ((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24)) + +#define SWRM_MAX_ROW_VAL 0 /* Rows = 48 */ +#define SWRM_DEFAULT_ROWS 48 +#define SWRM_MIN_COL_VAL 0 /* Cols = 2 */ +#define SWRM_DEFAULT_COL 16 +#define SWRM_MAX_COL_VAL 7 +#define SWRM_SPECIAL_CMD_ID 0xF +#define MAX_FREQ_NUM 1 +#define TIMEOUT_MS (2 * HZ) +#define QCOM_SWRM_MAX_RD_LEN 0xf +#define QCOM_SDW_MAX_PORTS 14 +#define DEFAULT_CLK_FREQ 9600000 +#define SWRM_MAX_DAIS 0xF + +struct qcom_swrm_port_config { + u8 si; + u8 off1; + u8 off2; +}; + +struct qcom_swrm_ctrl { + struct sdw_bus bus; + struct device *dev; + struct regmap *regmap; + struct completion *comp; + struct work_struct slave_work; + /* read/write lock */ + spinlock_t comp_lock; + /* Port alloc/free lock */ + struct mutex port_lock; + struct clk *hclk; + void __iomem *base; + u8 wr_cmd_id; + u8 rd_cmd_id; + int irq; + unsigned int version; + int num_din_ports; + int num_dout_ports; + unsigned long dout_port_mask; + unsigned long din_port_mask; + struct qcom_swrm_port_config pconfig[QCOM_SDW_MAX_PORTS]; + struct sdw_stream_runtime *sruntime[SWRM_MAX_DAIS]; + enum sdw_slave_status status[SDW_MAX_DEVICES]; + int (*reg_read)(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val); + int (*reg_write)(struct qcom_swrm_ctrl *ctrl, int reg, int val); +}; + +#define to_qcom_sdw(b) container_of(b, struct qcom_swrm_ctrl, bus) + +static int qcom_swrm_abh_reg_read(struct qcom_swrm_ctrl *ctrl, int reg, + u32 *val) +{ + struct regmap *wcd_regmap = ctrl->regmap; + int ret; + + /* pg register + offset */ + ret = regmap_bulk_write(wcd_regmap, SWRM_AHB_BRIDGE_RD_ADDR_0, + (u8 *)®, 4); + if (ret < 0) + return SDW_CMD_FAIL; + + ret = regmap_bulk_read(wcd_regmap, SWRM_AHB_BRIDGE_RD_DATA_0, + val, 4); + if (ret < 0) + return SDW_CMD_FAIL; + + return SDW_CMD_OK; +} + +static int qcom_swrm_mmio_reg_read(struct qcom_swrm_ctrl *ctrl, int reg, + u32 *val) +{ + *val = readl_relaxed(ctrl->base + reg); + + return 0; +} + +static int qcom_swrm_mmio_reg_write(struct qcom_swrm_ctrl *ctrl, + int reg, int val) +{ + writel_relaxed(val, ctrl->base + reg); + + return 0; +} + +static int qcom_swrm_ahb_reg_write(struct qcom_swrm_ctrl *ctrl, + int reg, int val) +{ + struct regmap *wcd_regmap = ctrl->regmap; + int ret; + /* pg register + offset */ + ret = regmap_bulk_write(wcd_regmap, SWRM_AHB_BRIDGE_WR_DATA_0, + (u8 *)&val, 4); + if (ret) + return SDW_CMD_FAIL; + + /* write address register */ + ret = regmap_bulk_write(wcd_regmap, SWRM_AHB_BRIDGE_WR_ADDR_0, + (u8 *)®, 4); + if (ret) + return SDW_CMD_FAIL; + + return SDW_CMD_OK; +} + +static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *ctrl, u8 cmd_data, + u8 dev_addr, u16 reg_addr) +{ + DECLARE_COMPLETION_ONSTACK(comp); + unsigned long flags; + u32 val; + int ret; + + spin_lock_irqsave(&ctrl->comp_lock, flags); + ctrl->comp = ∁ + spin_unlock_irqrestore(&ctrl->comp_lock, flags); + val = SWRM_REG_VAL_PACK(cmd_data, dev_addr, + SWRM_SPECIAL_CMD_ID, reg_addr); + ret = ctrl->reg_write(ctrl, SWRM_CMD_FIFO_WR_CMD, val); + if (ret) + goto err; + + ret = wait_for_completion_timeout(ctrl->comp, + msecs_to_jiffies(TIMEOUT_MS)); + + if (!ret) + ret = SDW_CMD_IGNORED; + else + ret = SDW_CMD_OK; +err: + spin_lock_irqsave(&ctrl->comp_lock, flags); + ctrl->comp = NULL; + spin_unlock_irqrestore(&ctrl->comp_lock, flags); + + return ret; +} + +static int qcom_swrm_cmd_fifo_rd_cmd(struct qcom_swrm_ctrl *ctrl, + u8 dev_addr, u16 reg_addr, + u32 len, u8 *rval) +{ + int i, ret; + u32 val; + DECLARE_COMPLETION_ONSTACK(comp); + unsigned long flags; + + spin_lock_irqsave(&ctrl->comp_lock, flags); + ctrl->comp = ∁ + spin_unlock_irqrestore(&ctrl->comp_lock, flags); + + val = SWRM_REG_VAL_PACK(len, dev_addr, SWRM_SPECIAL_CMD_ID, reg_addr); + ret = ctrl->reg_write(ctrl, SWRM_CMD_FIFO_RD_CMD, val); + if (ret) + goto err; + + ret = wait_for_completion_timeout(ctrl->comp, + msecs_to_jiffies(TIMEOUT_MS)); + + if (!ret) { + ret = SDW_CMD_IGNORED; + goto err; + } else { + ret = SDW_CMD_OK; + } + + for (i = 0; i < len; i++) { + ret = ctrl->reg_read(ctrl, SWRM_CMD_FIFO_RD_FIFO_ADDR, &val); + if (ret) + return ret; + + rval[i] = val & 0xFF; + } + +err: + spin_lock_irqsave(&ctrl->comp_lock, flags); + ctrl->comp = NULL; + spin_unlock_irqrestore(&ctrl->comp_lock, flags); + + return ret; +} + +static void qcom_swrm_get_device_status(struct qcom_swrm_ctrl *ctrl) +{ + u32 val; + int i; + + ctrl->reg_read(ctrl, SWRM_MCP_SLV_STATUS, &val); + + for (i = 0; i < SDW_MAX_DEVICES; i++) { + u32 s; + + s = (val >> (i * 2)); + s &= SWRM_MCP_SLV_STATUS_MASK; + ctrl->status[i] = s; + } +} + +static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) +{ + struct qcom_swrm_ctrl *ctrl = dev_id; + u32 sts, value; + unsigned long flags; + + ctrl->reg_read(ctrl, SWRM_INTERRUPT_STATUS, &sts); + + if (sts & SWRM_INTERRUPT_STATUS_CMD_ERROR) { + ctrl->reg_read(ctrl, SWRM_CMD_FIFO_STATUS, &value); + dev_err_ratelimited(ctrl->dev, + "CMD error, fifo status 0x%x\n", + value); + ctrl->reg_write(ctrl, SWRM_CMD_FIFO_CMD, 0x1); + } + + if ((sts & SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED) || + sts & SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS) + schedule_work(&ctrl->slave_work); + + ctrl->reg_write(ctrl, SWRM_INTERRUPT_CLEAR, sts); + + if (sts & SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED) { + spin_lock_irqsave(&ctrl->comp_lock, flags); + if (ctrl->comp) + complete(ctrl->comp); + spin_unlock_irqrestore(&ctrl->comp_lock, flags); + } + + return IRQ_HANDLED; +} +static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl) +{ + u32 val; + + /* Clear Rows and Cols */ + val = (SWRM_MAX_ROW_VAL << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT | + SWRM_MIN_COL_VAL << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT); + + ctrl->reg_write(ctrl, SWRM_MCP_FRAME_CTRL_BANK_ADDR(0), val); + + /* Disable Auto enumeration */ + ctrl->reg_write(ctrl, SWRM_ENUMERATOR_CFG_ADDR, 0); + + /* Mask soundwire interrupts */ + ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, + SWRM_INTERRUPT_STATUS_RMSK); + + /* Configure No pings */ + ctrl->reg_read(ctrl, SWRM_MCP_CFG_ADDR, &val); + val &= ~SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK; + val |= (SWRM_DEF_CMD_NO_PINGS << + SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT); + ctrl->reg_write(ctrl, SWRM_MCP_CFG_ADDR, val); + + /* Configure number of retries of a read/write cmd */ + ctrl->reg_write(ctrl, SWRM_CMD_FIFO_CFG_ADDR, SWRM_RD_WR_CMD_RETRIES); + + /* Set IRQ to PULSE */ + ctrl->reg_write(ctrl, SWRM_COMP_CFG_ADDR, + SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK | + SWRM_COMP_CFG_ENABLE_MSK); + return 0; +} + +static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus, + struct sdw_msg *msg) +{ + struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); + int ret, i, len; + + if (msg->flags == SDW_MSG_FLAG_READ) { + for (i = 0; i < msg->len;) { + if ((msg->len - i) < QCOM_SWRM_MAX_RD_LEN) + len = msg->len - i; + else + len = QCOM_SWRM_MAX_RD_LEN; + + ret = qcom_swrm_cmd_fifo_rd_cmd(ctrl, msg->dev_num, + msg->addr + i, len, + &msg->buf[i]); + if (ret) + return ret; + + i = i + len; + } + } else if (msg->flags == SDW_MSG_FLAG_WRITE) { + for (i = 0; i < msg->len; i++) { + ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->buf[i], + msg->dev_num, + msg->addr + i); + if (ret) + return SDW_CMD_IGNORED; + } + } + + return SDW_CMD_OK; +} + +static int qcom_swrm_pre_bank_switch(struct sdw_bus *bus) +{ + u32 reg = SWRM_MCP_FRAME_CTRL_BANK_ADDR(bus->params.next_bank); + struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); + u32 val; + int ret; + + ret = ctrl->reg_read(ctrl, reg, &val); + if (ret) + return ret; + + val &= ~SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK; + val &= ~SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK; + + val |= (SWRM_MAX_ROW_VAL << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT | + SWRM_MAX_COL_VAL << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT); + + return ctrl->reg_write(ctrl, reg, val); +} + +static int qcom_swrm_port_params(struct sdw_bus *bus, + struct sdw_port_params *p_params, + unsigned int bank) +{ + /* TBD */ + return 0; +} + +static int qcom_swrm_transport_params(struct sdw_bus *bus, + struct sdw_transport_params *params, + enum sdw_reg_bank bank) +{ + struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); + u32 value; + + value = params->offset1 << SWRM_DP_PORT_CTRL_OFFSET1_SHFT; + value |= params->offset2 << SWRM_DP_PORT_CTRL_OFFSET2_SHFT; + value |= params->sample_interval - 1; + + return ctrl->reg_write(ctrl, + SWRM_DP_PORT_CTRL_BANK((params->port_num), bank), + value); +} + +static int qcom_swrm_port_enable(struct sdw_bus *bus, + struct sdw_enable_ch *enable_ch, + unsigned int bank) +{ + u32 reg = SWRM_DP_PORT_CTRL_BANK(enable_ch->port_num, bank); + struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); + u32 val; + int ret; + + ret = ctrl->reg_read(ctrl, reg, &val); + if (ret) + return ret; + + if (enable_ch->enable) + val |= (enable_ch->ch_mask << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); + else + val &= ~(enable_ch->ch_mask << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); + + return ctrl->reg_write(ctrl, reg, val); +} + +static struct sdw_master_port_ops qcom_swrm_port_ops = { + .dpn_set_port_params = qcom_swrm_port_params, + .dpn_set_port_transport_params = qcom_swrm_transport_params, + .dpn_port_enable_ch = qcom_swrm_port_enable, +}; + +static struct sdw_master_ops qcom_swrm_ops = { + .xfer_msg = qcom_swrm_xfer_msg, + .pre_bank_switch = qcom_swrm_pre_bank_switch, +}; + +static int qcom_swrm_compute_params(struct sdw_bus *bus) +{ + struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); + struct sdw_master_runtime *m_rt; + struct sdw_slave_runtime *s_rt; + struct sdw_port_runtime *p_rt; + struct qcom_swrm_port_config *pcfg; + int i = 0; + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + pcfg = &ctrl->pconfig[p_rt->num - 1]; + p_rt->transport_params.port_num = p_rt->num; + p_rt->transport_params.sample_interval = pcfg->si + 1; + p_rt->transport_params.offset1 = pcfg->off1; + p_rt->transport_params.offset2 = pcfg->off2; + } + + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { + list_for_each_entry(p_rt, &s_rt->port_list, port_node) { + pcfg = &ctrl->pconfig[i]; + p_rt->transport_params.port_num = p_rt->num; + p_rt->transport_params.sample_interval = + pcfg->si + 1; + p_rt->transport_params.offset1 = pcfg->off1; + p_rt->transport_params.offset2 = pcfg->off2; + i++; + } + } + } + + return 0; +} + +static u32 qcom_swrm_freq_tbl[MAX_FREQ_NUM] = { + DEFAULT_CLK_FREQ, +}; + +static void qcom_swrm_slave_wq(struct work_struct *work) +{ + struct qcom_swrm_ctrl *ctrl = + container_of(work, struct qcom_swrm_ctrl, slave_work); + + qcom_swrm_get_device_status(ctrl); + sdw_handle_slave_status(&ctrl->bus, ctrl->status); +} + +static int qcom_swrm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev); + + if (!ctrl->sruntime[dai->id]) + return -EINVAL; + + return sdw_enable_stream(ctrl->sruntime[dai->id]); +} + +static void qcom_swrm_stream_free_ports(struct qcom_swrm_ctrl *ctrl, + struct sdw_stream_runtime *stream) +{ + struct sdw_master_runtime *m_rt; + struct sdw_port_runtime *p_rt; + unsigned long *port_mask; + + mutex_lock(&ctrl->port_lock); + + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + if (m_rt->direction == SDW_DATA_DIR_RX) + port_mask = &ctrl->dout_port_mask; + else + port_mask = &ctrl->din_port_mask; + + list_for_each_entry(p_rt, &m_rt->port_list, port_node) + clear_bit(p_rt->num - 1, port_mask); + } + + mutex_unlock(&ctrl->port_lock); +} + +static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, + struct sdw_stream_runtime *stream, + struct snd_pcm_hw_params *params, + int direction) +{ + struct sdw_port_config pconfig[QCOM_SDW_MAX_PORTS]; + struct sdw_stream_config sconfig; + struct sdw_master_runtime *m_rt; + struct sdw_slave_runtime *s_rt; + struct sdw_port_runtime *p_rt; + unsigned long *port_mask; + int i, maxport, pn, nports = 0, ret = 0; + + mutex_lock(&ctrl->port_lock); + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + if (m_rt->direction == SDW_DATA_DIR_RX) { + maxport = ctrl->num_dout_ports; + port_mask = &ctrl->dout_port_mask; + } else { + maxport = ctrl->num_din_ports; + port_mask = &ctrl->din_port_mask; + } + + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { + list_for_each_entry(p_rt, &s_rt->port_list, port_node) { + /* Port numbers start from 1 - 14*/ + pn = find_first_zero_bit(port_mask, maxport); + if (pn > (maxport - 1)) { + dev_err(ctrl->dev, "All ports busy\n"); + ret = -EBUSY; + goto err; + } + set_bit(pn, port_mask); + pconfig[nports].num = pn + 1; + pconfig[nports].ch_mask = p_rt->ch_mask; + nports++; + } + } + } + + if (direction == SNDRV_PCM_STREAM_CAPTURE) + sconfig.direction = SDW_DATA_DIR_TX; + else + sconfig.direction = SDW_DATA_DIR_RX; + + sconfig.ch_count = 1; + sconfig.frame_rate = params_rate(params); + sconfig.type = stream->type; + sconfig.bps = 1; + sdw_stream_add_master(&ctrl->bus, &sconfig, pconfig, + nports, stream); +err: + if (ret) { + for (i = 0; i < nports; i++) + clear_bit(pconfig[i].num - 1, port_mask); + } + + mutex_unlock(&ctrl->port_lock); + + return ret; +} + +static int qcom_swrm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev); + struct sdw_stream_runtime *sruntime = ctrl->sruntime[dai->id]; + int ret; + + ret = qcom_swrm_stream_alloc_ports(ctrl, sruntime, params, + substream->stream); + if (ret) + return ret; + + ret = sdw_prepare_stream(sruntime); + if (ret) + qcom_swrm_stream_free_ports(ctrl, sruntime); + + return ret; +} + +static int qcom_swrm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev); + struct sdw_stream_runtime *sruntime = ctrl->sruntime[dai->id]; + + qcom_swrm_stream_free_ports(ctrl, sruntime); + sdw_stream_remove_master(&ctrl->bus, sruntime); + sdw_deprepare_stream(sruntime); + sdw_disable_stream(sruntime); + + return 0; +} + +static int qcom_swrm_set_sdw_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev); + + ctrl->sruntime[dai->id] = stream; + + return 0; +} + +static int qcom_swrm_startup(struct snd_pcm_substream *stream, + struct snd_soc_dai *dai) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev); + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct sdw_stream_runtime *sruntime; + int ret, i; + + sruntime = sdw_alloc_stream(dai->name); + if (!sruntime) + return -ENOMEM; + + ctrl->sruntime[dai->id] = sruntime; + + for (i = 0; i < rtd->num_codecs; i++) { + ret = snd_soc_dai_set_sdw_stream(rtd->codec_dais[i], sruntime, + stream->stream); + if (ret < 0 && ret != -ENOTSUPP) { + dev_err(dai->dev, "Failed to set sdw stream on %s", + rtd->codec_dais[i]->name); + sdw_release_stream(sruntime); + return ret; + } + } + + return 0; +} + +static void qcom_swrm_shutdown(struct snd_pcm_substream *stream, + struct snd_soc_dai *dai) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev); + + sdw_release_stream(ctrl->sruntime[dai->id]); + ctrl->sruntime[dai->id] = NULL; +} + +static const struct snd_soc_dai_ops qcom_swrm_pdm_dai_ops = { + .hw_params = qcom_swrm_hw_params, + .prepare = qcom_swrm_prepare, + .hw_free = qcom_swrm_hw_free, + .startup = qcom_swrm_startup, + .shutdown = qcom_swrm_shutdown, + .set_sdw_stream = qcom_swrm_set_sdw_stream, +}; + +static const struct snd_soc_component_driver qcom_swrm_dai_component = { + .name = "soundwire", +}; + +static int qcom_swrm_register_dais(struct qcom_swrm_ctrl *ctrl) +{ + int num_dais = ctrl->num_dout_ports + ctrl->num_din_ports; + struct snd_soc_dai_driver *dais; + struct snd_soc_pcm_stream *stream; + struct device *dev = ctrl->dev; + int i; + + /* PDM dais are only tested for now */ + dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL); + if (!dais) + return -ENOMEM; + + for (i = 0; i < num_dais; i++) { + dais[i].name = devm_kasprintf(dev, GFP_KERNEL, "SDW Pin%d", i); + if (!dais[i].name) + return -ENOMEM; + + if (i < ctrl->num_dout_ports) { + stream = &dais[i].playback; + stream->stream_name = devm_kasprintf(dev, GFP_KERNEL, + "SDW Tx%d", i); + } else { + stream = &dais[i].capture; + stream->stream_name = devm_kasprintf(dev, GFP_KERNEL, + "SDW Rx%d", i); + } + + if (!stream->stream_name) + return -ENOMEM; + + stream->channels_min = 1; + stream->channels_max = 1; + stream->rates = SNDRV_PCM_RATE_48000; + stream->formats = SNDRV_PCM_FMTBIT_S16_LE; + + dais[i].ops = &qcom_swrm_pdm_dai_ops; + dais[i].id = i; + } + + return devm_snd_soc_register_component(ctrl->dev, + &qcom_swrm_dai_component, + dais, num_dais); +} + +static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) +{ + struct device_node *np = ctrl->dev->of_node; + u8 off1[QCOM_SDW_MAX_PORTS]; + u8 off2[QCOM_SDW_MAX_PORTS]; + u8 si[QCOM_SDW_MAX_PORTS]; + int i, ret, nports, val; + + ret = ctrl->reg_read(ctrl, SWRM_COMP_PARAMS, &val); + if (ret) + return ret; + + ctrl->num_dout_ports = val & SWRM_COMP_PARAMS_DOUT_PORTS_MASK; + ctrl->num_din_ports = (val & SWRM_COMP_PARAMS_DIN_PORTS_MASK) >> 5; + + ret = of_property_read_u32(np, "qcom,din-ports", &val); + if (ret) + return ret; + + if (val > ctrl->num_din_ports) + return -EINVAL; + + ctrl->num_din_ports = val; + + ret = of_property_read_u32(np, "qcom,dout-ports", &val); + if (ret) + return ret; + + if (val > ctrl->num_dout_ports) + return -EINVAL; + + ctrl->num_dout_ports = val; + + nports = ctrl->num_dout_ports + ctrl->num_din_ports; + + ret = of_property_read_u8_array(np, "qcom,ports-offset1", + off1, nports); + if (ret) + return ret; + + ret = of_property_read_u8_array(np, "qcom,ports-offset2", + off2, nports); + if (ret) + return ret; + + ret = of_property_read_u8_array(np, "qcom,ports-sinterval-low", + si, nports); + if (ret) + return ret; + + for (i = 0; i < nports; i++) { + ctrl->pconfig[i].si = si[i]; + ctrl->pconfig[i].off1 = off1[i]; + ctrl->pconfig[i].off2 = off2[i]; + } + + return 0; +} + +static int qcom_swrm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sdw_master_prop *prop; + struct sdw_bus_params *params; + struct qcom_swrm_ctrl *ctrl; + struct resource *res; + int ret; + u32 val; + + ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + if (dev->parent->bus == &slimbus_bus) { + ctrl->reg_read = qcom_swrm_abh_reg_read; + ctrl->reg_write = qcom_swrm_ahb_reg_write; + ctrl->regmap = dev_get_regmap(dev->parent, NULL); + if (!ctrl->regmap) + return -EINVAL; + } else { + ctrl->reg_read = qcom_swrm_mmio_reg_read; + ctrl->reg_write = qcom_swrm_mmio_reg_write; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctrl->base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->base)) + return PTR_ERR(ctrl->base); + } + + ctrl->irq = of_irq_get(dev->of_node, 0); + if (ctrl->irq < 0) + return ctrl->irq; + + ctrl->hclk = devm_clk_get(dev, "iface"); + if (IS_ERR(ctrl->hclk)) + return PTR_ERR(ctrl->hclk); + + clk_prepare_enable(ctrl->hclk); + + ctrl->dev = dev; + dev_set_drvdata(&pdev->dev, ctrl); + spin_lock_init(&ctrl->comp_lock); + mutex_init(&ctrl->port_lock); + INIT_WORK(&ctrl->slave_work, qcom_swrm_slave_wq); + + ctrl->bus.dev = dev; + ctrl->bus.ops = &qcom_swrm_ops; + ctrl->bus.port_ops = &qcom_swrm_port_ops; + ctrl->bus.compute_params = &qcom_swrm_compute_params; + + ret = qcom_swrm_get_port_config(ctrl); + if (ret) + return ret; + + params = &ctrl->bus.params; + params->max_dr_freq = DEFAULT_CLK_FREQ; + params->curr_dr_freq = DEFAULT_CLK_FREQ; + params->col = SWRM_DEFAULT_COL; + params->row = SWRM_DEFAULT_ROWS; + ctrl->reg_read(ctrl, SWRM_MCP_STATUS, &val); + params->curr_bank = val & SWRM_MCP_STATUS_BANK_NUM_MASK; + params->next_bank = !params->curr_bank; + + prop = &ctrl->bus.prop; + prop->max_clk_freq = DEFAULT_CLK_FREQ; + prop->num_clk_gears = 0; + prop->num_clk_freq = MAX_FREQ_NUM; + prop->clk_freq = &qcom_swrm_freq_tbl[0]; + prop->default_col = SWRM_DEFAULT_COL; + prop->default_row = SWRM_DEFAULT_ROWS; + + ctrl->reg_read(ctrl, SWRM_COMP_HW_VERSION, &ctrl->version); + + ret = devm_request_threaded_irq(dev, ctrl->irq, NULL, + qcom_swrm_irq_handler, + IRQF_TRIGGER_RISING, + "soundwire", ctrl); + if (ret) { + dev_err(dev, "Failed to request soundwire irq\n"); + goto err; + } + + ret = sdw_add_bus_master(&ctrl->bus); + if (ret) { + dev_err(dev, "Failed to register Soundwire controller (%d)\n", + ret); + goto err; + } + + qcom_swrm_init(ctrl); + ret = qcom_swrm_register_dais(ctrl); + if (ret) + goto err; + + dev_info(dev, "Qualcomm Soundwire controller v%x.%x.%x Registered\n", + (ctrl->version >> 24) & 0xff, (ctrl->version >> 16) & 0xff, + ctrl->version & 0xffff); + + return 0; +err: + clk_disable_unprepare(ctrl->hclk); + return ret; +} + +static int qcom_swrm_remove(struct platform_device *pdev) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(&pdev->dev); + + sdw_delete_bus_master(&ctrl->bus); + clk_disable_unprepare(ctrl->hclk); + + return 0; +} + +static int qcom_swrm_runtime_suspend(struct device *device) +{ + /* TBD */ + return 0; +} + +static int qcom_swrm_runtime_resume(struct device *device) +{ + /* TBD */ + return 0; +} + +static const struct dev_pm_ops qcom_swrm_dev_pm_ops = { + SET_RUNTIME_PM_OPS(qcom_swrm_runtime_suspend, + qcom_swrm_runtime_resume, + NULL + ) +}; + +static const struct of_device_id qcom_swrm_of_match[] = { + { .compatible = "qcom,soundwire-v1.3.0",}, + { .compatible = "qcom,soundwire-v1.5.0",}, + { .compatible = "qcom,soundwire-v1.6.0",}, + {/* sentinel */}, +}; + +MODULE_DEVICE_TABLE(of, qcom_swrm_of_match); + +static struct platform_driver qcom_swrm_driver = { + .probe = &qcom_swrm_probe, + .remove = &qcom_swrm_remove, + .driver = { + .name = "qcom-soundwire", + .of_match_table = qcom_swrm_of_match, + .pm = &qcom_swrm_dev_pm_ops, + } +}; +module_platform_driver(qcom_swrm_driver); + +MODULE_DESCRIPTION("Qualcomm soundwire driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index e69f94a8c3a8..cb584831f2e8 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -155,6 +155,8 @@ static int sdw_program_slave_port_params(struct sdw_bus *bus, addr6 = SDW_DPN_LANECTRL_B0(t_params->port_num); } +#if 0 + //Do not program these for SDW_STREAM_PDM type /* Program DPN_PortCtrl register */ wbuf = p_params->data_mode << SDW_REG_SHIFT(SDW_DPN_PORTCTRL_DATAMODE); wbuf |= p_params->flow_mode; @@ -176,6 +178,7 @@ static int sdw_program_slave_port_params(struct sdw_bus *bus, return ret; } +#endif /* Program DPN_SampleCtrl1 register */ wbuf = (t_params->sample_interval - 1) & SDW_DPN_SAMPLECTRL_LOW; ret = sdw_write(s_rt->slave, addr3, wbuf); @@ -689,9 +692,12 @@ static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count) } if (!multi_link) { - kfree(wr_msg); - kfree(wbuf); - bus->defer_msg.msg = NULL; + if (bus->defer_msg.msg) { + kfree(bus->defer_msg.msg->buf); + kfree(bus->defer_msg.msg); + bus->defer_msg.msg = NULL; + } + bus->params.curr_bank = !bus->params.curr_bank; bus->params.next_bank = !bus->params.next_bank; } @@ -701,7 +707,11 @@ static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count) error: kfree(wbuf); error_1: - kfree(wr_msg); + if (bus->defer_msg.msg) { + kfree(bus->defer_msg.msg); + bus->defer_msg.msg = NULL; + } + return ret; } @@ -734,6 +744,7 @@ static int sdw_ml_sync_bank_switch(struct sdw_bus *bus) if (bus->defer_msg.msg) { kfree(bus->defer_msg.msg->buf); kfree(bus->defer_msg.msg); + bus->defer_msg.msg = NULL; } return 0; @@ -825,9 +836,11 @@ static int do_bank_switch(struct sdw_stream_runtime *stream) error: list_for_each_entry(m_rt, &stream->master_list, stream_node) { bus = m_rt->bus; - - kfree(bus->defer_msg.msg->buf); - kfree(bus->defer_msg.msg); + if (bus->defer_msg.msg) { + kfree(bus->defer_msg.msg->buf); + kfree(bus->defer_msg.msg); + bus->defer_msg.msg = NULL; + } } msg_unlock: diff --git a/include/linux/mfd/wcd934x/registers.h b/include/linux/mfd/wcd934x/registers.h new file mode 100644 index 000000000000..169b3ab620c6 --- /dev/null +++ b/include/linux/mfd/wcd934x/registers.h @@ -0,0 +1,529 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _WCD934X_REGISTERS_H +#define _WCD934X_REGISTERS_H + +#define WCD934X_CODEC_RPM_CLK_GATE 0x0002 +#define WCD934X_CODEC_RPM_CLK_GATE_MASK GENMASK(1, 0) +#define WCD934X_CODEC_RPM_CLK_MCLK_CFG 0x0003 +#define WCD934X_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ BIT(0) +#define WCD934X_CODEC_RPM_CLK_MCLK_CFG_12P288MHZ BIT(1) +#define WCD934X_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK GENMASK(1, 0) +#define WCD934X_CODEC_RPM_RST_CTL 0x0009 +#define WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL 0x0011 +#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0 0x0021 +#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2 0x0023 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_CTL 0x0025 +#define WCD934X_EFUSE_SENSE_STATE_MASK GENMASK(4, 1) +#define WCD934X_EFUSE_SENSE_STATE_DEF 0x10 +#define WCD934X_EFUSE_SENSE_EN_MASK BIT(0) +#define WCD934X_EFUSE_SENSE_ENABLE BIT(0) +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14 0x0037 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15 0x0038 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS 0x0039 +#define WCD934X_DATA_HUB_SB_TX10_INP_CFG 0x006b +#define WCD934X_DATA_HUB_SB_TX11_INP_CFG 0x006c +#define WCD934X_DATA_HUB_SB_TX13_INP_CFG 0x006e +#define WCD934X_CPE_FLL_CONFIG_CTL_2 0x0111 +#define WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD 0x0213 +#define WCD934X_CPE_SS_SVA_CFG 0x0214 +#define WCD934X_CPE_SS_DMIC0_CTL 0x0218 +#define WCD934X_CPE_SS_DMIC1_CTL 0x0219 +#define WCD934X_DMIC_RATE_MASK GENMASK(3, 1) +#define WCD934X_CPE_SS_DMIC2_CTL 0x021a +#define WCD934X_CPE_SS_DMIC_CFG 0x021b +#define WCD934X_CPE_SS_DMIC_CFG 0x021b +#define WCD934X_CPE_SS_CPAR_CFG 0x021c +#define WCD934X_INTR_PIN1_MASK0 0x0409 +#define WCD934X_INTR_PIN1_STATUS0 0x0411 +#define WCD934X_INTR_PIN1_CLEAR0 0x0419 +#define WCD934X_INTR_PIN2_CLEAR3 0x0434 +#define WCD934X_INTR_LEVEL0 0x0461 +/* INTR_REG 0 */ +#define WCD934X_IRQ_SLIMBUS 0 +#define WCD934X_IRQ_MISC 1 +#define WCD934X_IRQ_HPH_PA_OCPL_FAULT 2 +#define WCD934X_IRQ_HPH_PA_OCPR_FAULT 3 +#define WCD934X_IRQ_EAR_PA_OCP_FAULT 4 +#define WCD934X_IRQ_HPH_PA_CNPL_COMPLETE 5 +#define WCD934X_IRQ_HPH_PA_CNPR_COMPLETE 6 +#define WCD934X_IRQ_EAR_PA_CNP_COMPLETE 7 +/* INTR_REG 1 */ +#define WCD934X_IRQ_MBHC_SW_DET 8 +#define WCD934X_IRQ_MBHC_ELECT_INS_REM_DET 9 +#define WCD934X_IRQ_MBHC_BUTTON_PRESS_DET 10 +#define WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET 11 +#define WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET 12 +#define WCD934X_IRQ_RESERVED_0 13 +#define WCD934X_IRQ_RESERVED_1 14 +#define WCD934X_IRQ_RESERVED_2 15 +/* INTR_REG 2 */ +#define WCD934X_IRQ_LINE_PA1_CNP_COMPLETE 16 +#define WCD934X_IRQ_LINE_PA2_CNP_COMPLETE 17 +#define WCD934X_IRQ_SLNQ_ANALOG_ERROR 18 +#define WCD934X_IRQ_RESERVED_3 19 +#define WCD934X_IRQ_SOUNDWIRE 20 +#define WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE 21 +#define WCD934X_IRQ_RCO_ERROR 22 +#define WCD934X_IRQ_CPE_ERROR 23 +/* INTR_REG 3 */ +#define WCD934X_IRQ_MAD_AUDIO 24 +#define WCD934X_IRQ_MAD_BEACON 25 +#define WCD934X_IRQ_MAD_ULTRASOUND 26 +#define WCD934X_IRQ_VBAT_ATTACK 27 +#define WCD934X_IRQ_VBAT_RESTORE 28 +#define WCD934X_IRQ_CPE1_INTR 29 +#define WCD934X_IRQ_RESERVED_4 30 +#define WCD934X_IRQ_SLNQ_DIGITAL 31 +#define WCD934X_NUM_IRQS 32 +#define WCD934X_ANA_BIAS 0x0601 +#define WCD934X_ANA_BIAS_EN_MASK BIT(7) +#define WCD934X_ANA_BIAS_EN BIT(7) +#define WCD934X_ANA_PRECHRG_EN_MASK BIT(6) +#define WCD934X_ANA_PRECHRG_EN BIT(6) +#define WCD934X_ANA_PRECHRG_MODE_MASK BIT(5) +#define WCD934X_ANA_PRECHRG_MODE_AUTO BIT(5) +#define WCD934X_ANA_RCO 0x0603 +#define WCD934X_ANA_RCO_BG_EN_MASK BIT(7) +#define WCD934X_ANA_RCO_BG_ENABLE BIT(7) +#define WCD934X_ANA_BUCK_CTL 0x0606 +#define WCD934X_ANA_BUCK_HI_ACCU_PRE_ENX_MASK GENMASK(1, 0) +#define WCD934X_ANA_BUCK_PRE_EN2_MASK BIT(0) +#define WCD934X_ANA_BUCK_PRE_EN2_ENABLE BIT(0) +#define WCD934X_ANA_BUCK_PRE_EN1_MASK BIT(1) +#define WCD934X_ANA_BUCK_PRE_EN1_ENABLE BIT(1) +#define WCD934X_ANA_BUCK_HI_ACCU_EN_MASK BIT(2) +#define WCD934X_ANA_BUCK_HI_ACCU_ENABLE BIT(2) +#define WCD934X_ANA_RX_SUPPLIES 0x0608 +#define WCD934X_ANA_HPH 0x0609 +#define WCD934X_ANA_EAR 0x060a +#define WCD934X_ANA_LO_1_2 0x060b +#define WCD934X_ANA_AMIC1 0x060e +#define WCD934X_ANA_AMIC2 0x060f +#define WCD934X_ANA_AMIC3 0x0610 +#define WCD934X_ANA_AMIC4 0x0611 +#define WCD934X_ANA_MBHC_MECH 0x0614 +#define WCD934X_ANA_MBHC_ELECT 0x0615 +#define WCD934X_ANA_MBHC_ZDET 0x0616 +#define WCD934X_ANA_MBHC_RESULT_1 0x0617 +#define WCD934X_ANA_MBHC_RESULT_2 0x0618 +#define WCD934X_ANA_MBHC_RESULT_3 0x0619 +#define WCD934X_ANA_MICB1 0x0622 +#define WCD934X_MICB_VAL_MASK GENMASK(5, 0) +#define WCD934X_ANA_MICB_EN_MASK GENMASK(7, 6) +#define WCD934X_ANA_MICB_PULL_UP 0x80 +#define WCD934X_ANA_MICB_ENABLE 0x40 +#define WCD934X_ANA_MICB_DISABLE 0x0 +#define WCD934X_ANA_MICB2 0x0623 +#define WCD934X_ANA_MICB3 0x0625 +#define WCD934X_ANA_MICB4 0x0626 +#define WCD934X_BIAS_VBG_FINE_ADJ 0x0629 +#define WCD934X_MICB1_TEST_CTL_1 0x066b +#define WCD934X_MICB1_TEST_CTL_2 0x066c +#define WCD934X_MICB2_TEST_CTL_1 0x066e +#define WCD934X_MICB3_TEST_CTL_1 0x0671 +#define WCD934X_MICB4_TEST_CTL_1 0x0674 +#define WCD934X_CLASSH_MODE_1 0x0697 +#define WCD934X_CLASSH_MODE_2 0x0698 +#define WCD934X_CLASSH_MODE_3 0x0699 +#define WCD934X_CLASSH_CTRL_VCL_1 0x069a +#define WCD934X_CLASSH_CTRL_VCL_2 0x069b +#define WCD934X_CLASSH_CTRL_CCL_1 0x069c +#define WCD934X_CLASSH_CTRL_CCL_2 0x069d +#define WCD934X_CLASSH_CTRL_CCL_3 0x069e +#define WCD934X_CLASSH_CTRL_CCL_4 0x069f +#define WCD934X_CLASSH_CTRL_CCL_5 0x06a0 +#define WCD934X_CLASSH_BUCK_TMUX_A_D 0x06a1 +#define WCD934X_CLASSH_BUCK_SW_DRV_CNTL 0x06a2 +#define WCD934X_RX_OCP_CTL 0x06b6 +#define WCD934X_RX_OCP_COUNT 0x06b7 +#define WCD934X_HPH_CNP_EN 0x06cb +#define WCD934X_HPH_CNP_WG_CTL 0x06cc +#define WCD934X_HPH_GM3_BOOST_EN_MASK BIT(7) +#define WCD934X_HPH_GM3_BOOST_ENABLE BIT(7) +#define WCD934X_HPH_OCP_CTL 0x06ce +#define WCD934X_HPH_L_EN 0x06d3 +#define WCD934X_HPH_GAIN_SRC_SEL_MASK BIT(5) +#define WCD934X_HPH_GAIN_SRC_SEL_COMPANDER 0 +#define WCD934X_HPH_GAIN_SRC_SEL_REGISTER BIT(5) +#define WCD934X_HPH_L_TEST 0x06d4 +#define WCD934X_HPH_R_EN 0x06d6 +#define WCD934X_HPH_R_TEST 0x06d7 +#define WCD934X_HPH_OCP_DET_MASK BIT(0) +#define WCD934X_HPH_OCP_DET_ENABLE BIT(0) +#define WCD934X_HPH_OCP_DET_DISABLE 0 +#define WCD934X_DIFF_LO_LO2_COMPANDER 0x06ea +#define WCD934X_DIFF_LO_LO1_COMPANDER 0x06eb +#define WCD934X_CLK_SYS_MCLK_PRG 0x0711 +#define WCD934X_EXT_CLK_BUF_EN_MASK BIT(7) +#define WCD934X_EXT_CLK_BUF_EN BIT(7) +#define WCD934X_EXT_CLK_DIV_RATIO_MASK GENMASK(5, 4) +#define WCD934X_EXT_CLK_DIV_BY_2 0x10 +#define WCD934X_MCLK_SRC_MASK BIT(1) +#define WCD934X_MCLK_SRC_EXT_CLK 0 +#define WCD934X_MCLK_SRC_MASK BIT(1) +#define WCD934X_MCLK_EN_MASK BIT(0) +#define WCD934X_MCLK_EN BIT(0) +#define WCD934X_CLK_SYS_MCLK2_PRG1 0x0712 +#define WCD934X_CLK_SYS_MCLK2_PRG2 0x0713 +#define WCD934X_SIDO_NEW_VOUT_A_STARTUP 0x071b +#define WCD934X_SIDO_NEW_VOUT_D_STARTUP 0x071c +#define WCD934X_SIDO_NEW_VOUT_D_FREQ1 0x071d +#define WCD934X_SIDO_NEW_VOUT_D_FREQ2 0x071e +#define WCD934X_SIDO_RIPPLE_FREQ_EN_MASK BIT(0) +#define WCD934X_SIDO_RIPPLE_FREQ_ENABLE BIT(0) +#define WCD934X_MBHC_NEW_CTL_2 0x0721 +#define WCD934X_TX_NEW_AMIC_4_5_SEL 0x0727 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L 0x0733 +#define WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL 0x0735 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R 0x0736 +#define WCD934X_HPH_NEW_INT_HPH_TIMER1 0x073a +#define WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK BIT(1) +#define WCD934X_HPH_AUTOCHOP_TIMER_ENABLE BIT(1) +#define WCD934X_CDC_TX0_TX_PATH_CTL 0x0a31 +#define WCD934X_CDC_TX_PATH_CTL_PCM_RATE_MASK GENMASK(3, 0) +#define WCD934X_CDC_TX_PATH_CTL(dec) (0xa31 + dec * 0x10) +#define WCD934X_CDC_TX0_TX_PATH_CFG0 0x0a32 +#define WCD934X_CDC_TX0_TX_PATH_CFG1 0x0a33 +#define WCD934X_CDC_TX0_TX_VOL_CTL 0x0a34 +#define WCD934X_CDC_TX0_TX_PATH_192_CTL 0x0a35 +#define WCD934X_CDC_TX0_TX_PATH_192_CFG 0x0a36 +#define WCD934X_CDC_TX0_TX_PATH_SEC2 0x0a39 +#define WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK BIT(1) +#define WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ BIT(1) +#define WCD934X_CDC_TX1_TX_PATH_CTL 0x0a41 +#define WCD934X_CDC_TX1_TX_PATH_CFG0 0x0a42 +#define WCD934X_CDC_TX1_TX_PATH_CFG1 0x0a43 +#define WCD934X_CDC_TX1_TX_VOL_CTL 0x0a44 +#define WCD934X_CDC_TX2_TX_PATH_CTL 0x0a51 +#define WCD934X_CDC_TX2_TX_PATH_CFG0 0x0a52 +#define WCD934X_CDC_TX2_TX_PATH_CFG1 0x0a53 +#define WCD934X_CDC_TX2_TX_VOL_CTL 0x0a54 +#define WCD934X_CDC_TX3_TX_PATH_CTL 0x0a61 +#define WCD934X_CDC_TX3_TX_PATH_CFG0 0x0a62 +#define WCD934X_CDC_TX3_TX_PATH_CFG1 0x0a63 +#define WCD934X_CDC_TX3_TX_VOL_CTL 0x0a64 +#define WCD934X_CDC_TX3_TX_PATH_192_CTL 0x0a65 +#define WCD934X_CDC_TX3_TX_PATH_192_CFG 0x0a66 +#define WCD934X_CDC_TX4_TX_PATH_CTL 0x0a71 +#define WCD934X_CDC_TX4_TX_PATH_CFG0 0x0a72 +#define WCD934X_CDC_TX4_TX_PATH_CFG1 0x0a73 +#define WCD934X_CDC_TX4_TX_VOL_CTL 0x0a74 +#define WCD934X_CDC_TX4_TX_PATH_192_CTL 0x0a75 +#define WCD934X_CDC_TX4_TX_PATH_192_CFG 0x0a76 +#define WCD934X_CDC_TX5_TX_PATH_CTL 0x0a81 +#define WCD934X_CDC_TX5_TX_PATH_CFG0 0x0a82 +#define WCD934X_CDC_TX5_TX_PATH_CFG1 0x0a83 +#define WCD934X_CDC_TX5_TX_VOL_CTL 0x0a84 +#define WCD934X_CDC_TX5_TX_PATH_192_CTL 0x0a85 +#define WCD934X_CDC_TX5_TX_PATH_192_CFG 0x0a86 +#define WCD934X_CDC_TX6_TX_PATH_CTL 0x0a91 +#define WCD934X_CDC_TX6_TX_PATH_CFG0 0x0a92 +#define WCD934X_CDC_TX6_TX_PATH_CFG1 0x0a93 +#define WCD934X_CDC_TX6_TX_VOL_CTL 0x0a94 +#define WCD934X_CDC_TX6_TX_PATH_192_CTL 0x0a95 +#define WCD934X_CDC_TX6_TX_PATH_192_CFG 0x0a96 +#define WCD934X_CDC_TX7_TX_PATH_CTL 0x0aa1 +#define WCD934X_CDC_TX7_TX_PATH_CFG0 0x0aa2 +#define WCD934X_CDC_TX7_TX_PATH_CFG1 0x0aa3 +#define WCD934X_CDC_TX7_TX_VOL_CTL 0x0aa4 +#define WCD934X_CDC_TX7_TX_PATH_192_CTL 0x0aa5 +#define WCD934X_CDC_TX7_TX_PATH_192_CFG 0x0aa6 +#define WCD934X_CDC_TX8_TX_PATH_CTL 0x0ab1 +#define WCD934X_CDC_TX8_TX_PATH_CFG0 0x0ab2 +#define WCD934X_CDC_TX8_TX_PATH_CFG1 0x0ab3 +#define WCD934X_CDC_TX8_TX_VOL_CTL 0x0ab4 +#define WCD934X_CDC_TX8_TX_PATH_192_CTL 0x0ab5 +#define WCD934X_CDC_TX8_TX_PATH_192_CFG 0x0ab6 +#define WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0 0x0ac3 +#define WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0 0x0ac7 +#define WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0 0x0acb +#define WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0 0x0acf +#define WCD934X_CDC_COMPANDER1_CTL0 0x0b01 +#define WCD934X_COMP_CLK_EN_MASK BIT(0) +#define WCD934X_COMP_CLK_ENABLE BIT(0) +#define WCD934X_COMP_SOFT_RST_MASK BIT(1) +#define WCD934X_COMP_SOFT_RST_ENABLE BIT(1) +#define WCD934X_COMP_HALT_MASK BIT(2) +#define WCD934X_COMP_HALT BIT(2) +#define WCD934X_COMP_SOFT_RST_DISABLE 0 +#define WCD934X_CDC_COMPANDER1_CTL7 0x0b08 +#define WCD934X_HPH_LOW_PWR_MODE_EN_MASK BIT(5) +#define WCD934X_CDC_COMPANDER2_CTL7 0x0b10 +#define WCD934X_CDC_COMPANDER7_CTL3 0x0b34 +#define WCD934X_CDC_COMPANDER7_CTL7 0x0b38 +#define WCD934X_CDC_COMPANDER8_CTL3 0x0b3c +#define WCD934X_CDC_COMPANDER8_CTL7 0x0b40 +#define WCD934X_CDC_RX0_RX_PATH_CTL 0x0b41 +#define WCD934X_CDC_RX_PGA_MUTE_EN_MASK BIT(4) +#define WCD934X_CDC_RX_PGA_MUTE_ENABLE BIT(4) +#define WCD934X_CDC_RX_PGA_MUTE_DISABLE 0 +#define WCD934X_RX_CLK_EN_MASK BIT(5) +#define WCD934X_RX_CLK_ENABLE BIT(5) +#define WCD934X_RX_RESET_MASK BIT(6) +#define WCD934X_RX_RESET_ENABLE BIT(6) +#define WCD934X_RX_RESET_DISABLE 0 +#define WCD934X_RX_PCM_RATE_MASK GENMASK(3, 0) +#define WCD934X_RX_PCM_RATE_F_48K 0x04 +#define WCD934X_CDC_RX_PATH_CTL(rx) (0xb41 + rx * 0x14) +#define WCD934X_CDC_MIX_PCM_RATE_MASK GENMASK(3, 0) +#define WCD934X_CDC_RX0_RX_PATH_CFG0 0x0b42 +#define WCD934X_RX_DLY_ZN_EN_MASK BIT(3) +#define WCD934X_RX_DLY_ZN_ENABLE BIT(3) +#define WCD934X_RX_DLY_ZN_DISABLE 0 +#define WCD934X_CDC_RX0_RX_PATH_CFG1 0x0b43 +#define WCD934X_CDC_RX0_RX_PATH_CFG2 0x0b44 +#define WCD934X_CDC_RX0_RX_VOL_CTL 0x0b45 +#define WCD934X_CDC_RX0_RX_PATH_MIX_CTL 0x0b46 +#define WCD934X_CDC_RX_MIX_CLK_EN_MASK BIT(5) +#define WCD934X_CDC_RX_MIX_CLK_ENABLE BIT(5) +#define WCD934X_CDC_RX_PATH_MIX_CTL(rx) (0xb46 + rx * 0x14) +#define WCD934X_CDC_RX0_RX_PATH_MIX_CFG 0x0b47 +#define WCD934X_CDC_RX0_RX_VOL_MIX_CTL 0x0b48 +#define WCD934X_CDC_RX0_RX_PATH_SEC0 0x0b49 +#define WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL 0x0b53 +#define WCD934X_CDC_RX1_RX_PATH_CTL 0x0b55 +#define WCD934X_RX_PATH_PGA_MUTE_EN_MASK BIT(4) +#define WCD934X_RX_PATH_PGA_MUTE_ENABLE BIT(4) +#define WCD934X_CDC_RX_PATH_PGA_MUTE_DISABLE 0 +#define WCD934X_CDC_RX_PATH_CLK_EN_MASK BIT(5) +#define WCD934X_CDC_RX_PATH_CLK_ENABLE BIT(5) +#define WCD934X_CDC_RX_PATH_CLK_DISABLE 0 +#define WCD934X_CDC_RX1_RX_PATH_CFG0 0x0b56 +#define WCD934X_HPH_CMP_EN_MASK BIT(1) +#define WCD934X_HPH_CMP_ENABLE BIT(1) +#define WCD934X_HPH_CMP_DISABLE 0 +#define WCD934X_CDC_RX1_RX_PATH_CFG2 0x0b58 +#define WCD934X_CDC_RX1_RX_VOL_CTL 0x0b59 +#define WCD934X_CDC_RX1_RX_PATH_MIX_CTL 0x0b5a +#define WCD934X_CDC_RX1_RX_PATH_MIX_CFG 0x0b5b +#define WCD934X_CDC_RX1_RX_VOL_MIX_CTL 0x0b5c +#define WCD934X_CDC_RX1_RX_PATH_SEC0 0x0b5d +#define WCD934X_CDC_RX1_RX_PATH_SEC3 0x0b60 +#define WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_MASK GENMASK(5, 2) +#define WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_0P3125 0x14 +#define WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_0P0000 0 +#define WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL 0x0b67 +#define WCD934X_CDC_RX2_RX_PATH_CTL 0x0b69 +#define WCD934X_CDC_RX2_RX_PATH_CFG0 0x0b6a +#define WCD934X_CDC_RX_PATH_CFG_HD2_EN_MASK BIT(2) +#define WCD934X_CDC_RX_PATH_CFG_HD2_ENABLE BIT(2) +#define WCD934X_CDC_RX_PATH_CFG_HD2_DISABLE 0 +#define WCD934X_CDC_RX2_RX_PATH_CFG2 0x0b6c +#define WCD934X_CDC_RX2_RX_VOL_CTL 0x0b6d +#define WCD934X_CDC_RX2_RX_PATH_MIX_CTL 0x0b6e +#define WCD934X_CDC_RX2_RX_PATH_MIX_CFG 0x0b6f +#define WCD934X_CDC_RX2_RX_VOL_MIX_CTL 0x0b70 +#define WCD934X_CDC_RX2_RX_PATH_SEC0 0x0b71 +#define WCD934X_CDC_RX2_RX_PATH_SEC3 0x0b74 +#define WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL 0x0b7b +#define WCD934X_CDC_RX3_RX_PATH_CTL 0x0b7d +#define WCD934X_CDC_RX3_RX_PATH_CFG0 0x0b6e +#define WCD934X_CDC_RX3_RX_PATH_CFG2 0x0b80 +#define WCD934X_CDC_RX3_RX_VOL_CTL 0x0b81 +#define WCD934X_CDC_RX3_RX_PATH_MIX_CTL 0x0b82 +#define WCD934X_CDC_RX3_RX_PATH_MIX_CFG 0x0b83 +#define WCD934X_CDC_RX3_RX_VOL_MIX_CTL 0x0b84 +#define WCD934X_CDC_RX3_RX_PATH_SEC0 0x0b85 +#define WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL 0x0b8f +#define WCD934X_CDC_RX4_RX_PATH_CTL 0x0b91 +#define WCD934X_CDC_RX4_RX_PATH_CFG0 0x0b92 +#define WCD934X_CDC_RX4_RX_PATH_CFG2 0x0b94 +#define WCD934X_CDC_RX4_RX_VOL_CTL 0x0b95 +#define WCD934X_CDC_RX4_RX_PATH_MIX_CTL 0x0b96 +#define WCD934X_CDC_RX4_RX_PATH_MIX_CFG 0x0b97 +#define WCD934X_CDC_RX4_RX_VOL_MIX_CTL 0x0b98 +#define WCD934X_CDC_RX4_RX_PATH_SEC0 0x0b99 +#define WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL 0x0ba3 +#define WCD934X_CDC_RX7_RX_PATH_CTL 0x0bcd +#define WCD934X_CDC_RX7_RX_PATH_CFG0 0x0bce +#define WCD934X_CDC_RX7_RX_PATH_CFG1 0x0bcf +#define WCD934X_CDC_RX7_RX_PATH_CFG2 0x0bd0 +#define WCD934X_CDC_RX7_RX_VOL_CTL 0x0bd1 +#define WCD934X_CDC_RX7_RX_PATH_MIX_CTL 0x0bd2 +#define WCD934X_CDC_RX7_RX_PATH_MIX_CFG 0x0bd3 +#define WCD934X_CDC_RX7_RX_VOL_MIX_CTL 0x0bd4 +#define WCD934X_CDC_RX7_RX_PATH_SEC1 0x0bd6 +#define WCD934X_CDC_RX7_RX_PATH_MIX_SEC0 0x0bdd +#define WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL 0x0bdf +#define WCD934X_CDC_RX8_RX_PATH_CTL 0x0be1 +#define WCD934X_CDC_RX8_RX_PATH_CFG0 0x0be2 +#define WCD934X_CDC_RX8_RX_PATH_CFG1 0x0be3 +#define WCD934X_RX_SMART_BOOST_EN_MASK BIT(0) +#define WCD934X_RX_SMART_BOOST_ENABLE BIT(0) +#define WCD934X_RX_SMART_BOOST_DISABLE 0 +#define WCD934X_CDC_RX8_RX_PATH_CFG2 0x0be4 +#define WCD934X_CDC_RX8_RX_VOL_CTL 0x0be5 +#define WCD934X_CDC_RX8_RX_PATH_MIX_CTL 0x0be6 +#define WCD934X_CDC_RX8_RX_PATH_MIX_CFG 0x0be7 +#define WCD934X_CDC_RX8_RX_VOL_MIX_CTL 0x0be8 +#define WCD934X_CDC_RX8_RX_PATH_SEC1 0x0bea +#define WCD934X_CDC_RX8_RX_PATH_MIX_SEC0 0x0bf1 +#define WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL 0x0bf3 +#define WCD934X_CDC_CLSH_DECAY_CTRL 0x0c03 +#define WCD934X_CDC_CLSH_K2_MSB 0x0c0a +#define WCD934X_CDC_CLSH_K2_LSB 0x0c0b +#define WCD934X_CDC_CLSH_TEST0 0x0c0f +#define WCD934X_CDC_BOOST0_BOOST_PATH_CTL 0x0c19 +#define WCD934X_BOOST_PATH_CLK_EN_MASK BIT(4) +#define WCD934X_BOOST_PATH_CLK_ENABLE BIT(4) +#define WCD934X_BOOST_PATH_CLK_DISABLE 0 +#define WCD934X_CDC_BOOST0_BOOST_CTL 0x0c1a +#define WCD934X_CDC_BOOST0_BOOST_CFG1 0x0c1b +#define WCD934X_CDC_BOOST0_BOOST_CFG2 0x0c1c +#define WCD934X_CDC_BOOST1_BOOST_PATH_CTL 0x0c21 +#define WCD934X_CDC_BOOST1_BOOST_CTL 0x0c22 +#define WCD934X_CDC_BOOST1_BOOST_CFG1 0x0c23 +#define WCD934X_CDC_BOOST1_BOOST_CFG2 0x0c24 +#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_0 0x0c91 +#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_1 0x0c92 +#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_2 0x0c93 +#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_3 0x0c94 +#define WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS 0x0c96 +#define WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL 0x0cb5 +#define WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL 0x0cb9 +#define WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0 0x0d01 +#define WCD934X_CDC_RX_INP_MUX_RX_INT_CFG0(i) (0xd01 + i * 0x2) +#define WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK GENMASK(3, 0) +#define WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1 0x0d02 +#define WCD934X_CDC_RX_INP_MUX_RX_INT_CFG1(i) (0xd02 + i * 0x2) +#define WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0 0x0d03 +#define WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1 0x0d04 +#define WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0 0x0d05 +#define WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1 0x0d06 +#define WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0 0x0d07 +#define WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1 0x0d08 +#define WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0 0x0d09 +#define WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1 0x0d0a +#define WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0 0x0d0f +#define WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1 0x0d10 +#define WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0 0x0d11 +#define WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1 0x0d12 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0 0x0d13 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1 0x0d14 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2 0x0d15 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3 0x0d16 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4 0x0d17 +#define WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0 0x0d18 +#define WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1 0x0d19 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 0x0d1d +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 0x0d1e +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0 0x0d1f +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1 0x0d20 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0 0x0d21 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1 0x0d22 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0 0x0d23 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1 0x0d25 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 0x0d26 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0 0x0d27 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0 0x0d28 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0 0x0d29 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0 0x0d2a +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0 0x0d2b +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0 0x0d2c +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0 0x0d2d +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0 0x0d2e +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0 0x0d31 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1 0x0d32 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2 0x0d33 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3 0x0d34 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0 0x0d35 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1 0x0d36 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2 0x0d37 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3 0x0d38 +#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0 0x0d3a +#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1 0x0d3b +#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2 0x0d3c +#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3 0x0d3d +#define WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41 +#define WCD934X_CDC_MCLK_EN_MASK BIT(0) +#define WCD934X_CDC_MCLK_EN_ENABLE BIT(0) +#define WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42 +#define WCD934X_CDC_FS_MCLK_CNT_EN_MASK BIT(0) +#define WCD934X_CDC_FS_MCLK_CNT_ENABLE BIT(0) +#define WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL 0x0d43 +#define WCD934X_CDC_SWR_CLK_EN_MASK BIT(0) +#define WCD934X_CDC_SWR_CLK_ENABLE BIT(0) +#define WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL 0x0d44 +#define WCD934X_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL 0x0d45 +#define WCD934X_CDC_CLK_RST_CTRL_GFM_CONTROL 0x0d46 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL 0x0d55 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL 0x0d56 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL 0x0d57 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL 0x0d58 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL 0x0d59 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL 0x0d5a +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL 0x0d5b +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL 0x0d5c +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL 0x0d5d +#define WCD934X_CDC_SIDETONE_IIR0_IIR_CTL 0x0d5e +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL 0x0d5f +#define WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL 0x0d60 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL 0x0d61 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL 0x0d65 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL 0x0d66 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL 0x0d67 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL 0x0d68 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL 0x0d69 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL 0x0d6a +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL 0x0d6b +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL 0x0d6c +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL 0x0d6d +#define WCD934X_CDC_SIDETONE_IIR1_IIR_CTL 0x0d6e +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL 0x0d6f +#define WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL 0x0d70 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL 0x0d71 +#define WCD934X_CDC_TOP_TOP_CFG1 0x0d82 +#define WCD934X_CDC_TOP_TOP_CFG7 0x0d88 +#define WCD934X_CDC_TOP_HPHL_COMP_LUT 0x0d8b +#define WCD934X_CDC_TOP_HPHR_COMP_LUT 0x0d90 +#define WCD934X_HPH_LUT_BYPASS_MASK BIT(7) +#define WCD934X_HPH_LUT_BYPASS_ENABLE BIT(7) +#define WCD934X_HPH_LUT_BYPASS_DISABLE 0 +#define WCD934X_CODEC_CPR_WR_DATA_0 0x5001 +#define WCD934X_CODEC_CPR_WR_ADDR_0 0x5005 +#define WCD934X_CODEC_CPR_SVS_CX_VDD 0x5022 +#define WCD934X_CODEC_CPR_SVS2_CX_VDD 0x5023 +#define WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD 0x5027 +#define WCD934X_TLMM_DMIC1_CLK_PINCFG 0x8015 +#define WCD934X_TLMM_DMIC1_DATA_PINCFG 0x8016 +#define WCD934X_TLMM_DMIC2_CLK_PINCFG 0x8017 +#define WCD934X_TLMM_DMIC2_DATA_PINCFG 0x8018 +#define WCD934X_TLMM_DMIC3_CLK_PINCFG 0x8019 +#define WCD934X_TLMM_DMIC3_DATA_PINCFG 0x801a +#define WCD934X_TEST_DEBUG_PAD_DRVCTL_0 0x803b +#define WCD934X_TEST_DEBUG_NPL_DLY_TEST_1 0x803e + +#define WCD934X_MAX_REGISTER 0xffff +#define WCD934X_SEL_REGISTER 0x800 +#define WCD934X_SEL_MASK 0xff +#define WCD934X_SEL_SHIFT 0x0 +#define WCD934X_WINDOW_START 0x800 +#define WCD934X_WINDOW_LENGTH 0x100 + +/* SLIMBUS Slave Registers */ +#define WCD934X_SLIM_PGD_PORT_INT_EN0 0x30 +#define WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_0 0x34 +#define WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_1 0x35 +#define WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_0 0x36 +#define WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_1 0x37 +#define WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 0x38 +#define WCD934X_SLIM_PGD_PORT_INT_CLR_RX_1 0x39 +#define WCD934X_SLIM_PGD_PORT_INT_CLR_TX_0 0x3A +#define WCD934X_SLIM_PGD_PORT_INT_CLR_TX_1 0x3B +#define WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0 0x60 +#define WCD934X_SLIM_PGD_PORT_INT_TX_SOURCE0 0x70 +#define WCD934X_SLIM_PGD_RX_PORT_CFG(p) (0x30 + p) +#define WCD934X_SLIM_PGD_PORT_CFG(p) (0x40 + p) +#define WCD934X_SLIM_PGD_TX_PORT_CFG(p) (0x50 + p) +#define WCD934X_SLIM_PGD_PORT_INT_SRC(p) (0x60 + p) +#define WCD934X_SLIM_PGD_PORT_INT_STATUS(p) (0x80 + p) +#define WCD934X_SLIM_PGD_TX_PORT_MULTI_CHNL_0(p) (0x100 + 4 * p) +/* ports range from 10-16 */ +#define WCD934X_SLIM_PGD_TX_PORT_MULTI_CHNL_1(p) (0x101 + 4 * p) +#define WCD934X_SLIM_PGD_RX_PORT_MULTI_CHNL_0(p) (0x140 + 4 * p) + +#define SLIM_MANF_ID_QCOM 0x217 +#define SLIM_PROD_CODE_WCD9340 0x250 + +#endif diff --git a/include/linux/mfd/wcd934x/wcd934x.h b/include/linux/mfd/wcd934x/wcd934x.h new file mode 100644 index 000000000000..d102e211948c --- /dev/null +++ b/include/linux/mfd/wcd934x/wcd934x.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __WCD934X_H__ +#define __WCD934X_H__ +#include <linux/clk.h> +#include <linux/regulator/consumer.h> +#include <linux/regmap.h> +#include <linux/slimbus.h> + +#define WCD934X_MAX_SUPPLY 5 + +struct wcd934x_data { + int reset_gpio; + int irq; + struct device *dev; + struct clk *extclk; + struct regmap *regmap; + struct slim_device *sdev; + struct regmap_irq_chip_data *irq_data; + struct regulator_bulk_data supplies[WCD934X_MAX_SUPPLY]; +}; + +#endif /* __WCD934X_H__ */ + diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 229cc89f8c5a..59e70843c23a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -203,6 +203,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C select SND_SOC_WCD9335 if SLIMBUS + select SND_SOC_WSA881X if SOUNDWIRE + select SND_SOC_WCD934X if SLIMBUS select SND_SOC_WL1273 if MFD_WL1273_CORE select SND_SOC_WM0010 if SPI_MASTER select SND_SOC_WM1250_EV1 if I2C @@ -1233,6 +1235,24 @@ config SND_SOC_WCD9335 Qualcomm Technologies, Inc. (QTI) multimedia solutions, including the MSM8996, MSM8976, and MSM8956 chipsets. +config SND_SOC_WSA881X + tristate "WSA881X Codec" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + tristate + help + This enables support for Qualcomm WSA8810/WSA8815 Class-D + Smart Speaker Amplifier. + +config SND_SOC_WCD934X + tristate "WCD9340/WCD9341 Codec" + depends on SLIMBUS + select REGMAP_SLIMBUS + select REGMAP_IRQ + help + The WCD9340/9341 is a audio codec IC Integrated in + Qualcomm SoCs like SDM845. + config SND_SOC_WL1273 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index c498373dcc5f..93d07e01b73e 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -216,6 +216,8 @@ snd-soc-uda1334-objs := uda1334.o snd-soc-uda134x-objs := uda134x.o snd-soc-uda1380-objs := uda1380.o snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o +snd-soc-wsa881x-objs := wsa881x.o +snd-soc-wcd934x-objs := wcd-clsh-v2.o wcd934x.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o snd-soc-wm0010-objs := wm0010.o @@ -499,6 +501,8 @@ obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o +obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o +obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index b4bd59ccb8c3..047b74c57464 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -5191,11 +5191,11 @@ static const struct regmap_range_cfg wcd9335_ranges[] = { .name = "WCD9335", .range_min = 0x0, .range_max = WCD9335_MAX_REGISTER, - .selector_reg = WCD9335_REG(0x0, 0), + .selector_reg = WCD9335_SEL_REGISTER, .selector_mask = 0xff, .selector_shift = 0, - .window_start = 0x0, - .window_len = 0x1000, + .window_start = 0x800, + .window_len = 0x100, }, }; @@ -5233,12 +5233,12 @@ static const struct regmap_range_cfg wcd9335_ifc_ranges[] = { { .name = "WCD9335-IFC-DEV", .range_min = 0x0, - .range_max = WCD9335_REG(0, 0x7ff), - .selector_reg = WCD9335_REG(0, 0x0), - .selector_mask = 0xff, + .range_max = WCD9335_MAX_REGISTER, + .selector_reg = WCD9335_SEL_REGISTER, + .selector_mask = 0xfff, .selector_shift = 0, - .window_start = 0x0, - .window_len = 0x1000, + .window_start = 0x800, + .window_len = 0x400, }, }; diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h index 4d9be2496c30..72060824c743 100644 --- a/sound/soc/codecs/wcd9335.h +++ b/sound/soc/codecs/wcd9335.h @@ -8,9 +8,9 @@ * in slimbus mode the reg base starts from 0x800 * in i2s/i2c mode the reg base is 0x0 */ -#define WCD9335_REG(pg, r) ((pg << 12) | (r) | 0x800) +#define WCD9335_REG(pg, r) ((pg << 8) | (r)) #define WCD9335_REG_OFFSET(r) (r & 0xFF) -#define WCD9335_PAGE_OFFSET(r) ((r >> 12) & 0xFF) +#define WCD9335_PAGE_OFFSET(r) ((r >> 8) & 0xFF) /* Page-0 Registers */ #define WCD9335_PAGE0_PAGE_REGISTER WCD9335_REG(0x00, 0x000) @@ -600,7 +600,8 @@ #define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_ENABLE BIT(0) #define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_DISABLE 0 #define WCD9335_CDC_TOP_TOP_CFG1 WCD9335_REG(0x0d, 0x082) -#define WCD9335_MAX_REGISTER WCD9335_REG(0x80, 0x0FF) +#define WCD9335_MAX_REGISTER 0xffff +#define WCD9335_SEL_REGISTER 0x800 /* SLIMBUS Slave Registers */ #define WCD9335_SLIM_PGD_PORT_INT_EN0 WCD9335_REG(0, 0x30) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c new file mode 100644 index 000000000000..0f5361cb34f3 --- /dev/null +++ b/sound/soc/codecs/wcd934x.c @@ -0,0 +1,5218 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019, Linaro Limited + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/wcd934x/registers.h> +#include <linux/mfd/wcd934x/wcd934x.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_clk.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/slimbus.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include "wcd-clsh-v2.h" + +#define WCD934X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +/* Fractional Rates */ +#define WCD934X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400) +#define WCD934X_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +/* slave port water mark level + * (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes) + */ +#define SLAVE_PORT_WATER_MARK_6BYTES 0 +#define SLAVE_PORT_WATER_MARK_9BYTES 1 +#define SLAVE_PORT_WATER_MARK_12BYTES 2 +#define SLAVE_PORT_WATER_MARK_15BYTES 3 +#define SLAVE_PORT_WATER_MARK_SHIFT 1 +#define SLAVE_PORT_ENABLE 1 +#define SLAVE_PORT_DISABLE 0 +#define WCD934X_SLIM_WATER_MARK_VAL \ + ((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \ + (SLAVE_PORT_ENABLE)) + +#define WCD934X_SLIM_NUM_PORT_REG 3 +#define WCD934X_SLIM_PGD_PORT_INT_TX_EN0 (WCD934X_SLIM_PGD_PORT_INT_EN0 + 2) +#define WCD934X_SLIM_IRQ_OVERFLOW BIT(0) +#define WCD934X_SLIM_IRQ_UNDERFLOW BIT(1) +#define WCD934X_SLIM_IRQ_PORT_CLOSED BIT(2) + +#define WCD934X_MCLK_CLK_12P288MHZ 12288000 +#define WCD934X_MCLK_CLK_9P6MHZ 9600000 + +/* Only valid for 9.6 MHz mclk */ +#define WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ 2400000 +#define WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ 4800000 + +/* Only valid for 12.288 MHz mclk */ +#define WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ 4096000 + +#define WCD934X_DMIC_CLK_DIV_2 0x0 +#define WCD934X_DMIC_CLK_DIV_3 0x1 +#define WCD934X_DMIC_CLK_DIV_4 0x2 +#define WCD934X_DMIC_CLK_DIV_6 0x3 +#define WCD934X_DMIC_CLK_DIV_8 0x4 +#define WCD934X_DMIC_CLK_DIV_16 0x5 +#define WCD934X_DMIC_CLK_DRIVE_DEFAULT 0x02 + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +#define WCD934X_RX_START 16 +#define WCD934X_NUM_INTERPOLATORS 9 +#define WCD934X_RX_PATH_CTL_OFFSET 20 +#define WCD934X_MAX_VALID_ADC_MUX 13 +#define WCD934X_INVALID_ADC_MUX 9 + +#define WCD934X_SLIM_RX_CH(p) \ + {.port = p + WCD934X_RX_START, .shift = p,} + +#define WCD934X_SLIM_TX_CH(p) \ + {.port = p, .shift = p,} + +/* Feature masks to distinguish codec version */ +#define DSD_DISABLED_MASK 0 +#define SLNQ_DISABLED_MASK 1 + +#define DSD_DISABLED BIT(DSD_DISABLED_MASK) +#define SLNQ_DISABLED BIT(SLNQ_DISABLED_MASK) + +/* As fine version info cannot be retrieved before wcd probe. + * Define three coarse versions for possible future use before wcd probe. + */ +#define WCD_VERSION_WCD9340_1_0 0x400 +#define WCD_VERSION_WCD9341_1_0 0x410 +#define WCD_VERSION_WCD9340_1_1 0x401 +#define WCD_VERSION_WCD9341_1_1 0x411 +#define WCD934X_AMIC_PWR_LEVEL_LP 0 +#define WCD934X_AMIC_PWR_LEVEL_DEFAULT 1 +#define WCD934X_AMIC_PWR_LEVEL_HP 2 +#define WCD934X_AMIC_PWR_LEVEL_HYBRID 3 +#define WCD934X_AMIC_PWR_LVL_MASK 0x60 +#define WCD934X_AMIC_PWR_LVL_SHIFT 0x5 + +#define WCD934X_DEC_PWR_LVL_MASK 0x06 +#define WCD934X_DEC_PWR_LVL_LP 0x02 +#define WCD934X_DEC_PWR_LVL_HP 0x04 +#define WCD934X_DEC_PWR_LVL_DF 0x00 +#define WCD934X_DEC_PWR_LVL_HYBRID WCD934X_DEC_PWR_LVL_DF + +#define WCD934X_MAX_MICBIAS 4 +#define WCD934X_DEF_MICBIAS_MV 1800 +#define WCD934X_MAX_MICBIAS_MV 2850 + +#define WCD_IIR_FILTER_SIZE (sizeof(u32) * BAND_MAX) + +#define WCD_IIR_FILTER_CTL(xname, iidx, bidx) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = wcd934x_iir_filter_info, \ + .get = wcd934x_get_iir_band_audio_mixer, \ + .put = wcd934x_put_iir_band_audio_mixer, \ + .private_value = (unsigned long)&(struct wcd_iir_filter_ctl) { \ + .iir_idx = iidx, \ + .band_idx = bidx, \ + .bytes_ext = {.max = WCD_IIR_FILTER_SIZE, }, \ + } \ +} + +#define WCD934X_INTERPOLATOR_PATH(id) \ + {"RX INT" #id "_1 MIX1 INP0", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP0", "IIR0", "IIR0"}, \ + {"RX INT" #id "_1 MIX1 INP0", "IIR1", "IIR1"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP1", "IIR0", "IIR0"}, \ + {"RX INT" #id "_1 MIX1 INP1", "IIR1", "IIR1"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP2", "IIR0", "IIR0"}, \ + {"RX INT" #id "_1 MIX1 INP2", "IIR1", "IIR1"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP0"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP1"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP2"}, \ + {"RX INT" #id "_2 MUX", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_2 MUX", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_2 MUX", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_2 MUX", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_2 MUX", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_2 MUX", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_2 MUX", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_2 MUX", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_2 MUX", NULL, "INT" #id "_CLK"}, \ + {"RX INT" #id "_2 MUX", NULL, "DSMDEM" #id "_CLK"}, \ + {"RX INT" #id "_2 INTERP", NULL, "RX INT" #id "_2 MUX"}, \ + {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_2 INTERP"}, \ + {"RX INT" #id "_1 INTERP", NULL, "RX INT" #id "_1 MIX1"}, \ + {"RX INT" #id "_1 INTERP", NULL, "INT" #id "_CLK"}, \ + {"RX INT" #id "_1 INTERP", NULL, "DSMDEM" #id "_CLK"}, \ + {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_1 INTERP"} + +#define WCD934X_INTERPOLATOR_MIX2(id) \ + {"RX INT" #id " MIX2", NULL, "RX INT" #id " SEC MIX"}, \ + {"RX INT" #id " MIX2", NULL, "RX INT" #id " MIX2 INP"} + +#define WCD934X_SLIM_RX_AIF_PATH(id) \ + {"SLIM RX"#id" MUX", "AIF1_PB", "AIF1 PB"}, \ + {"SLIM RX"#id" MUX", "AIF2_PB", "AIF2 PB"}, \ + {"SLIM RX"#id" MUX", "AIF3_PB", "AIF3 PB"}, \ + {"SLIM RX"#id" MUX", "AIF4_PB", "AIF4 PB"}, \ + {"SLIM RX"#id, NULL, "SLIM RX"#id" MUX"} + +#define WCD934X_ADC_MUX(id) \ + {"ADC MUX" #id, "DMIC", "DMIC MUX" #id }, \ + {"ADC MUX" #id, "AMIC", "AMIC MUX" #id }, \ + {"DMIC MUX" #id, "DMIC0", "DMIC0"}, \ + {"DMIC MUX" #id, "DMIC1", "DMIC1"}, \ + {"DMIC MUX" #id, "DMIC2", "DMIC2"}, \ + {"DMIC MUX" #id, "DMIC3", "DMIC3"}, \ + {"DMIC MUX" #id, "DMIC4", "DMIC4"}, \ + {"DMIC MUX" #id, "DMIC5", "DMIC5"}, \ + {"AMIC MUX" #id, "ADC1", "ADC1"}, \ + {"AMIC MUX" #id, "ADC2", "ADC2"}, \ + {"AMIC MUX" #id, "ADC3", "ADC3"}, \ + {"AMIC MUX" #id, "ADC4", "ADC4"} + +#define WCD934X_IIR_INP_MUX(id) \ + {"IIR" #id, NULL, "IIR" #id " INP0 MUX"}, \ + {"IIR" #id " INP0 MUX", "DEC0", "ADC MUX0"}, \ + {"IIR" #id " INP0 MUX", "DEC1", "ADC MUX1"}, \ + {"IIR" #id " INP0 MUX", "DEC2", "ADC MUX2"}, \ + {"IIR" #id " INP0 MUX", "DEC3", "ADC MUX3"}, \ + {"IIR" #id " INP0 MUX", "DEC4", "ADC MUX4"}, \ + {"IIR" #id " INP0 MUX", "DEC5", "ADC MUX5"}, \ + {"IIR" #id " INP0 MUX", "DEC6", "ADC MUX6"}, \ + {"IIR" #id " INP0 MUX", "DEC7", "ADC MUX7"}, \ + {"IIR" #id " INP0 MUX", "DEC8", "ADC MUX8"}, \ + {"IIR" #id " INP0 MUX", "RX0", "SLIM RX0"}, \ + {"IIR" #id " INP0 MUX", "RX1", "SLIM RX1"}, \ + {"IIR" #id " INP0 MUX", "RX2", "SLIM RX2"}, \ + {"IIR" #id " INP0 MUX", "RX3", "SLIM RX3"}, \ + {"IIR" #id " INP0 MUX", "RX4", "SLIM RX4"}, \ + {"IIR" #id " INP0 MUX", "RX5", "SLIM RX5"}, \ + {"IIR" #id " INP0 MUX", "RX6", "SLIM RX6"}, \ + {"IIR" #id " INP0 MUX", "RX7", "SLIM RX7"}, \ + {"IIR" #id, NULL, "IIR" #id " INP1 MUX"}, \ + {"IIR" #id " INP1 MUX", "DEC0", "ADC MUX0"}, \ + {"IIR" #id " INP1 MUX", "DEC1", "ADC MUX1"}, \ + {"IIR" #id " INP1 MUX", "DEC2", "ADC MUX2"}, \ + {"IIR" #id " INP1 MUX", "DEC3", "ADC MUX3"}, \ + {"IIR" #id " INP1 MUX", "DEC4", "ADC MUX4"}, \ + {"IIR" #id " INP1 MUX", "DEC5", "ADC MUX5"}, \ + {"IIR" #id " INP1 MUX", "DEC6", "ADC MUX6"}, \ + {"IIR" #id " INP1 MUX", "DEC7", "ADC MUX7"}, \ + {"IIR" #id " INP1 MUX", "DEC8", "ADC MUX8"}, \ + {"IIR" #id " INP1 MUX", "RX0", "SLIM RX0"}, \ + {"IIR" #id " INP1 MUX", "RX1", "SLIM RX1"}, \ + {"IIR" #id " INP1 MUX", "RX2", "SLIM RX2"}, \ + {"IIR" #id " INP1 MUX", "RX3", "SLIM RX3"}, \ + {"IIR" #id " INP1 MUX", "RX4", "SLIM RX4"}, \ + {"IIR" #id " INP1 MUX", "RX5", "SLIM RX5"}, \ + {"IIR" #id " INP1 MUX", "RX6", "SLIM RX6"}, \ + {"IIR" #id " INP1 MUX", "RX7", "SLIM RX7"}, \ + {"IIR" #id, NULL, "IIR" #id " INP2 MUX"}, \ + {"IIR" #id " INP2 MUX", "DEC0", "ADC MUX0"}, \ + {"IIR" #id " INP2 MUX", "DEC1", "ADC MUX1"}, \ + {"IIR" #id " INP2 MUX", "DEC2", "ADC MUX2"}, \ + {"IIR" #id " INP2 MUX", "DEC3", "ADC MUX3"}, \ + {"IIR" #id " INP2 MUX", "DEC4", "ADC MUX4"}, \ + {"IIR" #id " INP2 MUX", "DEC5", "ADC MUX5"}, \ + {"IIR" #id " INP2 MUX", "DEC6", "ADC MUX6"}, \ + {"IIR" #id " INP2 MUX", "DEC7", "ADC MUX7"}, \ + {"IIR" #id " INP2 MUX", "DEC8", "ADC MUX8"}, \ + {"IIR" #id " INP2 MUX", "RX0", "SLIM RX0"}, \ + {"IIR" #id " INP2 MUX", "RX1", "SLIM RX1"}, \ + {"IIR" #id " INP2 MUX", "RX2", "SLIM RX2"}, \ + {"IIR" #id " INP2 MUX", "RX3", "SLIM RX3"}, \ + {"IIR" #id " INP2 MUX", "RX4", "SLIM RX4"}, \ + {"IIR" #id " INP2 MUX", "RX5", "SLIM RX5"}, \ + {"IIR" #id " INP2 MUX", "RX6", "SLIM RX6"}, \ + {"IIR" #id " INP2 MUX", "RX7", "SLIM RX7"}, \ + {"IIR" #id, NULL, "IIR" #id " INP3 MUX"}, \ + {"IIR" #id " INP3 MUX", "DEC0", "ADC MUX0"}, \ + {"IIR" #id " INP3 MUX", "DEC1", "ADC MUX1"}, \ + {"IIR" #id " INP3 MUX", "DEC2", "ADC MUX2"}, \ + {"IIR" #id " INP3 MUX", "DEC3", "ADC MUX3"}, \ + {"IIR" #id " INP3 MUX", "DEC4", "ADC MUX4"}, \ + {"IIR" #id " INP3 MUX", "DEC5", "ADC MUX5"}, \ + {"IIR" #id " INP3 MUX", "DEC6", "ADC MUX6"}, \ + {"IIR" #id " INP3 MUX", "DEC7", "ADC MUX7"}, \ + {"IIR" #id " INP3 MUX", "DEC8", "ADC MUX8"}, \ + {"IIR" #id " INP3 MUX", "RX0", "SLIM RX0"}, \ + {"IIR" #id " INP3 MUX", "RX1", "SLIM RX1"}, \ + {"IIR" #id " INP3 MUX", "RX2", "SLIM RX2"}, \ + {"IIR" #id " INP3 MUX", "RX3", "SLIM RX3"}, \ + {"IIR" #id " INP3 MUX", "RX4", "SLIM RX4"}, \ + {"IIR" #id " INP3 MUX", "RX5", "SLIM RX5"}, \ + {"IIR" #id " INP3 MUX", "RX6", "SLIM RX6"}, \ + {"IIR" #id " INP3 MUX", "RX7", "SLIM RX7"} + +#define WCD934X_SLIM_TX_AIF_PATH(id) \ + {"AIF1_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \ + {"AIF2_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \ + {"AIF3_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \ + {"SLIM TX" #id, NULL, "CDC_IF TX" #id " MUX"} + +enum { + MIC_BIAS_1 = 1, + MIC_BIAS_2, + MIC_BIAS_3, + MIC_BIAS_4 +}; + +enum { + MICB_PULLUP_ENABLE, + MICB_PULLUP_DISABLE, + MICB_ENABLE, + MICB_DISABLE, +}; + +enum { + SIDO_SOURCE_INTERNAL, + SIDO_SOURCE_RCO_BG, +}; + +enum { + INTERP_EAR = 0, + INTERP_HPHL, + INTERP_HPHR, + INTERP_LO1, + INTERP_LO2, + INTERP_LO3_NA, /* LO3 not avalible in Tavil */ + INTERP_LO4_NA, + INTERP_SPKR1, /*INT7 WSA Speakers via soundwire */ + INTERP_SPKR2, /*INT8 WSA Speakers via soundwire */ + INTERP_MAX, +}; + +enum { + WCD934X_RX0 = 0, + WCD934X_RX1, + WCD934X_RX2, + WCD934X_RX3, + WCD934X_RX4, + WCD934X_RX5, + WCD934X_RX6, + WCD934X_RX7, + WCD934X_RX8, + WCD934X_RX9, + WCD934X_RX10, + WCD934X_RX11, + WCD934X_RX12, + WCD934X_RX_MAX, +}; + +enum { + WCD934X_TX0 = 0, + WCD934X_TX1, + WCD934X_TX2, + WCD934X_TX3, + WCD934X_TX4, + WCD934X_TX5, + WCD934X_TX6, + WCD934X_TX7, + WCD934X_TX8, + WCD934X_TX9, + WCD934X_TX10, + WCD934X_TX11, + WCD934X_TX12, + WCD934X_TX13, + WCD934X_TX14, + WCD934X_TX15, + WCD934X_TX_MAX, +}; + +struct wcd934x_slim_ch { + u32 ch_num; + u16 port; + u16 shift; + struct list_head list; +}; + +static const struct wcd934x_slim_ch wcd934x_tx_chs[WCD934X_TX_MAX] = { + WCD934X_SLIM_TX_CH(0), + WCD934X_SLIM_TX_CH(1), + WCD934X_SLIM_TX_CH(2), + WCD934X_SLIM_TX_CH(3), + WCD934X_SLIM_TX_CH(4), + WCD934X_SLIM_TX_CH(5), + WCD934X_SLIM_TX_CH(6), + WCD934X_SLIM_TX_CH(7), + WCD934X_SLIM_TX_CH(8), + WCD934X_SLIM_TX_CH(9), + WCD934X_SLIM_TX_CH(10), + WCD934X_SLIM_TX_CH(11), + WCD934X_SLIM_TX_CH(12), + WCD934X_SLIM_TX_CH(13), + WCD934X_SLIM_TX_CH(14), + WCD934X_SLIM_TX_CH(15), +}; + +static const struct wcd934x_slim_ch wcd934x_rx_chs[WCD934X_RX_MAX] = { + WCD934X_SLIM_RX_CH(0), /* 16 */ + WCD934X_SLIM_RX_CH(1), /* 17 */ + WCD934X_SLIM_RX_CH(2), + WCD934X_SLIM_RX_CH(3), + WCD934X_SLIM_RX_CH(4), + WCD934X_SLIM_RX_CH(5), + WCD934X_SLIM_RX_CH(6), + WCD934X_SLIM_RX_CH(7), + WCD934X_SLIM_RX_CH(8), + WCD934X_SLIM_RX_CH(9), + WCD934X_SLIM_RX_CH(10), + WCD934X_SLIM_RX_CH(11), + WCD934X_SLIM_RX_CH(12), +}; + +/* Codec supports 2 IIR filters */ +enum { + IIR0 = 0, + IIR1, + IIR_MAX, +}; + +/* Each IIR has 5 Filter Stages */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +enum { + COMPANDER_1, /* HPH_L */ + COMPANDER_2, /* HPH_R */ + COMPANDER_3, /* LO1_DIFF */ + COMPANDER_4, /* LO2_DIFF */ + COMPANDER_5, /* LO3_SE - not used in Tavil */ + COMPANDER_6, /* LO4_SE - not used in Tavil */ + COMPANDER_7, /* SWR SPK CH1 */ + COMPANDER_8, /* SWR SPK CH2 */ + COMPANDER_MAX, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_PB, + AIF2_CAP, + AIF3_PB, + AIF3_CAP, + AIF4_PB, + AIF4_VIFEED, + AIF4_MAD_TX, + NUM_CODEC_DAIS, +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, + INTn_1_INP_SEL_IIR0, + INTn_1_INP_SEL_IIR1, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, + INTn_1_INP_SEL_RX2, + INTn_1_INP_SEL_RX3, + INTn_1_INP_SEL_RX4, + INTn_1_INP_SEL_RX5, + INTn_1_INP_SEL_RX6, + INTn_1_INP_SEL_RX7, +}; + +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, + INTn_2_INP_SEL_RX4, + INTn_2_INP_SEL_RX5, + INTn_2_INP_SEL_RX6, + INTn_2_INP_SEL_RX7, + INTn_2_INP_SEL_PROXIMITY, +}; + +enum { + INTERP_MAIN_PATH, + INTERP_MIX_PATH, +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate sr_val_tbl[] = { + {8000, 0x0}, + {16000, 0x1}, + {32000, 0x3}, + {48000, 0x4}, + {96000, 0x5}, + {192000, 0x6}, + {384000, 0x7}, + {44100, 0x9}, + {88200, 0xA}, + {176400, 0xB}, + {352800, 0xC}, +}; + +struct wcd_slim_codec_dai_data { + struct list_head slim_ch_list; + struct slim_stream_config sconfig; + struct slim_stream_runtime *sruntime; +}; + +static const struct regmap_range_cfg wcd934x_ifc_ranges[] = { + { + .name = "WCD9335-IFC-DEV", + .range_min = 0x0, + .range_max = 0xffff, + .selector_reg = 0x800, + .selector_mask = 0xfff, + .selector_shift = 0, + .window_start = 0x800, + .window_len = 0x400, + }, +}; + +static struct regmap_config wcd934x_ifc_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xffff, + .ranges = wcd934x_ifc_ranges, + .num_ranges = ARRAY_SIZE(wcd934x_ifc_ranges), +}; + +struct wcd934x_codec { + struct device *dev; + struct clk_hw hw; + struct clk *extclk; + struct regmap *regmap; + struct regmap *if_regmap; + struct slim_device *sdev; + struct slim_device *sidev; + struct wcd_clsh_ctrl *clsh_ctrl; + struct snd_soc_component *component; + struct wcd934x_slim_ch rx_chs[WCD934X_RX_MAX]; + struct wcd934x_slim_ch tx_chs[WCD934X_TX_MAX]; + struct wcd_slim_codec_dai_data dai[NUM_CODEC_DAIS]; + int rate; + u32 version; + u32 hph_mode; + int num_rx_port; + int num_tx_port; + + u32 tx_port_value[WCD934X_TX_MAX]; + u32 rx_port_value[WCD934X_RX_MAX]; + + int sido_input_src; + int dmic_0_1_clk_cnt; + int dmic_2_3_clk_cnt; + int dmic_4_5_clk_cnt; + + int dmic_sample_rate; + + int comp_enabled[COMPANDER_MAX]; + int micb_ref[WCD934X_MAX_MICBIAS]; + int pullup_ref[WCD934X_MAX_MICBIAS]; + + int sysclk_users; + struct mutex sysclk_mutex; +}; + +#define to_wcd934x_codec(_hw) container_of(_hw, struct wcd934x_codec, hw) + +struct wcd_iir_filter_ctl { + unsigned int iir_idx; + unsigned int band_idx; + struct soc_bytes_ext bytes_ext; +}; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); +static const DECLARE_TLV_DB_SCALE(ear_pa_gain, 0, 150, 0); + +/* Cutoff frequency for high pass filter */ +static const char * const cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" +}; + +static const char * const rx_cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ", + "CF_NEG_3DB_0P48HZ" +}; + +static const char * const rx_hph_mode_mux_text[] = { + "Class H Invalid", "Class-H Hi-Fi", "Class-H Low Power", "Class-AB", + "Class-H Hi-Fi Low Power" +}; + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB", +}; + +static const char * const rx_int0_7_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7", "PROXIMITY" +}; + +static const char * const rx_int_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7" +}; + +static const char * const rx_prim_mix_text[] = { + "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2", + "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_sidetone_mix_text[] = { + "ZERO", "SRC0", "SRC1", "SRC_SUM" +}; + +static const char * const iir_inp_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", + "DEC7", "DEC8", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "CLSH_DSM_OUT", +}; + +static const char * const rx_int0_1_interp_mux_text[] = { + "ZERO", "RX INT0_1 MIX1", +}; + +static const char * const rx_int1_1_interp_mux_text[] = { + "ZERO", "RX INT1_1 MIX1", +}; + +static const char * const rx_int2_1_interp_mux_text[] = { + "ZERO", "RX INT2_1 MIX1", +}; + +static const char * const rx_int3_1_interp_mux_text[] = { + "ZERO", "RX INT3_1 MIX1", +}; + +static const char * const rx_int4_1_interp_mux_text[] = { + "ZERO", "RX INT4_1 MIX1", +}; + +static const char * const rx_int7_1_interp_mux_text[] = { + "ZERO", "RX INT7_1 MIX1", +}; + +static const char * const rx_int8_1_interp_mux_text[] = { + "ZERO", "RX INT8_1 MIX1", +}; + +static const char * const rx_int0_2_interp_mux_text[] = { + "ZERO", "RX INT0_2 MUX", +}; + +static const char * const rx_int1_2_interp_mux_text[] = { + "ZERO", "RX INT1_2 MUX", +}; + +static const char * const rx_int2_2_interp_mux_text[] = { + "ZERO", "RX INT2_2 MUX", +}; + +static const char * const rx_int3_2_interp_mux_text[] = { + "ZERO", "RX INT3_2 MUX", +}; + +static const char * const rx_int4_2_interp_mux_text[] = { + "ZERO", "RX INT4_2 MUX", +}; + +static const char * const rx_int7_2_interp_mux_text[] = { + "ZERO", "RX INT7_2 MUX", +}; + +static const char * const rx_int8_2_interp_mux_text[] = { + "ZERO", "RX INT8_2 MUX", +}; + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5" +}; + +static const char * const amic_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "ADC4" +}; + +static const char * const amic4_5_sel_text[] = { + "AMIC4", "AMIC5" +}; + +static const char * const adc_mux_text[] = { + "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2" +}; + +static const char * const cdc_if_tx0_mux_text[] = { + "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192" +}; + +static const char * const cdc_if_tx1_mux_text[] = { + "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192" +}; + +static const char * const cdc_if_tx2_mux_text[] = { + "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192" +}; + +static const char * const cdc_if_tx3_mux_text[] = { + "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192" +}; + +static const char * const cdc_if_tx4_mux_text[] = { + "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192" +}; + +static const char * const cdc_if_tx5_mux_text[] = { + "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192" +}; + +static const char * const cdc_if_tx6_mux_text[] = { + "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192" +}; + +static const char * const cdc_if_tx7_mux_text[] = { + "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192" +}; + +static const char * const cdc_if_tx8_mux_text[] = { + "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192" +}; + +static const char * const cdc_if_tx9_mux_text[] = { + "ZERO", "DEC7", "DEC7_192" +}; + +static const char * const cdc_if_tx10_mux_text[] = { + "ZERO", "DEC6", "DEC6_192" +}; + +static const char * const cdc_if_tx11_mux_text[] = { + "DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST" +}; + +static const char * const cdc_if_tx11_inp1_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", + "DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12" +}; + +static const char * const cdc_if_tx13_mux_text[] = { + "CDC_DEC_5", "MAD_BRDCST" +}; + +static const char * const cdc_if_tx13_inp1_mux_text[] = { + "ZERO", "DEC5", "DEC5_192" +}; + +static const struct soc_enum cf_dec0_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX0_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX1_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec2_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX2_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec3_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX3_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec4_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX4_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec5_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX5_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec6_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX6_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec7_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX7_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec8_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX8_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_int0_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX0_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int1_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX1_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int2_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX2_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int3_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX3_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int4_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX4_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int7_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX7_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int8_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX8_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const struct soc_enum slim_rx_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text); + +static const struct soc_enum rx_int0_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, 10, + rx_int0_7_mix_mux_text); + +static const struct soc_enum rx_int1_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int2_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int3_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int4_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int7_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, 10, + rx_int0_7_mix_mux_text); + +static const struct soc_enum rx_int8_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int0_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int1_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int2_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int3_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int4_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int7_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, 4, + rx_sidetone_mix_text); + +static const struct soc_enum iir0_inp0_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir0_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir0_inp2_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir0_inp3_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir1_inp0_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir1_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir1_inp2_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir1_inp3_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum rx_int0_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX0_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int1_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX1_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int2_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX2_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum tx_adc_mux0_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux2_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux3_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux4_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 2, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux5_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 2, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux6_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 2, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux7_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 2, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux8_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 4, + ARRAY_SIZE(adc_mux_text), adc_mux_text); + +static const struct soc_enum rx_int0_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, + rx_int0_1_interp_mux_text); + +static const struct soc_enum rx_int1_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, + rx_int1_1_interp_mux_text); + +static const struct soc_enum rx_int2_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, + rx_int2_1_interp_mux_text); + +static const struct soc_enum rx_int3_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int3_1_interp_mux_text); + +static const struct soc_enum rx_int4_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int4_1_interp_mux_text); + +static const struct soc_enum rx_int7_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int7_1_interp_mux_text); + +static const struct soc_enum rx_int8_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int8_1_interp_mux_text); + +static const struct soc_enum rx_int0_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int0_2_interp_mux_text); + +static const struct soc_enum rx_int1_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int1_2_interp_mux_text); + +static const struct soc_enum rx_int2_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int2_2_interp_mux_text); + +static const struct soc_enum rx_int3_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int3_2_interp_mux_text); + +static const struct soc_enum rx_int4_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int4_2_interp_mux_text); + +static const struct soc_enum rx_int7_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int7_2_interp_mux_text); + +static const struct soc_enum rx_int8_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int8_2_interp_mux_text); + +static const struct soc_enum tx_dmic_mux0_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux2_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux3_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux4_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux5_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux6_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux7_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux8_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_amic_mux0_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux2_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux3_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux4_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux5_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux6_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux7_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux8_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, 5, + amic_mux_text); + +static const struct soc_enum tx_amic4_5_enum = + SOC_ENUM_SINGLE(WCD934X_TX_NEW_AMIC_4_5_SEL, 7, 2, + amic4_5_sel_text); + +static const struct soc_enum cdc_if_tx0_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 0, + ARRAY_SIZE(cdc_if_tx0_mux_text), cdc_if_tx0_mux_text); +static const struct soc_enum cdc_if_tx1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 2, + ARRAY_SIZE(cdc_if_tx1_mux_text), cdc_if_tx1_mux_text); +static const struct soc_enum cdc_if_tx2_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 4, + ARRAY_SIZE(cdc_if_tx2_mux_text), cdc_if_tx2_mux_text); +static const struct soc_enum cdc_if_tx3_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 6, + ARRAY_SIZE(cdc_if_tx3_mux_text), cdc_if_tx3_mux_text); +static const struct soc_enum cdc_if_tx4_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 0, + ARRAY_SIZE(cdc_if_tx4_mux_text), cdc_if_tx4_mux_text); +static const struct soc_enum cdc_if_tx5_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 2, + ARRAY_SIZE(cdc_if_tx5_mux_text), cdc_if_tx5_mux_text); +static const struct soc_enum cdc_if_tx6_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 4, + ARRAY_SIZE(cdc_if_tx6_mux_text), cdc_if_tx6_mux_text); +static const struct soc_enum cdc_if_tx7_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 6, + ARRAY_SIZE(cdc_if_tx7_mux_text), cdc_if_tx7_mux_text); +static const struct soc_enum cdc_if_tx8_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 0, + ARRAY_SIZE(cdc_if_tx8_mux_text), cdc_if_tx8_mux_text); +static const struct soc_enum cdc_if_tx9_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 2, + ARRAY_SIZE(cdc_if_tx9_mux_text), cdc_if_tx9_mux_text); +static const struct soc_enum cdc_if_tx10_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 4, + ARRAY_SIZE(cdc_if_tx10_mux_text), cdc_if_tx10_mux_text); +static const struct soc_enum cdc_if_tx11_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 0, + ARRAY_SIZE(cdc_if_tx11_inp1_mux_text), + cdc_if_tx11_inp1_mux_text); +static const struct soc_enum cdc_if_tx11_mux_enum = + SOC_ENUM_SINGLE(WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0, + ARRAY_SIZE(cdc_if_tx11_mux_text), cdc_if_tx11_mux_text); +static const struct soc_enum cdc_if_tx13_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 4, + ARRAY_SIZE(cdc_if_tx13_inp1_mux_text), + cdc_if_tx13_inp1_mux_text); +static const struct soc_enum cdc_if_tx13_mux_enum = + SOC_ENUM_SINGLE(WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0, + ARRAY_SIZE(cdc_if_tx13_mux_text), cdc_if_tx13_mux_text); + +static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, + int sido_src) +{ + if (sido_src == wcd->sido_input_src) + return 0; + + if (sido_src == SIDO_SOURCE_INTERNAL) { + regmap_update_bits(wcd->regmap, WCD934X_ANA_BUCK_CTL, + WCD934X_ANA_BUCK_HI_ACCU_EN_MASK, 0); + usleep_range(100, 110); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BUCK_CTL, + WCD934X_ANA_BUCK_HI_ACCU_PRE_ENX_MASK, 0x0); + usleep_range(100, 110); + regmap_update_bits(wcd->regmap, WCD934X_ANA_RCO, + WCD934X_ANA_RCO_BG_EN_MASK, 0); + usleep_range(100, 110); + } else if (sido_src == SIDO_SOURCE_RCO_BG) { + regmap_update_bits(wcd->regmap, WCD934X_ANA_RCO, + WCD934X_ANA_RCO_BG_EN_MASK, + WCD934X_ANA_RCO_BG_ENABLE); + usleep_range(100, 110); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BUCK_CTL, + WCD934X_ANA_BUCK_PRE_EN1_MASK, + WCD934X_ANA_BUCK_PRE_EN1_ENABLE); + usleep_range(100, 110); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BUCK_CTL, + WCD934X_ANA_BUCK_PRE_EN2_MASK, + WCD934X_ANA_BUCK_PRE_EN2_ENABLE); + usleep_range(100, 110); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BUCK_CTL, + WCD934X_ANA_BUCK_HI_ACCU_EN_MASK, + WCD934X_ANA_BUCK_HI_ACCU_ENABLE); + usleep_range(100, 110); + } + wcd->sido_input_src = sido_src; + + return 0; +} + +static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) +{ + mutex_lock(&wcd->sysclk_mutex); + + if (++wcd->sysclk_users != 1) { + mutex_unlock(&wcd->sysclk_mutex); + return 0; + } + mutex_unlock(&wcd->sysclk_mutex); + + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_BIAS_EN_MASK, + WCD934X_ANA_BIAS_EN); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_PRECHRG_EN_MASK, + WCD934X_ANA_PRECHRG_EN); + /* + * 1ms delay is required after pre-charge is enabled + * as per HW requirement + */ + usleep_range(1000, 1100); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_PRECHRG_EN_MASK, 0); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_PRECHRG_MODE_MASK, 0); + + /* + * In data clock contrl register is changed + * to CLK_SYS_MCLK_PRG + */ + + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_EXT_CLK_BUF_EN_MASK, + WCD934X_EXT_CLK_BUF_EN); + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_EXT_CLK_DIV_RATIO_MASK, + WCD934X_EXT_CLK_DIV_BY_2); + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_MCLK_SRC_MASK, + WCD934X_MCLK_SRC_EXT_CLK); + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_MCLK_EN_MASK, WCD934X_MCLK_EN); + regmap_update_bits(wcd->regmap, + WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + WCD934X_CDC_FS_MCLK_CNT_EN_MASK, + WCD934X_CDC_FS_MCLK_CNT_ENABLE); + regmap_update_bits(wcd->regmap, + WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, + WCD934X_MCLK_EN_MASK, + WCD934X_MCLK_EN); + regmap_update_bits(wcd->regmap, WCD934X_CODEC_RPM_CLK_GATE, + WCD934X_CODEC_RPM_CLK_GATE_MASK, 0x0); + /* + * 10us sleep is required after clock is enabled + * as per HW requirement + */ + usleep_range(10, 15); + + wcd934x_set_sido_input_src(wcd, SIDO_SOURCE_RCO_BG); + + return 0; +} + +static int wcd934x_disable_ana_bias_and_syclk(struct wcd934x_codec *wcd) +{ + mutex_lock(&wcd->sysclk_mutex); + if (--wcd->sysclk_users != 0) { + mutex_unlock(&wcd->sysclk_mutex); + return 0; + } + mutex_unlock(&wcd->sysclk_mutex); + + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_EXT_CLK_BUF_EN_MASK | + WCD934X_MCLK_EN_MASK, 0x0); + wcd934x_set_sido_input_src(wcd, SIDO_SOURCE_INTERNAL); + + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_BIAS_EN_MASK, 0); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_PRECHRG_EN_MASK, 0); + + return 0; +} + +static int __wcd934x_cdc_mclk_enable(struct wcd934x_codec *wcd, + bool enable) +{ + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(wcd->extclk); + + if (ret) { + dev_err(wcd->dev, "%s: ext clk enable failed\n", + __func__); + return ret; + } + ret = wcd934x_enable_ana_bias_and_sysclk(wcd); + } else { + int val; + + regmap_read(wcd->regmap, WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + &val); + + /* Don't disable clock if soundwire using it.*/ + if (val & WCD934X_CDC_SWR_CLK_EN_MASK) + return 0; + + wcd934x_disable_ana_bias_and_syclk(wcd); + clk_disable_unprepare(wcd->extclk); + } + + return ret; +} + +static int wcd934x_codec_enable_mclk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return __wcd934x_cdc_mclk_enable(wcd, true); + case SND_SOC_DAPM_POST_PMD: + return __wcd934x_cdc_mclk_enable(wcd, false); + } + return 0; +} + +static int wcd934x_get_version(struct wcd934x_codec *wcd) +{ + int val1, val2, ver, ret; + struct regmap *regmap; + u16 id_minor; + u32 version_mask = 0; + + regmap = wcd->regmap; + ver = 0; + + ret = regmap_bulk_read(regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, + (u8 *)&id_minor, sizeof(u16)); + + if (ret) + return ret; + + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val1); + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val2); + + version_mask |= (!!((u8)val1 & 0x80)) << DSD_DISABLED_MASK; + version_mask |= (!!((u8)val2 & 0x01)) << SLNQ_DISABLED_MASK; + + switch (version_mask) { + case DSD_DISABLED | SLNQ_DISABLED: + if (id_minor == 0) + ver = WCD_VERSION_WCD9340_1_0; + else if (id_minor == 0x01) + ver = WCD_VERSION_WCD9340_1_1; + break; + case SLNQ_DISABLED: + if (id_minor == 0) + ver = WCD_VERSION_WCD9341_1_0; + else if (id_minor == 0x01) + ver = WCD_VERSION_WCD9341_1_1; + break; + } + + wcd->version = ver; + dev_info(wcd->dev, "WCD934X Minor:0x%x Version:0x%x\n", id_minor, ver); + + return 0; +} + +static void wcd934x_enable_efuse_sensing(struct wcd934x_codec *wcd) +{ + int rc, val; + + __wcd934x_cdc_mclk_enable(wcd, true); + + regmap_update_bits(wcd->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, + WCD934X_EFUSE_SENSE_STATE_MASK, + WCD934X_EFUSE_SENSE_STATE_DEF); + regmap_update_bits(wcd->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, + WCD934X_EFUSE_SENSE_EN_MASK, + WCD934X_EFUSE_SENSE_ENABLE); + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + wcd934x_set_sido_input_src(wcd, SIDO_SOURCE_RCO_BG); + + rc = regmap_read(wcd->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS, &val); + if (rc || (!(val & 0x01))) + WARN(1, "%s: Efuse sense is not complete val=%x, ret=%d\n", + __func__, val, rc); + + __wcd934x_cdc_mclk_enable(wcd, false); +} + +static int wcd934x_swrm_clock(struct wcd934x_codec *wcd, bool enable) +{ + if (enable) { + __wcd934x_cdc_mclk_enable(wcd, true); + regmap_update_bits(wcd->regmap, + WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + WCD934X_CDC_SWR_CLK_EN_MASK, + WCD934X_CDC_SWR_CLK_ENABLE); + } else { + regmap_update_bits(wcd->regmap, + WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + WCD934X_CDC_SWR_CLK_EN_MASK, 0); + __wcd934x_cdc_mclk_enable(wcd, false); + } + + return 0; +} + +static int wcd934x_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_val, + u32 rate) +{ + struct snd_soc_component *comp = dai->component; + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + struct wcd934x_slim_ch *ch; + u8 cfg0, cfg1, inp0_sel, inp1_sel, inp2_sel; + int inp, j; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + inp = ch->shift + INTn_1_INP_SEL_RX0; + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the slim rx port + * is connected + */ + for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) { + /* Interpolators 5 and 6 are not aviliable in Tavil */ + if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) + continue; + + cfg0 = snd_soc_component_read32(comp, + WCD934X_CDC_RX_INP_MUX_RX_INT_CFG0(j)); + cfg1 = snd_soc_component_read32(comp, + WCD934X_CDC_RX_INP_MUX_RX_INT_CFG1(j)); + + inp0_sel = cfg0 & + WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + inp1_sel = (cfg0 >> 4) & + WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + inp2_sel = (cfg1 >> 4) & + WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + + if ((inp0_sel == inp) || (inp1_sel == inp) || + (inp2_sel == inp)) { + /* rate is in Hz */ + /* + * Ear and speaker primary path does not support + * native sample rates + */ + if ((j == INTERP_EAR || j == INTERP_SPKR1 || + j == INTERP_SPKR2) && rate == 44100) + dev_err(wcd->dev, + "Cannot set 44.1KHz on INT%d\n", + j); + else + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX_PATH_CTL(j), + WCD934X_CDC_MIX_PCM_RATE_MASK, + rate_val); + } + } + } + + return 0; +} + +static int wcd934x_set_mix_interpolator_rate(struct snd_soc_dai *dai, + int rate_val, + u32 rate) +{ + struct snd_soc_component *component = dai->component; + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + struct wcd934x_slim_ch *ch; + int val, j; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) { + /* Interpolators 5 and 6 are not aviliable in Tavil */ + if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) + continue; + val = snd_soc_component_read32(component, + WCD934X_CDC_RX_INP_MUX_RX_INT_CFG1(j)) & + WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + + if (val == (ch->shift + INTn_2_INP_SEL_RX0)) { + /* + * Ear mix path supports only 48, 96, 192, + * 384KHz only + */ + if ((j == INTERP_EAR) && + (rate_val < 0x4 || + rate_val > 0x7)) { + dev_err(component->dev, + "Invalid rate for AIF_PB DAI(%d)\n", + dai->id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, + WCD934X_CDC_RX_PATH_MIX_CTL(j), + WCD934X_CDC_MIX_PCM_RATE_MASK, + rate_val); + } + } + } + + return 0; +} + +static int wcd934x_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + int rate_val = 0; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) { + if (sample_rate == sr_val_tbl[i].sample_rate) { + rate_val = sr_val_tbl[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(sr_val_tbl)) || (rate_val < 0)) { + dev_err(dai->dev, "Unsupported sample rate: %d\n", sample_rate); + return -EINVAL; + } + + ret = wcd934x_set_prim_interpolator_rate(dai, (u8)rate_val, + sample_rate); + if (ret) + return ret; + ret = wcd934x_set_mix_interpolator_rate(dai, (u8)rate_val, + sample_rate); + if (ret) + return ret; + + return ret; +} + +static int wcd934x_set_decimator_rate(struct snd_soc_dai *dai, + u8 rate_val, u32 rate) +{ + struct snd_soc_component *comp = dai->component; + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp); + u8 shift = 0, shift_val = 0, tx_mux_sel; + struct wcd934x_slim_ch *ch; + int tx_port, tx_port_reg; + int decimator = -1; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + tx_port = ch->port; + /* Find the SB TX MUX input - which decimator is connected */ + switch (tx_port) { + case 0 ... 3: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0; + shift = (tx_port << 1); + shift_val = 0x03; + break; + case 4 ... 7: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1; + shift = ((tx_port - 4) << 1); + shift_val = 0x03; + break; + case 8 ... 10: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2; + shift = ((tx_port - 8) << 1); + shift_val = 0x03; + break; + case 11: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 0; + shift_val = 0x0F; + break; + case 13: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 4; + shift_val = 0x03; + break; + default: + dev_err(wcd->dev, "Invalid SLIM TX%u port DAI ID:%d\n", + tx_port, dai->id); + return -EINVAL; + } + + tx_mux_sel = snd_soc_component_read32(comp, tx_port_reg) & + (shift_val << shift); + + tx_mux_sel = tx_mux_sel >> shift; + switch (tx_port) { + case 0 ... 8: + if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3)) + decimator = tx_port; + break; + case 9 ... 10: + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = ((tx_port == 9) ? 7 : 6); + break; + case 11: + if ((tx_mux_sel >= 1) && (tx_mux_sel < 7)) + decimator = tx_mux_sel - 1; + break; + case 13: + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = 5; + break; + default: + dev_err(wcd->dev, "ERROR: Invalid tx_port: %d\n", + tx_port); + return -EINVAL; + } + + snd_soc_component_update_bits(comp, + WCD934X_CDC_TX_PATH_CTL(decimator), + WCD934X_CDC_TX_PATH_CTL_PCM_RATE_MASK, + rate_val); + } + + return 0; +} + +static int wcd934x_slim_set_hw_params(struct wcd934x_codec *wcd, + struct wcd_slim_codec_dai_data *dai_data, + int direction) +{ + struct list_head *slim_ch_list = &dai_data->slim_ch_list; + struct slim_stream_config *cfg = &dai_data->sconfig; + struct wcd934x_slim_ch *ch; + u16 payload = 0; + int ret, i; + + cfg->ch_count = 0; + cfg->direction = direction; + cfg->port_mask = 0; + + /* Configure slave interface device */ + list_for_each_entry(ch, slim_ch_list, list) { + cfg->ch_count++; + payload |= 1 << ch->shift; + cfg->port_mask |= BIT(ch->port); + } + + cfg->chs = kcalloc(cfg->ch_count, sizeof(unsigned int), GFP_KERNEL); + if (!cfg->chs) + return -ENOMEM; + + i = 0; + list_for_each_entry(ch, slim_ch_list, list) { + cfg->chs[i++] = ch->ch_num; + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + /* write to interface device */ + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_RX_PORT_MULTI_CHNL_0(ch->port), + payload); + + if (ret < 0) + goto err; + + /* configure the slave port for water mark and enable*/ + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_RX_PORT_CFG(ch->port), + WCD934X_SLIM_WATER_MARK_VAL); + if (ret < 0) + goto err; + } else { + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_TX_PORT_MULTI_CHNL_0(ch->port), + payload & 0x00FF); + if (ret < 0) + goto err; + + /* ports 8,9 */ + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_TX_PORT_MULTI_CHNL_1(ch->port), + (payload & 0xFF00) >> 8); + if (ret < 0) + goto err; + + /* configure the slave port for water mark and enable*/ + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_TX_PORT_CFG(ch->port), + WCD934X_SLIM_WATER_MARK_VAL); + + if (ret < 0) + goto err; + } + } + + dai_data->sruntime = slim_stream_allocate(wcd->sdev, "WCD934x-SLIM"); + + return 0; + +err: + dev_err(wcd->dev, "Error Setting slim hw params\n"); + kfree(cfg->chs); + cfg->chs = NULL; + + return ret; +} + +static int wcd934x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wcd934x_codec *wcd; + int ret, tx_fs_rate = 0; + + wcd = snd_soc_component_get_drvdata(dai->component); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = wcd934x_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(wcd->dev, "cannot set sample rate: %u\n", + params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16 ... 24: + wcd->dai[dai->id].sconfig.bps = params_width(params); + break; + default: + dev_err(wcd->dev, "Invalid format 0x%x\n", + params_width(params)); + return -EINVAL; + } + break; + + case SNDRV_PCM_STREAM_CAPTURE: + switch (params_rate(params)) { + 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(wcd->dev, "Invalid TX sample rate: %d\n", + params_rate(params)); + return -EINVAL; + + }; + + ret = wcd934x_set_decimator_rate(dai, tx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(wcd->dev, "Cannot set TX Decimator rate\n"); + return ret; + } + switch (params_width(params)) { + case 16 ... 32: + wcd->dai[dai->id].sconfig.bps = params_width(params); + break; + default: + dev_err(wcd->dev, "Invalid format 0x%x\n", + params_width(params)); + return -EINVAL; + }; + break; + default: + dev_err(wcd->dev, "Invalid stream type %d\n", + substream->stream); + return -EINVAL; + }; + + wcd->dai[dai->id].sconfig.rate = params_rate(params); + wcd934x_slim_set_hw_params(wcd, &wcd->dai[dai->id], substream->stream); + + return 0; +} + +static int wcd934x_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct wcd_slim_codec_dai_data *dai_data; + struct wcd934x_codec *wcd; + struct slim_stream_config *cfg; + + wcd = snd_soc_component_get_drvdata(dai->component); + + dai_data = &wcd->dai[dai->id]; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + cfg = &dai_data->sconfig; + slim_stream_prepare(dai_data->sruntime, cfg); + slim_stream_enable(dai_data->sruntime); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + slim_stream_unprepare(dai_data->sruntime); + slim_stream_disable(dai_data->sruntime); + break; + default: + break; + } + + return 0; +} + +static int wcd934x_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct wcd934x_codec *wcd; + int i; + + wcd = snd_soc_component_get_drvdata(dai->component); + + if (!tx_slot || !rx_slot) { + dev_err(wcd->dev, "Invalid tx_slot=%p, rx_slot=%p\n", + tx_slot, rx_slot); + return -EINVAL; + } + + if (wcd->rx_chs) { + wcd->num_rx_port = rx_num; + for (i = 0; i < rx_num; i++) { + wcd->rx_chs[i].ch_num = rx_slot[i]; + INIT_LIST_HEAD(&wcd->rx_chs[i].list); + } + } + + if (wcd->tx_chs) { + wcd->num_tx_port = tx_num; + for (i = 0; i < tx_num; i++) { + wcd->tx_chs[i].ch_num = tx_slot[i]; + INIT_LIST_HEAD(&wcd->tx_chs[i].list); + } + } + + return 0; +} + +static int wcd934x_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 wcd934x_slim_ch *ch; + struct wcd934x_codec *wcd; + int i = 0; + + wcd = snd_soc_component_get_drvdata(dai->component); + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + case AIF4_PB: + if (!rx_slot || !rx_num) { + dev_err(wcd->dev, "Invalid rx_slot %p or rx_num %p\n", + rx_slot, rx_num); + return -EINVAL; + } + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) + rx_slot[i++] = ch->ch_num; + + *rx_num = i; + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + if (!tx_slot || !tx_num) { + dev_err(wcd->dev, "Invalid tx_slot %p or tx_num %p\n", + tx_slot, tx_num); + return -EINVAL; + } + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) + tx_slot[i++] = ch->ch_num; + + *tx_num = i; + break; + default: + dev_err(wcd->dev, "Invalid DAI ID %x\n", dai->id); + break; + } + + return 0; +} + +static struct snd_soc_dai_ops wcd934x_dai_ops = { + .hw_params = wcd934x_hw_params, + .trigger = wcd934x_trigger, + .set_channel_map = wcd934x_set_channel_map, + .get_channel_map = wcd934x_get_channel_map, +}; + +static struct snd_soc_dai_driver wcd934x_slim_dais[] = { + [0] = { + .name = "wcd934x_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd934x_dai_ops, + }, + [1] = { + .name = "wcd934x_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD934X_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd934x_dai_ops, + }, + [2] = { + .name = "wcd934x_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd934x_dai_ops, + }, + [3] = { + .name = "wcd934x_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD934X_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd934x_dai_ops, + }, + [4] = { + .name = "wcd934x_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd934x_dai_ops, + }, + [5] = { + .name = "wcd934x_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD934X_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd934x_dai_ops, + }, + [6] = { + .name = "wcd934x_rx4", + .id = AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd934x_dai_ops, + }, +}; + +static int swclk_gate_enable(struct clk_hw *hw) +{ + return wcd934x_swrm_clock(to_wcd934x_codec(hw), true); +} + +static void swclk_gate_disable(struct clk_hw *hw) +{ + wcd934x_swrm_clock(to_wcd934x_codec(hw), false); +} + +static int swclk_gate_is_enabled(struct clk_hw *hw) +{ + struct wcd934x_codec *wcd = to_wcd934x_codec(hw); + int ret, val; + + regmap_read(wcd->regmap, WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, &val); + ret = val & WCD934X_CDC_SWR_CLK_EN_MASK; + + 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 *wcd934x_register_mclk_output(struct wcd934x_codec *wcd) +{ + struct clk *parent = wcd->extclk; + struct device *dev = wcd->dev; + struct device_node *np = dev->parent->of_node; + const char *parent_clk_name = NULL; + const char *clk_name = "mclk"; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (of_property_read_u32(np, "clock-frequency", &wcd->rate)) + return NULL; + + parent_clk_name = __clk_get_name(parent); + + of_property_read_string(np, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = &swclk_gate_ops; + init.flags = 0; + init.parent_names = &parent_clk_name; + init.num_parents = 1; + wcd->hw.init = &init; + + hw = &wcd->hw; + ret = clk_hw_register(wcd->dev->parent, hw); + if (ret) + return ERR_PTR(ret); + + of_clk_add_provider(np, of_clk_src_simple_get, hw->clk); + + return NULL; +} + +static int wcd934x_get_micbias_val(struct device *dev, const char *micbias) +{ + int mv; + + if (of_property_read_u32(dev->parent->of_node, micbias, &mv)) { + dev_err(dev, "%s value not found, using default\n", micbias); + mv = WCD934X_DEF_MICBIAS_MV; + } + + if (mv < 1000 || mv > 2850) { + dev_err(dev, "%s value not in valid range, using default\n", + micbias); + mv = WCD934X_DEF_MICBIAS_MV; + } + + return (mv - 1000) / 50; +} + +static int wcd934x_init_dmic(struct snd_soc_component *comp) +{ + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + u32 def_dmic_rate, dmic_clk_drv; + + vout_ctl_1 = wcd934x_get_micbias_val(comp->dev, + "qcom,mibias1-millivolt"); + vout_ctl_2 = wcd934x_get_micbias_val(comp->dev, + "qcom,mibias2-millivolt"); + vout_ctl_3 = wcd934x_get_micbias_val(comp->dev, + "qcom,mibias3-millivolt"); + vout_ctl_4 = wcd934x_get_micbias_val(comp->dev, + "qcom,mibias4-millivolt"); + + snd_soc_component_update_bits(comp, WCD934X_ANA_MICB1, + WCD934X_MICB_VAL_MASK, vout_ctl_1); + snd_soc_component_update_bits(comp, WCD934X_ANA_MICB2, + WCD934X_MICB_VAL_MASK, vout_ctl_2); + snd_soc_component_update_bits(comp, WCD934X_ANA_MICB3, + WCD934X_MICB_VAL_MASK, vout_ctl_3); + snd_soc_component_update_bits(comp, WCD934X_ANA_MICB4, + WCD934X_MICB_VAL_MASK, vout_ctl_4); + + if (wcd->rate == WCD934X_MCLK_CLK_9P6MHZ) + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + else + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ; + + wcd->dmic_sample_rate = def_dmic_rate; + + dmic_clk_drv = 0; + snd_soc_component_update_bits(comp, WCD934X_TEST_DEBUG_PAD_DRVCTL_0, + 0x0C, dmic_clk_drv << 2); + + return 0; +} + +static void wcd934x_hw_init(struct wcd934x_codec *wcd) +{ + struct regmap *rm = wcd->regmap; + + /* set SPKR rate to FS_2P4_3P072 */ + regmap_update_bits(rm, WCD934X_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08); + regmap_update_bits(rm, WCD934X_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08); + + /* Take DMICs out of reset */ + regmap_update_bits(rm, WCD934X_CPE_SS_DMIC_CFG, 0x80, 0x00); +} + +static int wcd934x_comp_init(struct snd_soc_component *component) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + + wcd934x_hw_init(wcd); + wcd934x_enable_efuse_sensing(wcd); + wcd934x_get_version(wcd); + + return 0; +} + +static irqreturn_t wcd934x_slim_irq_handler(int irq, void *data) +{ + struct wcd934x_codec *wcd = data; + unsigned long status = 0; + int i, j, port_id; + unsigned int val, int_val = 0; + irqreturn_t ret = IRQ_NONE; + bool tx; + unsigned short reg = 0; + + for (i = WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0; + i <= WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) { + regmap_read(wcd->if_regmap, i, &val); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = false; + port_id = j; + + if (j >= 16) { + tx = true; + port_id = j - 16; + } + + regmap_read(wcd->if_regmap, + WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0 + j, &val); + if (val) { + if (!tx) + reg = WCD934X_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + regmap_read(wcd->if_regmap, reg, &int_val); + } + + if (val & WCD934X_SLIM_IRQ_OVERFLOW) + dev_err_ratelimited(wcd->dev, + "overflow error on %s port %d, value %x\n", + (tx ? "TX" : "RX"), port_id, val); + + if (val & WCD934X_SLIM_IRQ_UNDERFLOW) + dev_err_ratelimited(wcd->dev, + "underflow error on %s port %d, value %x\n", + (tx ? "TX" : "RX"), port_id, val); + + if ((val & WCD934X_SLIM_IRQ_OVERFLOW) || + (val & WCD934X_SLIM_IRQ_UNDERFLOW)) { + if (!tx) + reg = WCD934X_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + regmap_read( + wcd->if_regmap, reg, &int_val); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + regmap_write(wcd->if_regmap, + reg, int_val); + } + } + + if (val & WCD934X_SLIM_IRQ_PORT_CLOSED) + dev_err_ratelimited(wcd->dev, + "Port Closed %s port %d, value %x\n", + (tx ? "TX" : "RX"), port_id, val); + + regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 + (j / 8), + BIT(j % 8)); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int wcd934x_comp_probe(struct snd_soc_component *component) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + int i; + + snd_soc_component_init_regmap(component, wcd->regmap); + wcd->component = component; + + /* Class-H Init*/ + wcd->clsh_ctrl = wcd_clsh_ctrl_alloc(component, wcd->version); + if (IS_ERR(wcd->clsh_ctrl)) + return PTR_ERR(wcd->clsh_ctrl); + + /* Default HPH Mode to Class-H Low HiFi */ + wcd->hph_mode = CLS_H_LOHIFI; + + wcd934x_comp_init(component); + + for (i = 0; i < NUM_CODEC_DAIS; i++) + INIT_LIST_HEAD(&wcd->dai[i].slim_ch_list); + + wcd934x_init_dmic(component); + return 0; +} + +static void wcd934x_comp_remove(struct snd_soc_component *comp) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + + wcd_clsh_ctrl_free(wcd->clsh_ctrl); +} + +static int wcd934x_comp_set_sysclk(struct snd_soc_component *comp, + int clk_id, int source, + unsigned int freq, int dir) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int val = WCD934X_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ; + + wcd->rate = freq; + + if (wcd->rate == WCD934X_MCLK_CLK_12P288MHZ) + val = WCD934X_CODEC_RPM_CLK_MCLK_CFG_12P288MHZ; + + snd_soc_component_update_bits(comp, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + WCD934X_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK, + val); + + return clk_set_rate(wcd->extclk, freq); +} + +static uint32_t get_iir_band_coeff(struct snd_soc_component *component, + int iir_idx, int band_idx, + int coeff_idx) +{ + u32 value = 0; + int reg, b2_reg; + + /* Address does not automatically update if reading */ + reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx; + b2_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx; + + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) * + sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_component_read32(component, b2_reg); + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_component_read32(component, b2_reg) << 8); + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_component_read32(component, b2_reg) << 16); + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= (snd_soc_component_read32(component, b2_reg) << 24); + return value; +} + +static void set_iir_band_coeff(struct snd_soc_component *component, + int iir_idx, int band_idx, + uint32_t value) +{ + int reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx; + + snd_soc_component_write(component, reg, (value & 0xFF)); + snd_soc_component_write(component, reg, (value >> 8) & 0xFF); + snd_soc_component_write(component, reg, (value >> 16) & 0xFF); + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_component_write(component, reg, (value >> 24) & 0x3F); +} + +static int wcd934x_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + int iir_idx = ctl->iir_idx; + int band_idx = ctl->band_idx; + u32 coeff[BAND_MAX]; + int reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx; + + memcpy(&coeff[0], ucontrol->value.bytes.data, params->max); + + /* Mask top bit it is reserved */ + /* Updates addr automatically for each B2 write */ + snd_soc_component_write(component, reg, (band_idx * BAND_MAX * + sizeof(uint32_t)) & 0x7F); + + set_iir_band_coeff(component, iir_idx, band_idx, coeff[0]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[1]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[2]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[3]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[4]); + + return 0; +} + +static int wcd934x_get_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + int iir_idx = ctl->iir_idx; + int band_idx = ctl->band_idx; + u32 coeff[BAND_MAX]; + + coeff[0] = get_iir_band_coeff(component, iir_idx, band_idx, 0); + coeff[1] = get_iir_band_coeff(component, iir_idx, band_idx, 1); + coeff[2] = get_iir_band_coeff(component, iir_idx, band_idx, 2); + coeff[3] = get_iir_band_coeff(component, iir_idx, band_idx, 3); + coeff[4] = get_iir_band_coeff(component, iir_idx, band_idx, 4); + + memcpy(ucontrol->value.bytes.data, &coeff[0], params->max); + + return 0; +} + +static int wcd934x_iir_filter_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} + +static int wcd934x_compander_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + int comp = ((struct soc_mixer_control *)kc->private_value)->shift; + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + + ucontrol->value.integer.value[0] = wcd->comp_enabled[comp]; + + return 0; +} + +static int wcd934x_compander_set(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + int comp = ((struct soc_mixer_control *)kc->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + int sel; + + wcd->comp_enabled[comp] = value; + sel = value ? WCD934X_HPH_GAIN_SRC_SEL_COMPANDER : + WCD934X_HPH_GAIN_SRC_SEL_REGISTER; + + /* Any specific register configuration for compander */ + switch (comp) { + case COMPANDER_1: + /* Set Gain Source Select based on compander enable/disable */ + snd_soc_component_update_bits(component, WCD934X_HPH_L_EN, + WCD934X_HPH_GAIN_SRC_SEL_MASK, + sel); + break; + case COMPANDER_2: + snd_soc_component_update_bits(component, WCD934X_HPH_R_EN, + WCD934X_HPH_GAIN_SRC_SEL_MASK, + sel); + break; + case COMPANDER_3: + case COMPANDER_4: + case COMPANDER_7: + case COMPANDER_8: + break; + default: + break; + }; + + return 0; +} + +static int wcd934x_rx_hph_mode_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + + ucontrol->value.enumerated.item[0] = wcd->hph_mode; + + return 0; +} + +static int wcd934x_rx_hph_mode_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + if (mode_val == 0) { + dev_err(wcd->dev, "Invalid HPH Mode, default to ClSH HiFi\n"); + mode_val = CLS_H_LOHIFI; + } + wcd->hph_mode = mode_val; + + return 0; +} + +static int slim_rx_mux_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(dapm->dev); + + ucontrol->value.enumerated.item[0] = wcd->rx_port_value[w->shift]; + + return 0; +} + +static int slim_rx_mux_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(w->dapm->dev); + struct soc_enum *e = (struct soc_enum *)kc->private_value; + struct snd_soc_dapm_update *update = NULL; + u32 port_id = w->shift; + + if (wcd->rx_port_value[port_id] == ucontrol->value.enumerated.item[0]) + return 0; + + wcd->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + + switch (wcd->rx_port_value[port_id]) { + case 0: + list_del_init(&wcd->rx_chs[port_id].list); + break; + case 1: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF1_PB].slim_ch_list); + break; + case 2: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF2_PB].slim_ch_list); + break; + case 3: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF3_PB].slim_ch_list); + break; + case 4: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF4_PB].slim_ch_list); + break; + default: + dev_err(wcd->dev, "Unknown AIF %d\n", + wcd->rx_port_value[port_id]); + goto err; + } + + snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value[port_id], + e, update); + + return 0; +err: + return -EINVAL; +} + +static int wcd934x_int_dem_inp_mux_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kc->private_value; + struct snd_soc_component *component; + int reg, val, ret; + + component = snd_soc_dapm_kcontrol_component(kc); + val = ucontrol->value.enumerated.item[0]; + if (e->reg == WCD934X_CDC_RX0_RX_PATH_SEC0) + reg = WCD934X_CDC_RX0_RX_PATH_CFG0; + else if (e->reg == WCD934X_CDC_RX1_RX_PATH_SEC0) + reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + else if (e->reg == WCD934X_CDC_RX2_RX_PATH_SEC0) + reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + else + return -EINVAL; + + /* Set Look Ahead Delay */ + if (val) + snd_soc_component_update_bits(component, reg, + WCD934X_RX_DLY_ZN_EN_MASK, + WCD934X_RX_DLY_ZN_ENABLE); + else + snd_soc_component_update_bits(component, reg, + WCD934X_RX_DLY_ZN_EN_MASK, + WCD934X_RX_DLY_ZN_DISABLE); + + ret = snd_soc_dapm_put_enum_double(kc, ucontrol); + + return ret; +} + +static int wcd934x_dec_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg = 0; + u8 mic_sel; + + comp = snd_soc_dapm_kcontrol_component(kcontrol); + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + switch (e->reg) { + case WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX0_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX4_TX_PATH_CFG0; + else if (e->shift_l == 4) + mic_sel_reg = WCD934X_CDC_TX8_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX1_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX5_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX2_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX6_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX3_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX7_TX_PATH_CFG0; + break; + default: + dev_err(comp->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + + /* ADC: 0, DMIC: 1 */ + mic_sel = val ? 0x0 : 0x1; + if (mic_sel_reg) + snd_soc_component_update_bits(comp, mic_sel_reg, BIT(7), + mic_sel << 7); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static const struct snd_kcontrol_new rx_int0_2_mux = + SOC_DAPM_ENUM("RX INT0_2 MUX Mux", rx_int0_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int1_2_mux = + SOC_DAPM_ENUM("RX INT1_2 MUX Mux", rx_int1_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int2_2_mux = + SOC_DAPM_ENUM("RX INT2_2 MUX Mux", rx_int2_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int3_2_mux = + SOC_DAPM_ENUM("RX INT3_2 MUX Mux", rx_int3_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int4_2_mux = + SOC_DAPM_ENUM("RX INT4_2 MUX Mux", rx_int4_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int7_2_mux = + SOC_DAPM_ENUM("RX INT7_2 MUX Mux", rx_int7_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int8_2_mux = + SOC_DAPM_ENUM("RX INT8_2 MUX Mux", rx_int8_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP0 Mux", rx_int0_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP1 Mux", rx_int0_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP2 Mux", rx_int0_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP0 Mux", rx_int1_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP1 Mux", rx_int1_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP2 Mux", rx_int1_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP0 Mux", rx_int2_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP1 Mux", rx_int2_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP2 Mux", rx_int2_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP0 Mux", rx_int3_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP1 Mux", rx_int3_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP2 Mux", rx_int3_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP0 Mux", rx_int4_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP1 Mux", rx_int4_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP2 Mux", rx_int4_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP0 Mux", rx_int7_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP1 Mux", rx_int7_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP2 Mux", rx_int7_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP0 Mux", rx_int8_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP1 Mux", rx_int8_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP2 Mux", rx_int8_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int0_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT0 MIX2 INP Mux", rx_int0_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT1 MIX2 INP Mux", rx_int1_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT2 MIX2 INP Mux", rx_int2_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT3 MIX2 INP Mux", rx_int3_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT4 MIX2 INP Mux", rx_int4_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT7 MIX2 INP Mux", rx_int7_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new iir0_inp0_mux = + SOC_DAPM_ENUM("IIR0 INP0 Mux", iir0_inp0_mux_enum); +static const struct snd_kcontrol_new iir0_inp1_mux = + SOC_DAPM_ENUM("IIR0 INP1 Mux", iir0_inp1_mux_enum); +static const struct snd_kcontrol_new iir0_inp2_mux = + SOC_DAPM_ENUM("IIR0 INP2 Mux", iir0_inp2_mux_enum); +static const struct snd_kcontrol_new iir0_inp3_mux = + SOC_DAPM_ENUM("IIR0 INP3 Mux", iir0_inp3_mux_enum); + +static const struct snd_kcontrol_new iir1_inp0_mux = + SOC_DAPM_ENUM("IIR1 INP0 Mux", iir1_inp0_mux_enum); +static const struct snd_kcontrol_new iir1_inp1_mux = + SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); +static const struct snd_kcontrol_new iir1_inp2_mux = + SOC_DAPM_ENUM("IIR1 INP2 Mux", iir1_inp2_mux_enum); +static const struct snd_kcontrol_new iir1_inp3_mux = + SOC_DAPM_ENUM("IIR1 INP3 Mux", iir1_inp3_mux_enum); + +static const struct snd_kcontrol_new slim_rx_mux[WCD934X_RX_MAX] = { + SOC_DAPM_ENUM_EXT("SLIM RX0 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), +}; + +static const struct snd_kcontrol_new rx_int1_asrc_switch[] = { + SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int2_asrc_switch[] = { + SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int3_asrc_switch[] = { + SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int4_asrc_switch[] = { + SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int0_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT0 DEM MUX Mux", rx_int0_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + wcd934x_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int1_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT1 DEM MUX Mux", rx_int1_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + wcd934x_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int2_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT2 DEM MUX Mux", rx_int2_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + wcd934x_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int0_1_interp_mux = + SOC_DAPM_ENUM("RX INT0_1 INTERP Mux", rx_int0_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_1_interp_mux = + SOC_DAPM_ENUM("RX INT1_1 INTERP Mux", rx_int1_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_1_interp_mux = + SOC_DAPM_ENUM("RX INT2_1 INTERP Mux", rx_int2_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_1_interp_mux = + SOC_DAPM_ENUM("RX INT3_1 INTERP Mux", rx_int3_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_1_interp_mux = + SOC_DAPM_ENUM("RX INT4_1 INTERP Mux", rx_int4_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_1_interp_mux = + SOC_DAPM_ENUM("RX INT7_1 INTERP Mux", rx_int7_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int8_1_interp_mux = + SOC_DAPM_ENUM("RX INT8_1 INTERP Mux", rx_int8_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int0_2_interp_mux = + SOC_DAPM_ENUM("RX INT0_2 INTERP Mux", rx_int0_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_2_interp_mux = + SOC_DAPM_ENUM("RX INT1_2 INTERP Mux", rx_int1_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_2_interp_mux = + SOC_DAPM_ENUM("RX INT2_2 INTERP Mux", rx_int2_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_2_interp_mux = + SOC_DAPM_ENUM("RX INT3_2 INTERP Mux", rx_int3_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_2_interp_mux = + SOC_DAPM_ENUM("RX INT4_2 INTERP Mux", rx_int4_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_2_interp_mux = + SOC_DAPM_ENUM("RX INT7_2 INTERP Mux", rx_int7_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int8_2_interp_mux = + SOC_DAPM_ENUM("RX INT8_2 INTERP Mux", rx_int8_2_interp_mux_enum); + +static const struct snd_kcontrol_new tx_dmic_mux0 = + SOC_DAPM_ENUM("DMIC MUX0 Mux", tx_dmic_mux0_enum); + +static const struct snd_kcontrol_new tx_dmic_mux1 = + SOC_DAPM_ENUM("DMIC MUX1 Mux", tx_dmic_mux1_enum); + +static const struct snd_kcontrol_new tx_dmic_mux2 = + SOC_DAPM_ENUM("DMIC MUX2 Mux", tx_dmic_mux2_enum); + +static const struct snd_kcontrol_new tx_dmic_mux3 = + SOC_DAPM_ENUM("DMIC MUX3 Mux", tx_dmic_mux3_enum); + +static const struct snd_kcontrol_new tx_dmic_mux4 = + SOC_DAPM_ENUM("DMIC MUX4 Mux", tx_dmic_mux4_enum); + +static const struct snd_kcontrol_new tx_dmic_mux5 = + SOC_DAPM_ENUM("DMIC MUX5 Mux", tx_dmic_mux5_enum); + +static const struct snd_kcontrol_new tx_dmic_mux6 = + SOC_DAPM_ENUM("DMIC MUX6 Mux", tx_dmic_mux6_enum); + +static const struct snd_kcontrol_new tx_dmic_mux7 = + SOC_DAPM_ENUM("DMIC MUX7 Mux", tx_dmic_mux7_enum); + +static const struct snd_kcontrol_new tx_dmic_mux8 = + SOC_DAPM_ENUM("DMIC MUX8 Mux", tx_dmic_mux8_enum); + +static const struct snd_kcontrol_new tx_amic_mux0 = + SOC_DAPM_ENUM("AMIC MUX0 Mux", tx_amic_mux0_enum); + +static const struct snd_kcontrol_new tx_amic_mux1 = + SOC_DAPM_ENUM("AMIC MUX1 Mux", tx_amic_mux1_enum); + +static const struct snd_kcontrol_new tx_amic_mux2 = + SOC_DAPM_ENUM("AMIC MUX2 Mux", tx_amic_mux2_enum); + +static const struct snd_kcontrol_new tx_amic_mux3 = + SOC_DAPM_ENUM("AMIC MUX3 Mux", tx_amic_mux3_enum); + +static const struct snd_kcontrol_new tx_amic_mux4 = + SOC_DAPM_ENUM("AMIC MUX4 Mux", tx_amic_mux4_enum); + +static const struct snd_kcontrol_new tx_amic_mux5 = + SOC_DAPM_ENUM("AMIC MUX5 Mux", tx_amic_mux5_enum); + +static const struct snd_kcontrol_new tx_amic_mux6 = + SOC_DAPM_ENUM("AMIC MUX6 Mux", tx_amic_mux6_enum); + +static const struct snd_kcontrol_new tx_amic_mux7 = + SOC_DAPM_ENUM("AMIC MUX7 Mux", tx_amic_mux7_enum); + +static const struct snd_kcontrol_new tx_amic_mux8 = + SOC_DAPM_ENUM("AMIC MUX8 Mux", tx_amic_mux8_enum); + +static const struct snd_kcontrol_new tx_amic4_5 = + SOC_DAPM_ENUM("AMIC4_5 SEL Mux", tx_amic4_5_enum); + +static const struct snd_kcontrol_new tx_adc_mux0_mux = + SOC_DAPM_ENUM_EXT("ADC MUX0 Mux", tx_adc_mux0_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux1_mux = + SOC_DAPM_ENUM_EXT("ADC MUX1 Mux", tx_adc_mux1_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux2_mux = + SOC_DAPM_ENUM_EXT("ADC MUX2 Mux", tx_adc_mux2_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux3_mux = + SOC_DAPM_ENUM_EXT("ADC MUX3 Mux", tx_adc_mux3_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux4_mux = + SOC_DAPM_ENUM_EXT("ADC MUX4 Mux", tx_adc_mux4_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux5_mux = + SOC_DAPM_ENUM_EXT("ADC MUX5 Mux", tx_adc_mux5_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux6_mux = + SOC_DAPM_ENUM_EXT("ADC MUX6 Mux", tx_adc_mux6_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux7_mux = + SOC_DAPM_ENUM_EXT("ADC MUX7 Mux", tx_adc_mux7_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux8_mux = + SOC_DAPM_ENUM_EXT("ADC MUX8 Mux", tx_adc_mux8_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); + +static const struct snd_kcontrol_new cdc_if_tx0_mux = + SOC_DAPM_ENUM("CDC_IF TX0 MUX Mux", cdc_if_tx0_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx1_mux = + SOC_DAPM_ENUM("CDC_IF TX1 MUX Mux", cdc_if_tx1_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx2_mux = + SOC_DAPM_ENUM("CDC_IF TX2 MUX Mux", cdc_if_tx2_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx3_mux = + SOC_DAPM_ENUM("CDC_IF TX3 MUX Mux", cdc_if_tx3_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx4_mux = + SOC_DAPM_ENUM("CDC_IF TX4 MUX Mux", cdc_if_tx4_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx5_mux = + SOC_DAPM_ENUM("CDC_IF TX5 MUX Mux", cdc_if_tx5_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx6_mux = + SOC_DAPM_ENUM("CDC_IF TX6 MUX Mux", cdc_if_tx6_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx7_mux = + SOC_DAPM_ENUM("CDC_IF TX7 MUX Mux", cdc_if_tx7_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx8_mux = + SOC_DAPM_ENUM("CDC_IF TX8 MUX Mux", cdc_if_tx8_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx9_mux = + SOC_DAPM_ENUM("CDC_IF TX9 MUX Mux", cdc_if_tx9_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx10_mux = + SOC_DAPM_ENUM("CDC_IF TX10 MUX Mux", cdc_if_tx10_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx11_mux = + SOC_DAPM_ENUM("CDC_IF TX11 MUX Mux", cdc_if_tx11_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx11_inp1_mux = + SOC_DAPM_ENUM("CDC_IF TX11 INP1 MUX Mux", cdc_if_tx11_inp1_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx13_mux = + SOC_DAPM_ENUM("CDC_IF TX13 MUX Mux", cdc_if_tx13_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx13_inp1_mux = + SOC_DAPM_ENUM("CDC_IF TX13 INP1 MUX Mux", cdc_if_tx13_inp1_mux_enum); + +static int slim_tx_mixer_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(dapm->dev); + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kc->private_value; + int port_id = mixer->shift; + + ucontrol->value.integer.value[0] = wcd->tx_port_value[port_id]; + + return 0; +} + +static int slim_tx_mixer_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(widget->dapm->dev); + struct snd_soc_dapm_update *update = NULL; + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kc->private_value; + int enable = ucontrol->value.integer.value[0]; + int dai_id = widget->shift; + int port_id = mixer->shift; + + /* only add to the list if value not set */ + if (enable == wcd->tx_port_value[port_id]) + return 0; + + wcd->tx_port_value[port_id] = enable; + + if (enable) + list_add_tail(&wcd->tx_chs[port_id].list, + &wcd->dai[dai_id].slim_ch_list); + else + list_del_init(&wcd->tx_chs[port_id].list); + + snd_soc_dapm_mixer_update_power(widget->dapm, kc, enable, update); + + return 0; +} + +static const struct snd_kcontrol_new aif1_slim_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_slim_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_slim_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new wcd934x_snd_controls[] = { + /* Gain Controls */ + SOC_SINGLE_TLV("EAR PA Volume", WCD934X_ANA_EAR, 4, 4, 1, ear_pa_gain), + SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 24, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 24, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT1 Volume", WCD934X_DIFF_LO_LO1_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT2 Volume", WCD934X_DIFF_LO_LO2_COMPANDER, + 3, 16, 1, line_gain), + + SOC_SINGLE_TLV("ADC1 Volume", WCD934X_ANA_AMIC1, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD934X_ANA_AMIC2, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD934X_ANA_AMIC3, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD934X_ANA_AMIC4, 0, 20, 0, analog_gain), + + SOC_SINGLE_SX_TLV("RX0 Digital Volume", WCD934X_CDC_RX0_RX_VOL_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX1 Digital Volume", WCD934X_CDC_RX1_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Digital Volume", WCD934X_CDC_RX2_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Digital Volume", WCD934X_CDC_RX3_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX4 Digital Volume", WCD934X_CDC_RX4_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX7 Digital Volume", WCD934X_CDC_RX7_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Digital Volume", WCD934X_CDC_RX8_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX0 Mix Digital Volume", + WCD934X_CDC_RX0_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX1 Mix Digital Volume", + WCD934X_CDC_RX1_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Mix Digital Volume", + WCD934X_CDC_RX2_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Mix Digital Volume", + WCD934X_CDC_RX3_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX4 Mix Digital Volume", + WCD934X_CDC_RX4_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX7 Mix Digital Volume", + WCD934X_CDC_RX7_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Mix Digital Volume", + WCD934X_CDC_RX8_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("DEC0 Volume", WCD934X_CDC_TX0_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC1 Volume", WCD934X_CDC_TX1_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC2 Volume", WCD934X_CDC_TX2_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC3 Volume", WCD934X_CDC_TX3_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC4 Volume", WCD934X_CDC_TX4_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC5 Volume", WCD934X_CDC_TX5_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC6 Volume", WCD934X_CDC_TX6_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC7 Volume", WCD934X_CDC_TX7_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC8 Volume", WCD934X_CDC_TX8_TX_VOL_CTL, 0, + -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("IIR0 INP0 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP1 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP2 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP3 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP0 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84, 40, + digital_gain), + + SOC_ENUM("TX0 HPF cut off", cf_dec0_enum), + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + SOC_ENUM("TX3 HPF cut off", cf_dec3_enum), + SOC_ENUM("TX4 HPF cut off", cf_dec4_enum), + SOC_ENUM("TX5 HPF cut off", cf_dec5_enum), + SOC_ENUM("TX6 HPF cut off", cf_dec6_enum), + SOC_ENUM("TX7 HPF cut off", cf_dec7_enum), + SOC_ENUM("TX8 HPF cut off", cf_dec8_enum), + + SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum), + SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum), + SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum), + SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum), + SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum), + SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum), + SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum), + SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum), + SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum), + SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum), + SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum), + SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum), + SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum), + SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum), + + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + wcd934x_rx_hph_mode_get, wcd934x_rx_hph_mode_put), + + SOC_SINGLE("IIR1 Band1 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 0, 1, 0), + SOC_SINGLE("IIR1 Band2 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 1, 1, 0), + SOC_SINGLE("IIR1 Band3 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 2, 1, 0), + SOC_SINGLE("IIR1 Band4 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 3, 1, 0), + SOC_SINGLE("IIR1 Band5 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 4, 1, 0), + SOC_SINGLE("IIR2 Band1 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 0, 1, 0), + SOC_SINGLE("IIR2 Band2 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 1, 1, 0), + SOC_SINGLE("IIR2 Band3 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 2, 1, 0), + SOC_SINGLE("IIR2 Band4 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 3, 1, 0), + SOC_SINGLE("IIR2 Band5 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 4, 1, 0), + WCD_IIR_FILTER_CTL("IIR0 Band1", IIR0, BAND1), + WCD_IIR_FILTER_CTL("IIR0 Band2", IIR0, BAND2), + WCD_IIR_FILTER_CTL("IIR0 Band3", IIR0, BAND3), + WCD_IIR_FILTER_CTL("IIR0 Band4", IIR0, BAND4), + WCD_IIR_FILTER_CTL("IIR0 Band5", IIR0, BAND5), + + WCD_IIR_FILTER_CTL("IIR1 Band1", IIR1, BAND1), + WCD_IIR_FILTER_CTL("IIR1 Band2", IIR1, BAND2), + WCD_IIR_FILTER_CTL("IIR1 Band3", IIR1, BAND3), + WCD_IIR_FILTER_CTL("IIR1 Band4", IIR1, BAND4), + WCD_IIR_FILTER_CTL("IIR1 Band5", IIR1, BAND5), + + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), +}; + +static void wcd934x_codec_enable_int_port(struct wcd_slim_codec_dai_data *dai, + struct snd_soc_component *component) +{ + int port_num = 0; + unsigned short reg = 0; + unsigned int val = 0; + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + struct wcd934x_slim_ch *ch; + + list_for_each_entry(ch, &dai->slim_ch_list, list) { + if (ch->port >= WCD934X_RX_START) { + port_num = ch->port - WCD934X_RX_START; + reg = WCD934X_SLIM_PGD_PORT_INT_EN0 + (port_num / 8); + } else { + port_num = ch->port; + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + } + + regmap_read(wcd->if_regmap, reg, &val); + if (!(val & BIT(port_num % 8))) + regmap_write(wcd->if_regmap, reg, + val | BIT(port_num % 8)); + } +} + +static int wcd934x_codec_enable_slim(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp); + struct wcd_slim_codec_dai_data *dai = &wcd->dai[w->shift]; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wcd934x_codec_enable_int_port(dai, comp); + break; + case SNDRV_PCM_TRIGGER_STOP: + break; + case SND_SOC_DAPM_POST_PMD: + kfree(dai->sconfig.chs); + + break; + } + + return 0; +} + +static void wcd934x_codec_hd2_control(struct snd_soc_component *component, + u16 interp_idx, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + switch (interp_idx) { + case INTERP_HPHL: + hd2_scale_reg = WCD934X_CDC_RX1_RX_PATH_SEC3; + hd2_enable_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + break; + case INTERP_HPHR: + hd2_scale_reg = WCD934X_CDC_RX2_RX_PATH_SEC3; + hd2_enable_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + break; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_MASK, + WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_0P3125); + snd_soc_component_update_bits(component, hd2_enable_reg, + WCD934X_CDC_RX_PATH_CFG_HD2_EN_MASK, + WCD934X_CDC_RX_PATH_CFG_HD2_ENABLE); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, hd2_enable_reg, + WCD934X_CDC_RX_PATH_CFG_HD2_EN_MASK, + WCD934X_CDC_RX_PATH_CFG_HD2_DISABLE); + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_MASK, + WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_0P0000); + } +} + +static void wcd934x_codec_hphdelay_lutbypass(struct snd_soc_component *comp, + u16 interp_idx, int event) +{ + u8 hph_dly_mask; + u16 hph_lut_bypass_reg = 0; + u16 hph_comp_ctrl7 = 0; + + switch (interp_idx) { + case INTERP_HPHL: + hph_dly_mask = 1; + hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHL_COMP_LUT; + hph_comp_ctrl7 = WCD934X_CDC_COMPANDER1_CTL7; + break; + case INTERP_HPHR: + hph_dly_mask = 2; + hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHR_COMP_LUT; + hph_comp_ctrl7 = WCD934X_CDC_COMPANDER2_CTL7; + break; + default: + break; + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(comp, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, 0x0); + snd_soc_component_update_bits(comp, hph_lut_bypass_reg, + WCD934X_HPH_LUT_BYPASS_MASK, + WCD934X_HPH_LUT_BYPASS_ENABLE); + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(comp, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, hph_dly_mask); + snd_soc_component_update_bits(comp, hph_lut_bypass_reg, + WCD934X_HPH_LUT_BYPASS_MASK, + WCD934X_HPH_LUT_BYPASS_DISABLE); + } +} + +static int wcd934x_config_compander(struct snd_soc_component *comp, + int interp_n, int event) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int compander; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* EAR does not have compander */ + if (!interp_n) + return 0; + + compander = interp_n - 1; + if (!wcd->comp_enabled[compander]) + return 0; + + comp_ctl0_reg = WCD934X_CDC_COMPANDER1_CTL0 + (compander * 8); + rx_path_cfg0_reg = WCD934X_CDC_RX1_RX_PATH_CFG0 + (compander * 20); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable Compander Clock */ + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_CLK_EN_MASK, + WCD934X_COMP_CLK_ENABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, + WCD934X_COMP_SOFT_RST_ENABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, + WCD934X_COMP_SOFT_RST_DISABLE); + snd_soc_component_update_bits(comp, rx_path_cfg0_reg, + WCD934X_HPH_CMP_EN_MASK, + WCD934X_HPH_CMP_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(comp, rx_path_cfg0_reg, + WCD934X_HPH_CMP_EN_MASK, + WCD934X_HPH_CMP_DISABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_HALT_MASK, + WCD934X_COMP_HALT); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, + WCD934X_COMP_SOFT_RST_ENABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, + WCD934X_COMP_SOFT_RST_DISABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_CLK_EN_MASK, 0x0); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, 0x0); + break; + } + + return 0; +} + +static int wcd934x_codec_enable_interp_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int interp_idx = w->shift; + u16 main_reg = WCD934X_CDC_RX0_RX_PATH_CTL + (interp_idx * 20); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Clk enable */ + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_CLK_EN_MASK, + WCD934X_RX_CLK_ENABLE); + wcd934x_codec_hd2_control(comp, interp_idx, event); + wcd934x_codec_hphdelay_lutbypass(comp, interp_idx, event); + wcd934x_config_compander(comp, interp_idx, event); + break; + case SND_SOC_DAPM_POST_PMD: + wcd934x_config_compander(comp, interp_idx, event); + wcd934x_codec_hphdelay_lutbypass(comp, interp_idx, event); + wcd934x_codec_hd2_control(comp, interp_idx, event); + /* Clk Disable */ + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_CLK_EN_MASK, 0); + /* Reset enable and disable */ + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_RESET_MASK, + WCD934X_RX_RESET_ENABLE); + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_RESET_MASK, + WCD934X_RX_RESET_DISABLE); + /* Reset rate to 48K*/ + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_PCM_RATE_MASK, + WCD934X_RX_PCM_RATE_F_48K); + break; + } + + return 0; +} + +static int wcd934x_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int offset_val = 0; + u16 gain_reg, mix_reg; + int val = 0; + + gain_reg = WCD934X_CDC_RX0_RX_VOL_MIX_CTL + + (w->shift * WCD934X_RX_PATH_CTL_OFFSET); + mix_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL + + (w->shift * WCD934X_RX_PATH_CTL_OFFSET); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Clk enable */ + snd_soc_component_update_bits(comp, mix_reg, + WCD934X_CDC_RX_MIX_CLK_EN_MASK, + WCD934X_CDC_RX_MIX_CLK_ENABLE); + break; + + case SND_SOC_DAPM_POST_PMU: + val = snd_soc_component_read32(comp, gain_reg); + val += offset_val; + snd_soc_component_write(comp, gain_reg, val); + break; + case SND_SOC_DAPM_POST_PMD: + break; + }; + + return 0; +} + +static int wcd934x_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int reg = w->reg; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* B1 GAIN */ + snd_soc_component_write(comp, reg, + snd_soc_component_read32(comp, reg)); + /* B2 GAIN */ + reg++; + snd_soc_component_write(comp, reg, + snd_soc_component_read32(comp, reg)); + /* B3 GAIN */ + reg++; + snd_soc_component_write(comp, reg, + snd_soc_component_read32(comp, reg)); + /* B4 GAIN */ + reg++; + snd_soc_component_write(comp, reg, + snd_soc_component_read32(comp, reg)); + /* B5 GAIN */ + reg++; + snd_soc_component_write(comp, reg, + snd_soc_component_read32(comp, reg)); + break; + default: + break; + } + return 0; +} + +static int wcd934x_codec_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + u16 gain_reg; + u32 val; + + gain_reg = WCD934X_CDC_RX0_RX_VOL_CTL + (w->shift * + WCD934X_RX_PATH_CTL_OFFSET); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = snd_soc_component_read32(comp, gain_reg); + snd_soc_component_write(comp, gain_reg, val); + break; + }; + + return 0; +} + +static int wcd934x_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disable AutoChop timer during power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, 0x0); + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, CLS_H_NORMAL); + + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, CLS_H_NORMAL); + break; + }; + + return 0; +} + +static int wcd934x_codec_hphl_dac_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 wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + u8 dem_inp; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Read DEM INP Select */ + dem_inp = snd_soc_component_read32(comp, + WCD934X_CDC_RX1_RX_PATH_SEC0) & 0x03; + + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + return -EINVAL; + } + if (hph_mode != CLS_H_LP) + /* Ripple freq control enable */ + snd_soc_component_update_bits(comp, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, + WCD934X_SIDO_RIPPLE_FREQ_ENABLE); + /* Disable AutoChop timer during power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, 0x0); + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, hph_mode); + + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, hph_mode); + if (hph_mode != CLS_H_LP) + /* Ripple freq control disable */ + snd_soc_component_update_bits(comp, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, 0x0); + + break; + default: + break; + }; + + return 0; +} + +static int wcd934x_codec_hphr_dac_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 wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + u8 dem_inp; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dem_inp = snd_soc_component_read32(comp, + WCD934X_CDC_RX2_RX_PATH_SEC0) & 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + return -EINVAL; + } + if (hph_mode != CLS_H_LP) + /* Ripple freq control enable */ + snd_soc_component_update_bits(comp, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, + WCD934X_SIDO_RIPPLE_FREQ_ENABLE); + /* Disable AutoChop timer during power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, 0x0); + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + hph_mode); + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, hph_mode); + if (hph_mode != CLS_H_LP) + /* Ripple freq control disable */ + snd_soc_component_update_bits(comp, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, 0x0); + break; + default: + break; + }; + + return 0; +} + +static int wcd934x_codec_lineout_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_LO, CLS_AB); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_LO, CLS_AB); + break; + } + + return 0; +} + +static int wcd934x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is needed. + */ + usleep_range(20000, 20100); + + snd_soc_component_update_bits(comp, WCD934X_HPH_L_TEST, + WCD934X_HPH_OCP_DET_MASK, + WCD934X_HPH_OCP_DET_ENABLE); + /* Remove Mute on primary path */ + snd_soc_component_update_bits(comp, WCD934X_CDC_RX1_RX_PATH_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + 0); + /* Enable GM3 boost */ + snd_soc_component_update_bits(comp, WCD934X_HPH_CNP_WG_CTL, + WCD934X_HPH_GM3_BOOST_EN_MASK, + WCD934X_HPH_GM3_BOOST_ENABLE); + /* Enable AutoChop timer at the end of power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, + WCD934X_HPH_AUTOCHOP_TIMER_ENABLE); + /* Remove mix path mute */ + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + WCD934X_CDC_RX_PGA_MUTE_EN_MASK, 0x00); + break; + case SND_SOC_DAPM_PRE_PMD: + /* Enable DSD Mute before PA disable */ + + snd_soc_component_update_bits(comp, WCD934X_HPH_L_TEST, + WCD934X_HPH_OCP_DET_MASK, + WCD934X_HPH_OCP_DET_DISABLE); + snd_soc_component_update_bits(comp, WCD934X_CDC_RX1_RX_PATH_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + WCD934X_RX_PATH_PGA_MUTE_ENABLE); + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + WCD934X_RX_PATH_PGA_MUTE_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA disable. If compander is + * disabled, then 20ms delay is needed after PA disable. + */ + usleep_range(20000, 20100); + break; + }; + + return 0; +} + +static int wcd934x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is needed. + */ + usleep_range(20000, 20100); + snd_soc_component_update_bits(comp, WCD934X_HPH_R_TEST, + WCD934X_HPH_OCP_DET_MASK, + WCD934X_HPH_OCP_DET_ENABLE); + /* Remove mute */ + snd_soc_component_update_bits(comp, WCD934X_CDC_RX2_RX_PATH_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + 0); + /* Enable GM3 boost */ + snd_soc_component_update_bits(comp, WCD934X_HPH_CNP_WG_CTL, + WCD934X_HPH_GM3_BOOST_EN_MASK, + WCD934X_HPH_GM3_BOOST_ENABLE); + /* Enable AutoChop timer at the end of power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, + WCD934X_HPH_AUTOCHOP_TIMER_ENABLE); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_component_read32(comp, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL)) & 0x10) + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + WCD934X_CDC_RX_PGA_MUTE_EN_MASK, + WCD934X_CDC_RX_PGA_MUTE_DISABLE); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(comp, WCD934X_HPH_R_TEST, + WCD934X_HPH_OCP_DET_MASK, + WCD934X_HPH_OCP_DET_DISABLE); + snd_soc_component_update_bits(comp, WCD934X_CDC_RX2_RX_PATH_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + WCD934X_RX_PATH_PGA_MUTE_ENABLE); + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + WCD934X_CDC_RX_PGA_MUTE_EN_MASK, + WCD934X_CDC_RX_PGA_MUTE_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA disable. If compander is + * disabled, then 20ms delay is needed after PA disable. + */ + usleep_range(20000, 20100); + break; + }; + + return 0; +} + +static u32 wcd934x_get_dmic_sample_rate(struct snd_soc_component *comp, + unsigned int dmic, + struct wcd934x_codec *wcd) +{ + u8 tx_stream_fs; + u8 adc_mux_index = 0, adc_mux_sel = 0; + bool dec_found = false; + u16 adc_mux_ctl_reg, tx_fs_reg; + u32 dmic_fs; + + while (dec_found == 0 && adc_mux_index < WCD934X_MAX_VALID_ADC_MUX) { + if (adc_mux_index < 4) { + adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + (adc_mux_index * 2); + } else if (adc_mux_index < WCD934X_INVALID_ADC_MUX) { + adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_index - 4; + } else if (adc_mux_index == WCD934X_INVALID_ADC_MUX) { + ++adc_mux_index; + continue; + } + adc_mux_sel = ((snd_soc_component_read32(comp, adc_mux_ctl_reg) + & 0xF8) >> 3) - 1; + + if (adc_mux_sel == dmic) { + dec_found = true; + break; + } + + ++adc_mux_index; + } + + if (dec_found && adc_mux_index <= 8) { + tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index); + tx_stream_fs = snd_soc_component_read32(comp, tx_fs_reg) & 0x0F; + if (tx_stream_fs <= 4) { + if (wcd->dmic_sample_rate <= + WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ) + dmic_fs = wcd->dmic_sample_rate; + else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ; + } else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + } else { + dmic_fs = wcd->dmic_sample_rate; + } + + return dmic_fs; +} + +static u8 wcd934x_get_dmic_clk_val(struct snd_soc_component *comp, + u32 mclk_rate, u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val; + + /* Default value to return in case of error */ + if (mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2; + else + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3; + + if (dmic_clk_rate == 0) { + dev_err(comp->dev, + "%s: dmic_sample_rate cannot be 0\n", + __func__); + goto done; + } + + div_factor = mclk_rate / dmic_clk_rate; + switch (div_factor) { + case 2: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2; + break; + case 3: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3; + break; + case 4: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_4; + break; + case 6: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_6; + break; + case 8: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_8; + break; + case 16: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_16; + break; + default: + dev_err(comp->dev, + "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n", + __func__, div_factor, mclk_rate, dmic_clk_rate); + break; + } + +done: + return dmic_ctl_val; +} + +static int wcd934x_codec_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); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 dmic_rate_val, dmic_rate_shift = 1; + unsigned int dmic; + u32 dmic_sample_rate; + int ret; + char *wname; + + wname = strpbrk(w->name, "012345"); + if (!wname) { + dev_err(comp->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(comp->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &wcd->dmic_0_1_clk_cnt; + dmic_clk_reg = WCD934X_CPE_SS_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &wcd->dmic_2_3_clk_cnt; + dmic_clk_reg = WCD934X_CPE_SS_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &wcd->dmic_4_5_clk_cnt; + dmic_clk_reg = WCD934X_CPE_SS_DMIC2_CTL; + break; + default: + dev_err(comp->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + }; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dmic_sample_rate = wcd934x_get_dmic_sample_rate(comp, dmic, + wcd); + dmic_rate_val = wcd934x_get_dmic_clk_val(comp, wcd->rate, + dmic_sample_rate); + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + dmic_rate_val = dmic_rate_val << dmic_rate_shift; + snd_soc_component_update_bits(comp, dmic_clk_reg, + WCD934X_DMIC_RATE_MASK, + dmic_rate_val); + snd_soc_component_update_bits(comp, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) + snd_soc_component_update_bits(comp, dmic_clk_reg, + dmic_clk_en, 0); + break; + }; + + return 0; +} + +static int wcd934x_codec_find_amic_input(struct snd_soc_component *comp, + int adc_mux_n) +{ + u16 mask, shift, adc_mux_in_reg; + u16 amic_mux_sel_reg; + bool is_amic; + + if (adc_mux_n < 0 || adc_mux_n > WCD934X_MAX_VALID_ADC_MUX || + adc_mux_n == WCD934X_INVALID_ADC_MUX) + return 0; + + if (adc_mux_n < 3) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + adc_mux_n; + mask = 0x03; + shift = 0; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } else if (adc_mux_n < 4) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x03; + shift = 0; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } else if (adc_mux_n < 7) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + (adc_mux_n - 4); + mask = 0x0C; + shift = 2; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 8) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x0C; + shift = 2; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 12) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + ((adc_mux_n == 8) ? (adc_mux_n - 8) : + (adc_mux_n - 9)); + mask = 0x30; + shift = 4; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 13) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x30; + shift = 4; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1; + mask = 0xC0; + shift = 6; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } + + is_amic = (((snd_soc_component_read32(comp, adc_mux_in_reg) + & mask) >> shift) == 1); + if (!is_amic) + return 0; + + return snd_soc_component_read32(comp, amic_mux_sel_reg) & 0x07; +} + +static u16 wcd934x_codec_get_amic_pwlvl_reg(struct snd_soc_component *comp, + int amic) +{ + u16 pwr_level_reg = 0; + + switch (amic) { + case 1: + case 2: + pwr_level_reg = WCD934X_ANA_AMIC1; + break; + + case 3: + case 4: + pwr_level_reg = WCD934X_ANA_AMIC3; + break; + default: + break; + } + + return pwr_level_reg; +} + +static int wcd934x_codec_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; + char *dec_adc_mux_name = NULL; + char *widget_name = NULL; + char *wname; + int ret = 0, amic_n; + u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + char *dec; + u8 hpf_coff_freq; + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + wname = widget_name; + dec_adc_mux_name = strsep(&widget_name, " "); + if (!dec_adc_mux_name) { + dev_err(comp->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + dec_adc_mux_name = widget_name; + + dec = strpbrk(dec_adc_mux_name, "012345678"); + if (!dec) { + dev_err(comp->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(comp->dev, "%s: Invalid decimator = %s\n", + __func__, wname); + ret = -EINVAL; + goto out; + } + + tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + 16 * decimator; + hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; + dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = WCD934X_CDC_TX0_TX_VOL_CTL + 16 * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + amic_n = wcd934x_codec_find_amic_input(comp, decimator); + if (amic_n) + pwr_level_reg = wcd934x_codec_get_amic_pwlvl_reg(comp, + amic_n); + + if (pwr_level_reg) { + switch ((snd_soc_component_read32(comp, pwr_level_reg) & + WCD934X_AMIC_PWR_LVL_MASK) >> + WCD934X_AMIC_PWR_LVL_SHIFT) { + case WCD934X_AMIC_PWR_LEVEL_LP: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_LP); + break; + + case WCD934X_AMIC_PWR_LEVEL_HP: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_HP); + break; + case WCD934X_AMIC_PWR_LEVEL_DEFAULT: + case WCD934X_AMIC_PWR_LEVEL_HYBRID: + default: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); + break; + } + } + break; + case SND_SOC_DAPM_POST_PMU: + hpf_coff_freq = (snd_soc_component_read32(comp, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + if (hpf_coff_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, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK, + WCD934X_HPH_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, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK, + 0); + } + /* apply gain after decimator is enabled */ + snd_soc_component_write(comp, tx_gain_ctl_reg, + snd_soc_component_read32(comp, + tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_coff_freq = (snd_soc_component_read32(comp, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + + if (hpf_coff_freq != CF_MIN_3DB_150HZ) { + snd_soc_component_update_bits(comp, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_coff_freq << 5); + snd_soc_component_update_bits(comp, hpf_gate_reg, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK, + WCD934X_HPH_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, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK, + 0); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x00); + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); + break; + }; +out: + kfree(wname); + return ret; +} + +static void wcd934x_codec_set_tx_hold(struct snd_soc_component *comp, + u16 amic_reg, bool set) +{ + u8 mask = 0x20; + u8 val; + + if (amic_reg == WCD934X_ANA_AMIC1 || + amic_reg == WCD934X_ANA_AMIC3) + mask = 0x40; + + val = set ? mask : 0x00; + + switch (amic_reg) { + case WCD934X_ANA_AMIC1: + case WCD934X_ANA_AMIC2: + snd_soc_component_update_bits(comp, WCD934X_ANA_AMIC2, + mask, val); + break; + case WCD934X_ANA_AMIC3: + case WCD934X_ANA_AMIC4: + snd_soc_component_update_bits(comp, WCD934X_ANA_AMIC4, + mask, val); + break; + default: + break; + } +} + +static int wcd934x_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd934x_codec_set_tx_hold(comp, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static int wcd934x_micbias_control(struct snd_soc_component *comp, + int micb_num, int req, bool is_dapm) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int micb_index = micb_num - 1; + u16 micb_reg; + + if ((micb_index < 0) || (micb_index > WCD934X_MAX_MICBIAS - 1)) { + dev_err(comp->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD934X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD934X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD934X_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD934X_ANA_MICB4; + break; + default: + dev_err(comp->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + + switch (req) { + case MICB_PULLUP_ENABLE: + wcd->pullup_ref[micb_index]++; + if ((wcd->pullup_ref[micb_index] == 1) && + (wcd->micb_ref[micb_index] == 0)) { + snd_soc_component_update_bits(comp, micb_reg, + WCD934X_ANA_MICB_EN_MASK, + WCD934X_ANA_MICB_PULL_UP); + } + break; + case MICB_PULLUP_DISABLE: + if (wcd->pullup_ref[micb_index] > 0) + wcd->pullup_ref[micb_index]--; + if ((wcd->pullup_ref[micb_index] == 0) && + (wcd->micb_ref[micb_index] == 0)) + snd_soc_component_update_bits(comp, micb_reg, + WCD934X_ANA_MICB_EN_MASK, + WCD934X_ANA_MICB_DISABLE); + break; + case MICB_ENABLE: + wcd->micb_ref[micb_index]++; + if (wcd->micb_ref[micb_index] == 1) + snd_soc_component_update_bits(comp, micb_reg, + WCD934X_ANA_MICB_EN_MASK, + WCD934X_ANA_MICB_ENABLE); + break; + case MICB_DISABLE: + if (wcd->micb_ref[micb_index] > 0) + wcd->micb_ref[micb_index]--; + if ((wcd->micb_ref[micb_index] == 0) && + (wcd->pullup_ref[micb_index] > 0)) + snd_soc_component_update_bits(comp, micb_reg, + WCD934X_ANA_MICB_EN_MASK, + WCD934X_ANA_MICB_PULL_UP); + else if ((wcd->micb_ref[micb_index] == 0) && + (wcd->pullup_ref[micb_index] == 0)) { + snd_soc_component_update_bits(comp, micb_reg, + WCD934X_ANA_MICB_EN_MASK, + WCD934X_ANA_MICB_DISABLE); + } + break; + }; + + return 0; +} + +static int wcd934x_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int micb_num; + + if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) + micb_num = MIC_BIAS_1; + else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) + micb_num = MIC_BIAS_2; + else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) + micb_num = MIC_BIAS_3; + else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4"))) + micb_num = MIC_BIAS_4; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* + * MIC BIAS can also be requested by MBHC, + * so use ref count to handle micbias pullup + * and enable requests + */ + wcd934x_micbias_control(comp, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* wait for cnp time */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd934x_micbias_control(comp, micb_num, MICB_DISABLE, true); + break; + }; + + return 0; +} + +static const struct snd_soc_dapm_widget wcd934x_dapm_widgets[] = { + /* Analog Outputs */ + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + SND_SOC_DAPM_OUTPUT("SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("SPK2 OUT"), + SND_SOC_DAPM_OUTPUT("ANC EAR"), + SND_SOC_DAPM_OUTPUT("ANC HPHL"), + SND_SOC_DAPM_OUTPUT("ANC HPHR"), + SND_SOC_DAPM_OUTPUT("WDMA3_OUT"), + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT1"), + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT2"), + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, + AIF4_PB, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, WCD934X_RX0, 0, + &slim_rx_mux[WCD934X_RX0]), + SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, WCD934X_RX1, 0, + &slim_rx_mux[WCD934X_RX1]), + SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, WCD934X_RX2, 0, + &slim_rx_mux[WCD934X_RX2]), + SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, WCD934X_RX3, 0, + &slim_rx_mux[WCD934X_RX3]), + SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, WCD934X_RX4, 0, + &slim_rx_mux[WCD934X_RX4]), + SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, WCD934X_RX5, 0, + &slim_rx_mux[WCD934X_RX5]), + SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, WCD934X_RX6, 0, + &slim_rx_mux[WCD934X_RX6]), + SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, WCD934X_RX7, 0, + &slim_rx_mux[WCD934X_RX7]), + + SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", SND_SOC_NOPM, INTERP_LO1, 0, + &rx_int3_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", SND_SOC_NOPM, INTERP_LO2, 0, + &rx_int4_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp2_mux), + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int1_asrc_switch, + ARRAY_SIZE(rx_int1_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int2_asrc_switch, + ARRAY_SIZE(rx_int2_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int3_asrc_switch, + ARRAY_SIZE(rx_int3_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int4_asrc_switch, + ARRAY_SIZE(rx_int4_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, NULL, 0), + SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", WCD934X_CDC_RX0_RX_PATH_CFG0, 4, + 0, &rx_int0_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", WCD934X_CDC_RX1_RX_PATH_CFG0, 4, + 0, &rx_int1_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", WCD934X_CDC_RX2_RX_PATH_CFG0, 4, + 0, &rx_int2_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3 MIX2 INP", WCD934X_CDC_RX3_RX_PATH_CFG0, 4, + 0, &rx_int3_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4 MIX2 INP", WCD934X_CDC_RX4_RX_PATH_CFG0, 4, + 0, &rx_int4_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7 MIX2 INP", WCD934X_CDC_RX7_RX_PATH_CFG0, 4, + 0, &rx_int7_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("IIR0 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp0_mux), + SND_SOC_DAPM_MUX("IIR0 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp1_mux), + SND_SOC_DAPM_MUX("IIR0 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp2_mux), + SND_SOC_DAPM_MUX("IIR0 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp3_mux), + SND_SOC_DAPM_MUX("IIR1 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp0_mux), + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), + SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux), + SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux), + + SND_SOC_DAPM_PGA_E("IIR0", WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + 0, 0, NULL, 0, wcd934x_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("IIR1", WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, + 1, 0, NULL, 0, wcd934x_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MIXER("SRC0", WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SRC1", WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int0_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int1_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT2 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int2_dem_inp_mux), + + SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3_1 INTERP", SND_SOC_NOPM, INTERP_LO1, 0, + &rx_int3_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4_1 INTERP", SND_SOC_NOPM, INTERP_LO2, 0, + &rx_int4_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 INTERP", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 INTERP", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT0_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int0_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT1_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int1_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT2_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int2_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT3_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int3_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT4_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int4_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT7_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int7_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT8_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int8_2_interp_mux), + SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd934x_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, WCD934X_ANA_HPH, + 5, 0, wcd934x_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, WCD934X_ANA_HPH, + 4, 0, wcd934x_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd934x_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd934x_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("HPHL PA", WCD934X_ANA_HPH, 7, 0, NULL, 0, + wcd934x_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PA", WCD934X_ANA_HPH, 6, 0, NULL, 0, + wcd934x_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD934X_ANA_LO_1_2, 7, 0, NULL, 0, + NULL, 0), + SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD934X_ANA_LO_1_2, 6, 0, NULL, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("RX_BIAS", WCD934X_ANA_RX_SUPPLIES, 0, 0, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("SBOOST0", WCD934X_CDC_RX7_RX_PATH_CFG1, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SBOOST0_CLK", WCD934X_CDC_BOOST0_BOOST_PATH_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SBOOST1", WCD934X_CDC_RX8_RX_PATH_CFG1, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SBOOST1_CLK", WCD934X_CDC_BOOST1_BOOST_PATH_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("INT0_CLK", SND_SOC_NOPM, INTERP_EAR, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT1_CLK", SND_SOC_NOPM, INTERP_HPHL, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT2_CLK", SND_SOC_NOPM, INTERP_HPHR, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT3_CLK", SND_SOC_NOPM, INTERP_LO1, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT4_CLK", SND_SOC_NOPM, INTERP_LO2, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT7_CLK", SND_SOC_NOPM, INTERP_SPKR1, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT8_CLK", SND_SOC_NOPM, INTERP_SPKR2, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DSMDEM0_CLK", WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM1_CLK", WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM2_CLK", WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM3_CLK", WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM4_CLK", WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM7_CLK", WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM8_CLK", WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_mclk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* TX */ + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + 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_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM, + AIF2_CAP, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM, + AIF3_CAP, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("SLIM TX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX8", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX9", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX10", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX11", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX13", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("DMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_dmic_mux0), + SND_SOC_DAPM_MUX("DMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_dmic_mux1), + SND_SOC_DAPM_MUX("DMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_dmic_mux2), + SND_SOC_DAPM_MUX("DMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_dmic_mux3), + SND_SOC_DAPM_MUX("DMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_dmic_mux4), + SND_SOC_DAPM_MUX("DMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_dmic_mux5), + SND_SOC_DAPM_MUX("DMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_dmic_mux6), + SND_SOC_DAPM_MUX("DMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_dmic_mux7), + SND_SOC_DAPM_MUX("DMIC MUX8", SND_SOC_NOPM, 0, 0, &tx_dmic_mux8), + SND_SOC_DAPM_MUX("AMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_amic_mux0), + SND_SOC_DAPM_MUX("AMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_amic_mux1), + SND_SOC_DAPM_MUX("AMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_amic_mux2), + SND_SOC_DAPM_MUX("AMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_amic_mux3), + SND_SOC_DAPM_MUX("AMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_amic_mux4), + SND_SOC_DAPM_MUX("AMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_amic_mux5), + SND_SOC_DAPM_MUX("AMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_amic_mux6), + SND_SOC_DAPM_MUX("AMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_amic_mux7), + SND_SOC_DAPM_MUX("AMIC MUX8", SND_SOC_NOPM, 0, 0, &tx_amic_mux8), + SND_SOC_DAPM_MUX_E("ADC MUX0", WCD934X_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc_mux0_mux, wcd934x_codec_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("ADC MUX1", WCD934X_CDC_TX1_TX_PATH_CTL, 5, 0, + &tx_adc_mux1_mux, wcd934x_codec_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("ADC MUX2", WCD934X_CDC_TX2_TX_PATH_CTL, 5, 0, + &tx_adc_mux2_mux, wcd934x_codec_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("ADC MUX3", WCD934X_CDC_TX3_TX_PATH_CTL, 5, 0, + &tx_adc_mux3_mux, wcd934x_codec_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("ADC MUX4", WCD934X_CDC_TX4_TX_PATH_CTL, 5, 0, + &tx_adc_mux4_mux, wcd934x_codec_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("ADC MUX5", WCD934X_CDC_TX5_TX_PATH_CTL, 5, 0, + &tx_adc_mux5_mux, wcd934x_codec_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("ADC MUX6", WCD934X_CDC_TX6_TX_PATH_CTL, 5, 0, + &tx_adc_mux6_mux, wcd934x_codec_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("ADC MUX7", WCD934X_CDC_TX7_TX_PATH_CTL, 5, 0, + &tx_adc_mux7_mux, wcd934x_codec_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("ADC MUX8", WCD934X_CDC_TX8_TX_PATH_CTL, 5, 0, + &tx_adc_mux8_mux, wcd934x_codec_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_ADC_E("ADC1", NULL, WCD934X_ANA_AMIC1, 7, 0, + wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD934X_ANA_AMIC2, 7, 0, + wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD934X_ANA_AMIC3, 7, 0, + wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD934X_ANA_AMIC4, 7, 0, + wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("AMIC4_5 SEL", SND_SOC_NOPM, 0, 0, &tx_amic4_5), + SND_SOC_DAPM_MUX("CDC_IF TX0 MUX", SND_SOC_NOPM, WCD934X_TX0, 0, + &cdc_if_tx0_mux), + SND_SOC_DAPM_MUX("CDC_IF TX1 MUX", SND_SOC_NOPM, WCD934X_TX1, 0, + &cdc_if_tx1_mux), + SND_SOC_DAPM_MUX("CDC_IF TX2 MUX", SND_SOC_NOPM, WCD934X_TX2, 0, + &cdc_if_tx2_mux), + SND_SOC_DAPM_MUX("CDC_IF TX3 MUX", SND_SOC_NOPM, WCD934X_TX3, 0, + &cdc_if_tx3_mux), + SND_SOC_DAPM_MUX("CDC_IF TX4 MUX", SND_SOC_NOPM, WCD934X_TX4, 0, + &cdc_if_tx4_mux), + SND_SOC_DAPM_MUX("CDC_IF TX5 MUX", SND_SOC_NOPM, WCD934X_TX5, 0, + &cdc_if_tx5_mux), + SND_SOC_DAPM_MUX("CDC_IF TX6 MUX", SND_SOC_NOPM, WCD934X_TX6, 0, + &cdc_if_tx6_mux), + SND_SOC_DAPM_MUX("CDC_IF TX7 MUX", SND_SOC_NOPM, WCD934X_TX7, 0, + &cdc_if_tx7_mux), + SND_SOC_DAPM_MUX("CDC_IF TX8 MUX", SND_SOC_NOPM, WCD934X_TX8, 0, + &cdc_if_tx8_mux), + SND_SOC_DAPM_MUX("CDC_IF TX9 MUX", SND_SOC_NOPM, WCD934X_TX9, 0, + &cdc_if_tx9_mux), + SND_SOC_DAPM_MUX("CDC_IF TX10 MUX", SND_SOC_NOPM, WCD934X_TX10, 0, + &cdc_if_tx10_mux), + SND_SOC_DAPM_MUX("CDC_IF TX11 MUX", SND_SOC_NOPM, WCD934X_TX11, 0, + &cdc_if_tx11_mux), + SND_SOC_DAPM_MUX("CDC_IF TX11 INP1 MUX", SND_SOC_NOPM, WCD934X_TX11, 0, + &cdc_if_tx11_inp1_mux), + SND_SOC_DAPM_MUX("CDC_IF TX13 MUX", SND_SOC_NOPM, WCD934X_TX13, 0, + &cdc_if_tx13_mux), + SND_SOC_DAPM_MUX("CDC_IF TX13 INP1 MUX", SND_SOC_NOPM, WCD934X_TX13, 0, + &cdc_if_tx13_inp1_mux), + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_slim_cap_mixer, + ARRAY_SIZE(aif1_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_slim_cap_mixer, + ARRAY_SIZE(aif2_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_slim_cap_mixer, + ARRAY_SIZE(aif3_slim_cap_mixer)), +}; + +static const struct snd_soc_dapm_route wcd934x_audio_map[] = { + /* RX0-RX7 */ + WCD934X_SLIM_RX_AIF_PATH(0), + WCD934X_SLIM_RX_AIF_PATH(1), + WCD934X_SLIM_RX_AIF_PATH(2), + WCD934X_SLIM_RX_AIF_PATH(3), + WCD934X_SLIM_RX_AIF_PATH(4), + WCD934X_SLIM_RX_AIF_PATH(5), + WCD934X_SLIM_RX_AIF_PATH(6), + WCD934X_SLIM_RX_AIF_PATH(7), + + /* RX0 Ear out */ + WCD934X_INTERPOLATOR_PATH(0), + WCD934X_INTERPOLATOR_MIX2(0), + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"}, + {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"}, + {"RX INT0 DAC", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "RX INT0 DAC"}, + {"EAR", NULL, "EAR PA"}, + + /* RX1 Headphone left */ + WCD934X_INTERPOLATOR_PATH(1), + WCD934X_INTERPOLATOR_MIX2(1), + {"RX INT1 MIX3", NULL, "RX INT1 MIX2"}, + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX3"}, + {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"}, + {"RX INT1 DAC", NULL, "RX_BIAS"}, + {"HPHL PA", NULL, "RX INT1 DAC"}, + {"HPHL", NULL, "HPHL PA"}, + + /* RX2 Headphone right */ + WCD934X_INTERPOLATOR_PATH(2), + WCD934X_INTERPOLATOR_MIX2(2), + {"RX INT2 MIX3", NULL, "RX INT2 MIX2"}, + {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 MIX3"}, + {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"}, + {"RX INT2 DAC", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "RX INT2 DAC"}, + {"HPHR", NULL, "HPHR PA"}, + + /* RX3 HIFi LineOut1 */ + WCD934X_INTERPOLATOR_PATH(3), + WCD934X_INTERPOLATOR_MIX2(3), + {"RX INT3 MIX3", NULL, "RX INT3 MIX2"}, + {"RX INT3 DAC", NULL, "RX INT3 MIX3"}, + {"RX INT3 DAC", NULL, "RX_BIAS"}, + {"LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"LINEOUT1", NULL, "LINEOUT1 PA"}, + + /* RX4 HIFi LineOut2 */ + WCD934X_INTERPOLATOR_PATH(4), + WCD934X_INTERPOLATOR_MIX2(4), + {"RX INT4 MIX3", NULL, "RX INT4 MIX2"}, + {"RX INT4 DAC", NULL, "RX INT4 MIX3"}, + {"RX INT4 DAC", NULL, "RX_BIAS"}, + {"LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"LINEOUT2", NULL, "LINEOUT2 PA"}, + + /* RX7 Speaker Left Out PA */ + WCD934X_INTERPOLATOR_PATH(7), + WCD934X_INTERPOLATOR_MIX2(7), + {"RX INT7 CHAIN", NULL, "RX INT7 MIX2"}, + {"RX INT7 CHAIN", NULL, "RX_BIAS"}, + {"RX INT7 CHAIN", NULL, "SBOOST0"}, + {"RX INT7 CHAIN", NULL, "SBOOST0_CLK"}, + {"SPK1 OUT", NULL, "RX INT7 CHAIN"}, + + /* RX8 Speaker Right Out PA */ + WCD934X_INTERPOLATOR_PATH(8), + {"RX INT8 CHAIN", NULL, "RX INT8 SEC MIX"}, + {"RX INT8 CHAIN", NULL, "RX_BIAS"}, + {"RX INT8 CHAIN", NULL, "SBOOST1"}, + {"RX INT8 CHAIN", NULL, "SBOOST1_CLK"}, + {"SPK2 OUT", NULL, "RX INT8 CHAIN"}, + + /* Tx */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + + WCD934X_SLIM_TX_AIF_PATH(0), + WCD934X_SLIM_TX_AIF_PATH(1), + WCD934X_SLIM_TX_AIF_PATH(2), + WCD934X_SLIM_TX_AIF_PATH(3), + WCD934X_SLIM_TX_AIF_PATH(4), + WCD934X_SLIM_TX_AIF_PATH(5), + WCD934X_SLIM_TX_AIF_PATH(6), + WCD934X_SLIM_TX_AIF_PATH(7), + WCD934X_SLIM_TX_AIF_PATH(8), + + WCD934X_ADC_MUX(0), + WCD934X_ADC_MUX(1), + WCD934X_ADC_MUX(2), + WCD934X_ADC_MUX(3), + WCD934X_ADC_MUX(4), + WCD934X_ADC_MUX(5), + WCD934X_ADC_MUX(6), + WCD934X_ADC_MUX(7), + WCD934X_ADC_MUX(8), + + {"CDC_IF TX0 MUX", "DEC0", "ADC MUX0"}, + {"CDC_IF TX1 MUX", "DEC1", "ADC MUX1"}, + {"CDC_IF TX2 MUX", "DEC2", "ADC MUX2"}, + {"CDC_IF TX3 MUX", "DEC3", "ADC MUX3"}, + {"CDC_IF TX4 MUX", "DEC4", "ADC MUX4"}, + {"CDC_IF TX5 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX6 MUX", "DEC6", "ADC MUX6"}, + {"CDC_IF TX7 MUX", "DEC7", "ADC MUX7"}, + {"CDC_IF TX8 MUX", "DEC8", "ADC MUX8"}, + + {"AMIC4_5 SEL", "AMIC4", "AMIC4"}, + {"AMIC4_5 SEL", "AMIC5", "AMIC5"}, + + { "DMIC0", NULL, "DMIC0 Pin" }, + { "DMIC1", NULL, "DMIC1 Pin" }, + { "DMIC2", NULL, "DMIC2 Pin" }, + { "DMIC3", NULL, "DMIC3 Pin" }, + { "DMIC4", NULL, "DMIC4 Pin" }, + { "DMIC5", NULL, "DMIC5 Pin" }, + + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "AMIC2"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "AMIC4_5 SEL"}, + + WCD934X_IIR_INP_MUX(0), + WCD934X_IIR_INP_MUX(1), + + {"SRC0", NULL, "IIR0"}, + {"SRC1", NULL, "IIR1"}, +}; + +static const struct snd_soc_component_driver wcd934x_component_drv = { + .probe = wcd934x_comp_probe, + .remove = wcd934x_comp_remove, + .set_sysclk = wcd934x_comp_set_sysclk, + .controls = wcd934x_snd_controls, + .num_controls = ARRAY_SIZE(wcd934x_snd_controls), + .dapm_widgets = wcd934x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wcd934x_dapm_widgets), + .dapm_routes = wcd934x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wcd934x_audio_map), +}; + +static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd) +{ + struct device *dev = &wcd->sdev->dev; + struct device_node *ifc_dev_np; + + ifc_dev_np = of_parse_phandle(dev->of_node, "slim-ifc-dev", 0); + if (!ifc_dev_np) { + dev_err(dev, "No Interface device found\n"); + return -EINVAL; + } + + wcd->sidev = of_slim_get_device(wcd->sdev->ctrl, ifc_dev_np); + if (!wcd->sidev) { + dev_err(dev, "Unable to get SLIM Interface device\n"); + return -EINVAL; + } + + slim_get_logical_addr(wcd->sidev); + wcd->if_regmap = regmap_init_slimbus(wcd->sidev, + &wcd934x_ifc_regmap_config); + if (IS_ERR(wcd->if_regmap)) { + dev_err(dev, "Failed to allocate ifc register map\n"); + return PTR_ERR(wcd->if_regmap); + } + + of_property_read_u32(dev->parent->of_node, "qcom,dmic-sample-rate", + &wcd->dmic_sample_rate); + + return 0; +} + +static int wcd934x_codec_probe(struct platform_device *pdev) +{ + struct wcd934x_data *data = dev_get_drvdata(pdev->dev.parent); + struct wcd934x_codec *wcd; + struct device *dev = &pdev->dev; + int ret, irq; + + wcd = devm_kzalloc(&pdev->dev, sizeof(*wcd), GFP_KERNEL); + if (!wcd) + return -ENOMEM; + + wcd->dev = dev; + wcd->regmap = data->regmap; + wcd->extclk = data->extclk; + wcd->sdev = data->sdev; + mutex_init(&wcd->sysclk_mutex); + + ret = wcd934x_codec_parse_data(wcd); + if (ret) { + dev_err(wcd->dev, "Failed to get SLIM IRQ\n"); + return ret; + } + + /* set default rate 9P6MHz */ + regmap_update_bits(wcd->regmap, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + WCD934X_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK, + WCD934X_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ); + memcpy(wcd->rx_chs, wcd934x_rx_chs, sizeof(wcd934x_rx_chs)); + memcpy(wcd->tx_chs, wcd934x_tx_chs, sizeof(wcd934x_tx_chs)); + + irq = regmap_irq_get_virq(data->irq_data, WCD934X_IRQ_SLIMBUS); + if (irq < 0) { + dev_err(wcd->dev, "Failed to get SLIM IRQ\n"); + return irq; + } + + ret = devm_request_threaded_irq(dev, irq, NULL, + wcd934x_slim_irq_handler, + IRQF_TRIGGER_RISING, + "slim", wcd); + if (ret) { + dev_err(dev, "Failed to request slimbus irq\n"); + return ret; + } + wcd934x_register_mclk_output(wcd); + platform_set_drvdata(pdev, wcd); + + return devm_snd_soc_register_component(dev, &wcd934x_component_drv, + wcd934x_slim_dais, + ARRAY_SIZE(wcd934x_slim_dais)); +} + +static const struct platform_device_id wcd934x_driver_id[] = { + { + .name = "wcd934x-codec", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, wcd934x_driver_id); + +static struct platform_driver wcd934x_codec_driver = { + .probe = &wcd934x_codec_probe, + .id_table = wcd934x_driver_id, + .driver = { + .name = "wcd934x-codec", + } +}; + +MODULE_ALIAS("platform:wcd934x-codec"); +module_platform_driver(wcd934x_codec_driver); +MODULE_DESCRIPTION("WCD934x codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c new file mode 100644 index 000000000000..f0def31513bf --- /dev/null +++ b/sound/soc/codecs/wsa881x.c @@ -0,0 +1,1153 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2017, The Linux Foundation. +// Copyright (c) 2019, Linaro Limited + +#include <linux/bitops.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define WSA881X_DIGITAL_BASE 0x3000 +#define WSA881X_ANALOG_BASE 0x3100 + +/* Digital register address space */ +#define WSA881X_CHIP_ID0 (WSA881X_DIGITAL_BASE + 0x0000) +#define WSA881X_CHIP_ID1 (WSA881X_DIGITAL_BASE + 0x0001) +#define WSA881X_CHIP_ID2 (WSA881X_DIGITAL_BASE + 0x0002) +#define WSA881X_CHIP_ID3 (WSA881X_DIGITAL_BASE + 0x0003) +#define WSA881X_BUS_ID (WSA881X_DIGITAL_BASE + 0x0004) +#define WSA881X_CDC_RST_CTL (WSA881X_DIGITAL_BASE + 0x0005) +#define WSA881X_CDC_TOP_CLK_CTL (WSA881X_DIGITAL_BASE + 0x0006) +#define WSA881X_CDC_ANA_CLK_CTL (WSA881X_DIGITAL_BASE + 0x0007) +#define WSA881X_CDC_DIG_CLK_CTL (WSA881X_DIGITAL_BASE + 0x0008) +#define WSA881X_CLOCK_CONFIG (WSA881X_DIGITAL_BASE + 0x0009) +#define WSA881X_ANA_CTL (WSA881X_DIGITAL_BASE + 0x000A) +#define WSA881X_SWR_RESET_EN (WSA881X_DIGITAL_BASE + 0x000B) +#define WSA881X_RESET_CTL (WSA881X_DIGITAL_BASE + 0x000C) +#define WSA881X_TADC_VALUE_CTL (WSA881X_DIGITAL_BASE + 0x000F) +#define WSA881X_TEMP_DETECT_CTL (WSA881X_DIGITAL_BASE + 0x0010) +#define WSA881X_TEMP_MSB (WSA881X_DIGITAL_BASE + 0x0011) +#define WSA881X_TEMP_LSB (WSA881X_DIGITAL_BASE + 0x0012) +#define WSA881X_TEMP_CONFIG0 (WSA881X_DIGITAL_BASE + 0x0013) +#define WSA881X_TEMP_CONFIG1 (WSA881X_DIGITAL_BASE + 0x0014) +#define WSA881X_CDC_CLIP_CTL (WSA881X_DIGITAL_BASE + 0x0015) +#define WSA881X_SDM_PDM9_LSB (WSA881X_DIGITAL_BASE + 0x0016) +#define WSA881X_SDM_PDM9_MSB (WSA881X_DIGITAL_BASE + 0x0017) +#define WSA881X_CDC_RX_CTL (WSA881X_DIGITAL_BASE + 0x0018) +#define WSA881X_DEM_BYPASS_DATA0 (WSA881X_DIGITAL_BASE + 0x0019) +#define WSA881X_DEM_BYPASS_DATA1 (WSA881X_DIGITAL_BASE + 0x001A) +#define WSA881X_DEM_BYPASS_DATA2 (WSA881X_DIGITAL_BASE + 0x001B) +#define WSA881X_DEM_BYPASS_DATA3 (WSA881X_DIGITAL_BASE + 0x001C) +#define WSA881X_OTP_CTRL0 (WSA881X_DIGITAL_BASE + 0x001D) +#define WSA881X_OTP_CTRL1 (WSA881X_DIGITAL_BASE + 0x001E) +#define WSA881X_HDRIVE_CTL_GROUP1 (WSA881X_DIGITAL_BASE + 0x001F) +#define WSA881X_INTR_MODE (WSA881X_DIGITAL_BASE + 0x0020) +#define WSA881X_INTR_MASK (WSA881X_DIGITAL_BASE + 0x0021) +#define WSA881X_INTR_STATUS (WSA881X_DIGITAL_BASE + 0x0022) +#define WSA881X_INTR_CLEAR (WSA881X_DIGITAL_BASE + 0x0023) +#define WSA881X_INTR_LEVEL (WSA881X_DIGITAL_BASE + 0x0024) +#define WSA881X_INTR_SET (WSA881X_DIGITAL_BASE + 0x0025) +#define WSA881X_INTR_TEST (WSA881X_DIGITAL_BASE + 0x0026) +#define WSA881X_PDM_TEST_MODE (WSA881X_DIGITAL_BASE + 0x0030) +#define WSA881X_ATE_TEST_MODE (WSA881X_DIGITAL_BASE + 0x0031) +#define WSA881X_PIN_CTL_MODE (WSA881X_DIGITAL_BASE + 0x0032) +#define WSA881X_PIN_CTL_OE (WSA881X_DIGITAL_BASE + 0x0033) +#define WSA881X_PIN_WDATA_IOPAD (WSA881X_DIGITAL_BASE + 0x0034) +#define WSA881X_PIN_STATUS (WSA881X_DIGITAL_BASE + 0x0035) +#define WSA881X_DIG_DEBUG_MODE (WSA881X_DIGITAL_BASE + 0x0037) +#define WSA881X_DIG_DEBUG_SEL (WSA881X_DIGITAL_BASE + 0x0038) +#define WSA881X_DIG_DEBUG_EN (WSA881X_DIGITAL_BASE + 0x0039) +#define WSA881X_SWR_HM_TEST1 (WSA881X_DIGITAL_BASE + 0x003B) +#define WSA881X_SWR_HM_TEST2 (WSA881X_DIGITAL_BASE + 0x003C) +#define WSA881X_TEMP_DETECT_DBG_CTL (WSA881X_DIGITAL_BASE + 0x003D) +#define WSA881X_TEMP_DEBUG_MSB (WSA881X_DIGITAL_BASE + 0x003E) +#define WSA881X_TEMP_DEBUG_LSB (WSA881X_DIGITAL_BASE + 0x003F) +#define WSA881X_SAMPLE_EDGE_SEL (WSA881X_DIGITAL_BASE + 0x0044) +#define WSA881X_IOPAD_CTL (WSA881X_DIGITAL_BASE + 0x0045) +#define WSA881X_SPARE_0 (WSA881X_DIGITAL_BASE + 0x0050) +#define WSA881X_SPARE_1 (WSA881X_DIGITAL_BASE + 0x0051) +#define WSA881X_SPARE_2 (WSA881X_DIGITAL_BASE + 0x0052) +#define WSA881X_OTP_REG_0 (WSA881X_DIGITAL_BASE + 0x0080) +#define WSA881X_OTP_REG_1 (WSA881X_DIGITAL_BASE + 0x0081) +#define WSA881X_OTP_REG_2 (WSA881X_DIGITAL_BASE + 0x0082) +#define WSA881X_OTP_REG_3 (WSA881X_DIGITAL_BASE + 0x0083) +#define WSA881X_OTP_REG_4 (WSA881X_DIGITAL_BASE + 0x0084) +#define WSA881X_OTP_REG_5 (WSA881X_DIGITAL_BASE + 0x0085) +#define WSA881X_OTP_REG_6 (WSA881X_DIGITAL_BASE + 0x0086) +#define WSA881X_OTP_REG_7 (WSA881X_DIGITAL_BASE + 0x0087) +#define WSA881X_OTP_REG_8 (WSA881X_DIGITAL_BASE + 0x0088) +#define WSA881X_OTP_REG_9 (WSA881X_DIGITAL_BASE + 0x0089) +#define WSA881X_OTP_REG_10 (WSA881X_DIGITAL_BASE + 0x008A) +#define WSA881X_OTP_REG_11 (WSA881X_DIGITAL_BASE + 0x008B) +#define WSA881X_OTP_REG_12 (WSA881X_DIGITAL_BASE + 0x008C) +#define WSA881X_OTP_REG_13 (WSA881X_DIGITAL_BASE + 0x008D) +#define WSA881X_OTP_REG_14 (WSA881X_DIGITAL_BASE + 0x008E) +#define WSA881X_OTP_REG_15 (WSA881X_DIGITAL_BASE + 0x008F) +#define WSA881X_OTP_REG_16 (WSA881X_DIGITAL_BASE + 0x0090) +#define WSA881X_OTP_REG_17 (WSA881X_DIGITAL_BASE + 0x0091) +#define WSA881X_OTP_REG_18 (WSA881X_DIGITAL_BASE + 0x0092) +#define WSA881X_OTP_REG_19 (WSA881X_DIGITAL_BASE + 0x0093) +#define WSA881X_OTP_REG_20 (WSA881X_DIGITAL_BASE + 0x0094) +#define WSA881X_OTP_REG_21 (WSA881X_DIGITAL_BASE + 0x0095) +#define WSA881X_OTP_REG_22 (WSA881X_DIGITAL_BASE + 0x0096) +#define WSA881X_OTP_REG_23 (WSA881X_DIGITAL_BASE + 0x0097) +#define WSA881X_OTP_REG_24 (WSA881X_DIGITAL_BASE + 0x0098) +#define WSA881X_OTP_REG_25 (WSA881X_DIGITAL_BASE + 0x0099) +#define WSA881X_OTP_REG_26 (WSA881X_DIGITAL_BASE + 0x009A) +#define WSA881X_OTP_REG_27 (WSA881X_DIGITAL_BASE + 0x009B) +#define WSA881X_OTP_REG_28 (WSA881X_DIGITAL_BASE + 0x009C) +#define WSA881X_OTP_REG_29 (WSA881X_DIGITAL_BASE + 0x009D) +#define WSA881X_OTP_REG_30 (WSA881X_DIGITAL_BASE + 0x009E) +#define WSA881X_OTP_REG_31 (WSA881X_DIGITAL_BASE + 0x009F) +#define WSA881X_OTP_REG_63 (WSA881X_DIGITAL_BASE + 0x00BF) + +/* Analog Register address space */ +#define WSA881X_BIAS_REF_CTRL (WSA881X_ANALOG_BASE + 0x0000) +#define WSA881X_BIAS_TEST (WSA881X_ANALOG_BASE + 0x0001) +#define WSA881X_BIAS_BIAS (WSA881X_ANALOG_BASE + 0x0002) +#define WSA881X_TEMP_OP (WSA881X_ANALOG_BASE + 0x0003) +#define WSA881X_TEMP_IREF_CTRL (WSA881X_ANALOG_BASE + 0x0004) +#define WSA881X_TEMP_ISENS_CTRL (WSA881X_ANALOG_BASE + 0x0005) +#define WSA881X_TEMP_CLK_CTRL (WSA881X_ANALOG_BASE + 0x0006) +#define WSA881X_TEMP_TEST (WSA881X_ANALOG_BASE + 0x0007) +#define WSA881X_TEMP_BIAS (WSA881X_ANALOG_BASE + 0x0008) +#define WSA881X_TEMP_ADC_CTRL (WSA881X_ANALOG_BASE + 0x0009) +#define WSA881X_TEMP_DOUT_MSB (WSA881X_ANALOG_BASE + 0x000A) +#define WSA881X_TEMP_DOUT_LSB (WSA881X_ANALOG_BASE + 0x000B) +#define WSA881X_ADC_EN_MODU_V (WSA881X_ANALOG_BASE + 0x0010) +#define WSA881X_ADC_EN_MODU_I (WSA881X_ANALOG_BASE + 0x0011) +#define WSA881X_ADC_EN_DET_TEST_V (WSA881X_ANALOG_BASE + 0x0012) +#define WSA881X_ADC_EN_DET_TEST_I (WSA881X_ANALOG_BASE + 0x0013) +#define WSA881X_ADC_SEL_IBIAS (WSA881X_ANALOG_BASE + 0x0014) +#define WSA881X_ADC_EN_SEL_IBAIS (WSA881X_ANALOG_BASE + 0x0015) +#define WSA881X_SPKR_DRV_EN (WSA881X_ANALOG_BASE + 0x001A) +#define WSA881X_SPKR_DRV_GAIN (WSA881X_ANALOG_BASE + 0x001B) +#define WSA881X_PA_GAIN_SEL_MASK BIT(3) +#define WSA881X_PA_GAIN_SEL_REG BIT(3) +#define WSA881X_PA_GAIN_SEL_DRE 0 +#define WSA881X_SPKR_PAG_GAIN_MASK GENMASK(7, 4) +#define WSA881X_SPKR_DAC_CTL (WSA881X_ANALOG_BASE + 0x001C) +#define WSA881X_SPKR_DRV_DBG (WSA881X_ANALOG_BASE + 0x001D) +#define WSA881X_SPKR_PWRSTG_DBG (WSA881X_ANALOG_BASE + 0x001E) +#define WSA881X_SPKR_OCP_CTL (WSA881X_ANALOG_BASE + 0x001F) +#define WSA881X_SPKR_OCP_MASK GENMASK(7, 6) +#define WSA881X_SPKR_OCP_EN BIT(7) +#define WSA881X_SPKR_OCP_HOLD BIT(6) +#define WSA881X_SPKR_CLIP_CTL (WSA881X_ANALOG_BASE + 0x0020) +#define WSA881X_SPKR_BBM_CTL (WSA881X_ANALOG_BASE + 0x0021) +#define WSA881X_SPKR_MISC_CTL1 (WSA881X_ANALOG_BASE + 0x0022) +#define WSA881X_SPKR_MISC_CTL2 (WSA881X_ANALOG_BASE + 0x0023) +#define WSA881X_SPKR_BIAS_INT (WSA881X_ANALOG_BASE + 0x0024) +#define WSA881X_SPKR_PA_INT (WSA881X_ANALOG_BASE + 0x0025) +#define WSA881X_SPKR_BIAS_CAL (WSA881X_ANALOG_BASE + 0x0026) +#define WSA881X_SPKR_BIAS_PSRR (WSA881X_ANALOG_BASE + 0x0027) +#define WSA881X_SPKR_STATUS1 (WSA881X_ANALOG_BASE + 0x0028) +#define WSA881X_SPKR_STATUS2 (WSA881X_ANALOG_BASE + 0x0029) +#define WSA881X_BOOST_EN_CTL (WSA881X_ANALOG_BASE + 0x002A) +#define WSA881X_BOOST_EN_MASK BIT(7) +#define WSA881X_BOOST_EN BIT(7) +#define WSA881X_BOOST_CURRENT_LIMIT (WSA881X_ANALOG_BASE + 0x002B) +#define WSA881X_BOOST_PS_CTL (WSA881X_ANALOG_BASE + 0x002C) +#define WSA881X_BOOST_PRESET_OUT1 (WSA881X_ANALOG_BASE + 0x002D) +#define WSA881X_BOOST_PRESET_OUT2 (WSA881X_ANALOG_BASE + 0x002E) +#define WSA881X_BOOST_FORCE_OUT (WSA881X_ANALOG_BASE + 0x002F) +#define WSA881X_BOOST_LDO_PROG (WSA881X_ANALOG_BASE + 0x0030) +#define WSA881X_BOOST_SLOPE_COMP_ISENSE_FB (WSA881X_ANALOG_BASE + 0x0031) +#define WSA881X_BOOST_RON_CTL (WSA881X_ANALOG_BASE + 0x0032) +#define WSA881X_BOOST_LOOP_STABILITY (WSA881X_ANALOG_BASE + 0x0033) +#define WSA881X_BOOST_ZX_CTL (WSA881X_ANALOG_BASE + 0x0034) +#define WSA881X_BOOST_START_CTL (WSA881X_ANALOG_BASE + 0x0035) +#define WSA881X_BOOST_MISC1_CTL (WSA881X_ANALOG_BASE + 0x0036) +#define WSA881X_BOOST_MISC2_CTL (WSA881X_ANALOG_BASE + 0x0037) +#define WSA881X_BOOST_MISC3_CTL (WSA881X_ANALOG_BASE + 0x0038) +#define WSA881X_BOOST_ATEST_CTL (WSA881X_ANALOG_BASE + 0x0039) +#define WSA881X_SPKR_PROT_FE_GAIN (WSA881X_ANALOG_BASE + 0x003A) +#define WSA881X_SPKR_PROT_FE_CM_LDO_SET (WSA881X_ANALOG_BASE + 0x003B) +#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 (WSA881X_ANALOG_BASE + 0x003C) +#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 (WSA881X_ANALOG_BASE + 0x003D) +#define WSA881X_SPKR_PROT_ATEST1 (WSA881X_ANALOG_BASE + 0x003E) +#define WSA881X_SPKR_PROT_ATEST2 (WSA881X_ANALOG_BASE + 0x003F) +#define WSA881X_SPKR_PROT_FE_VSENSE_VCM (WSA881X_ANALOG_BASE + 0x0040) +#define WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 (WSA881X_ANALOG_BASE + 0x0041) +#define WSA881X_BONGO_RESRV_REG1 (WSA881X_ANALOG_BASE + 0x0042) +#define WSA881X_BONGO_RESRV_REG2 (WSA881X_ANALOG_BASE + 0x0043) +#define WSA881X_SPKR_PROT_SAR (WSA881X_ANALOG_BASE + 0x0044) +#define WSA881X_SPKR_STATUS3 (WSA881X_ANALOG_BASE + 0x0045) + +#define SWRS_SCP_FRAME_CTRL_BANK(m) (0x60 + 0x10 * (m)) +#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) +#define SWR_SLV_MAX_REG_ADDR 0x390 +#define SWR_SLV_START_REG_ADDR 0x40 +#define SWR_SLV_MAX_BUF_LEN 20 +#define BYTES_PER_LINE 12 +#define SWR_SLV_RD_BUF_LEN 8 +#define SWR_SLV_WR_BUF_LEN 32 +#define SWR_SLV_MAX_DEVICES 2 +#define WSA881X_MAX_SWR_PORTS 4 +#define WSA881X_VERSION_ENTRY_SIZE 27 +#define WSA881X_OCP_CTL_TIMER_SEC 2 +#define WSA881X_OCP_CTL_TEMP_CELSIUS 25 +#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60 + +#define WSA881X_PA_GAIN_TLV(xname, reg, shift, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ + .put = wsa881x_put_pa_gain, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } + +static struct reg_default wsa881x_defaults[] = { + {WSA881X_CHIP_ID0, 0x00}, + {WSA881X_CHIP_ID1, 0x00}, + {WSA881X_CHIP_ID2, 0x00}, + {WSA881X_CHIP_ID3, 0x02}, + {WSA881X_BUS_ID, 0x00}, + {WSA881X_CDC_RST_CTL, 0x00}, + {WSA881X_CDC_TOP_CLK_CTL, 0x03}, + {WSA881X_CDC_ANA_CLK_CTL, 0x00}, + {WSA881X_CDC_DIG_CLK_CTL, 0x00}, + {WSA881X_CLOCK_CONFIG, 0x00}, + {WSA881X_ANA_CTL, 0x08}, + {WSA881X_SWR_RESET_EN, 0x00}, + {WSA881X_TEMP_DETECT_CTL, 0x01}, + {WSA881X_TEMP_MSB, 0x00}, + {WSA881X_TEMP_LSB, 0x00}, + {WSA881X_TEMP_CONFIG0, 0x00}, + {WSA881X_TEMP_CONFIG1, 0x00}, + {WSA881X_CDC_CLIP_CTL, 0x03}, + {WSA881X_SDM_PDM9_LSB, 0x00}, + {WSA881X_SDM_PDM9_MSB, 0x00}, + {WSA881X_CDC_RX_CTL, 0x7E}, + {WSA881X_DEM_BYPASS_DATA0, 0x00}, + {WSA881X_DEM_BYPASS_DATA1, 0x00}, + {WSA881X_DEM_BYPASS_DATA2, 0x00}, + {WSA881X_DEM_BYPASS_DATA3, 0x00}, + {WSA881X_OTP_CTRL0, 0x00}, + {WSA881X_OTP_CTRL1, 0x00}, + {WSA881X_HDRIVE_CTL_GROUP1, 0x00}, + {WSA881X_INTR_MODE, 0x00}, + {WSA881X_INTR_STATUS, 0x00}, + {WSA881X_INTR_CLEAR, 0x00}, + {WSA881X_INTR_LEVEL, 0x00}, + {WSA881X_INTR_SET, 0x00}, + {WSA881X_INTR_TEST, 0x00}, + {WSA881X_PDM_TEST_MODE, 0x00}, + {WSA881X_ATE_TEST_MODE, 0x00}, + {WSA881X_PIN_CTL_MODE, 0x00}, + {WSA881X_PIN_CTL_OE, 0x00}, + {WSA881X_PIN_WDATA_IOPAD, 0x00}, + {WSA881X_PIN_STATUS, 0x00}, + {WSA881X_DIG_DEBUG_MODE, 0x00}, + {WSA881X_DIG_DEBUG_SEL, 0x00}, + {WSA881X_DIG_DEBUG_EN, 0x00}, + {WSA881X_SWR_HM_TEST1, 0x08}, + {WSA881X_SWR_HM_TEST2, 0x00}, + {WSA881X_TEMP_DETECT_DBG_CTL, 0x00}, + {WSA881X_TEMP_DEBUG_MSB, 0x00}, + {WSA881X_TEMP_DEBUG_LSB, 0x00}, + {WSA881X_SAMPLE_EDGE_SEL, 0x0C}, + {WSA881X_SPARE_0, 0x00}, + {WSA881X_SPARE_1, 0x00}, + {WSA881X_SPARE_2, 0x00}, + {WSA881X_OTP_REG_0, 0x01}, + {WSA881X_OTP_REG_1, 0xFF}, + {WSA881X_OTP_REG_2, 0xC0}, + {WSA881X_OTP_REG_3, 0xFF}, + {WSA881X_OTP_REG_4, 0xC0}, + {WSA881X_OTP_REG_5, 0xFF}, + {WSA881X_OTP_REG_6, 0xFF}, + {WSA881X_OTP_REG_7, 0xFF}, + {WSA881X_OTP_REG_8, 0xFF}, + {WSA881X_OTP_REG_9, 0xFF}, + {WSA881X_OTP_REG_10, 0xFF}, + {WSA881X_OTP_REG_11, 0xFF}, + {WSA881X_OTP_REG_12, 0xFF}, + {WSA881X_OTP_REG_13, 0xFF}, + {WSA881X_OTP_REG_14, 0xFF}, + {WSA881X_OTP_REG_15, 0xFF}, + {WSA881X_OTP_REG_16, 0xFF}, + {WSA881X_OTP_REG_17, 0xFF}, + {WSA881X_OTP_REG_18, 0xFF}, + {WSA881X_OTP_REG_19, 0xFF}, + {WSA881X_OTP_REG_20, 0xFF}, + {WSA881X_OTP_REG_21, 0xFF}, + {WSA881X_OTP_REG_22, 0xFF}, + {WSA881X_OTP_REG_23, 0xFF}, + {WSA881X_OTP_REG_24, 0x03}, + {WSA881X_OTP_REG_25, 0x01}, + {WSA881X_OTP_REG_26, 0x03}, + {WSA881X_OTP_REG_27, 0x11}, + {WSA881X_OTP_REG_63, 0x40}, + /* WSA881x Analog registers */ + {WSA881X_BIAS_REF_CTRL, 0x6C}, + {WSA881X_BIAS_TEST, 0x16}, + {WSA881X_BIAS_BIAS, 0xF0}, + {WSA881X_TEMP_OP, 0x00}, + {WSA881X_TEMP_IREF_CTRL, 0x56}, + {WSA881X_TEMP_ISENS_CTRL, 0x47}, + {WSA881X_TEMP_CLK_CTRL, 0x87}, + {WSA881X_TEMP_TEST, 0x00}, + {WSA881X_TEMP_BIAS, 0x51}, + {WSA881X_TEMP_DOUT_MSB, 0x00}, + {WSA881X_TEMP_DOUT_LSB, 0x00}, + {WSA881X_ADC_EN_MODU_V, 0x00}, + {WSA881X_ADC_EN_MODU_I, 0x00}, + {WSA881X_ADC_EN_DET_TEST_V, 0x00}, + {WSA881X_ADC_EN_DET_TEST_I, 0x00}, + {WSA881X_ADC_EN_SEL_IBAIS, 0x10}, + {WSA881X_SPKR_DRV_EN, 0x74}, + {WSA881X_SPKR_DRV_DBG, 0x15}, + {WSA881X_SPKR_PWRSTG_DBG, 0x00}, + {WSA881X_SPKR_OCP_CTL, 0xD4}, + {WSA881X_SPKR_CLIP_CTL, 0x90}, + {WSA881X_SPKR_PA_INT, 0x54}, + {WSA881X_SPKR_BIAS_CAL, 0xAC}, + {WSA881X_SPKR_STATUS1, 0x00}, + {WSA881X_SPKR_STATUS2, 0x00}, + {WSA881X_BOOST_EN_CTL, 0x18}, + {WSA881X_BOOST_CURRENT_LIMIT, 0x7A}, + {WSA881X_BOOST_PRESET_OUT2, 0x70}, + {WSA881X_BOOST_FORCE_OUT, 0x0E}, + {WSA881X_BOOST_LDO_PROG, 0x16}, + {WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x71}, + {WSA881X_BOOST_RON_CTL, 0x0F}, + {WSA881X_BOOST_ZX_CTL, 0x34}, + {WSA881X_BOOST_START_CTL, 0x23}, + {WSA881X_BOOST_MISC1_CTL, 0x80}, + {WSA881X_BOOST_MISC2_CTL, 0x00}, + {WSA881X_BOOST_MISC3_CTL, 0x00}, + {WSA881X_BOOST_ATEST_CTL, 0x00}, + {WSA881X_SPKR_PROT_FE_GAIN, 0x46}, + {WSA881X_SPKR_PROT_FE_CM_LDO_SET, 0x3B}, + {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1, 0x8D}, + {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2, 0x8D}, + {WSA881X_SPKR_PROT_ATEST1, 0x01}, + {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D}, + {WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D}, + {WSA881X_SPKR_PROT_SAR, 0x00}, + {WSA881X_SPKR_STATUS3, 0x00}, +}; + +static const struct reg_sequence wsa881x_pre_pmu_pa_2_0[] = { + {WSA881X_SPKR_DRV_GAIN, 0x41, 0}, + {WSA881X_SPKR_MISC_CTL1, 0x87, 0}, +}; + +static const struct reg_sequence wsa881x_vi_txfe_en_2_0[] = { + {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0}, + {WSA881X_SPKR_PROT_ATEST2, 0x0A, 0}, + {WSA881X_SPKR_PROT_FE_GAIN, 0x47, 0}, +}; + +/* Default register reset values for WSA881x rev 2.0 */ +static struct reg_sequence wsa881x_rev_2_0[] = { + {WSA881X_RESET_CTL, 0x00, 0x00}, + {WSA881X_TADC_VALUE_CTL, 0x01, 0x00}, + {WSA881X_INTR_MASK, 0x1B, 0x00}, + {WSA881X_IOPAD_CTL, 0x00, 0x00}, + {WSA881X_OTP_REG_28, 0x3F, 0x00}, + {WSA881X_OTP_REG_29, 0x3F, 0x00}, + {WSA881X_OTP_REG_30, 0x01, 0x00}, + {WSA881X_OTP_REG_31, 0x01, 0x00}, + {WSA881X_TEMP_ADC_CTRL, 0x03, 0x00}, + {WSA881X_ADC_SEL_IBIAS, 0x45, 0x00}, + {WSA881X_SPKR_DRV_GAIN, 0xC1, 0x00}, + {WSA881X_SPKR_DAC_CTL, 0x42, 0x00}, + {WSA881X_SPKR_BBM_CTL, 0x02, 0x00}, + {WSA881X_SPKR_MISC_CTL1, 0x40, 0x00}, + {WSA881X_SPKR_MISC_CTL2, 0x07, 0x00}, + {WSA881X_SPKR_BIAS_INT, 0x5F, 0x00}, + {WSA881X_SPKR_BIAS_PSRR, 0x44, 0x00}, + {WSA881X_BOOST_PS_CTL, 0xA0, 0x00}, + {WSA881X_BOOST_PRESET_OUT1, 0xB7, 0x00}, + {WSA881X_BOOST_LOOP_STABILITY, 0x8D, 0x00}, + {WSA881X_SPKR_PROT_ATEST2, 0x02, 0x00}, + {WSA881X_BONGO_RESRV_REG1, 0x5E, 0x00}, + {WSA881X_BONGO_RESRV_REG2, 0x07, 0x00}, +}; + +enum wsa_port_ids { + WSA881X_PORT_DAC, + WSA881X_PORT_COMP, + WSA881X_PORT_BOOST, + WSA881X_PORT_VISENSE, +}; + +/* 4 ports */ +static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = { + { + /* DAC */ + .num = 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + }, { + /* COMP */ + .num = 2, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + }, { + /* BOOST */ + .num = 3, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + }, { + /* VISENSE */ + .num = 4, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + } +}; + +static struct sdw_port_config wsa881x_pconfig[WSA881X_MAX_SWR_PORTS] = { + { + .num = 1, + .ch_mask = 0x1, + }, { + .num = 2, + .ch_mask = 0xf, + }, { + .num = 3, + .ch_mask = 0x3, + }, { /* IV feedback */ + .num = 4, + .ch_mask = 0x3, + }, +}; + +static bool wsa881x_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA881X_CHIP_ID0: + case WSA881X_CHIP_ID1: + case WSA881X_CHIP_ID2: + case WSA881X_CHIP_ID3: + case WSA881X_BUS_ID: + case WSA881X_CDC_RST_CTL: + case WSA881X_CDC_TOP_CLK_CTL: + case WSA881X_CDC_ANA_CLK_CTL: + case WSA881X_CDC_DIG_CLK_CTL: + case WSA881X_CLOCK_CONFIG: + case WSA881X_ANA_CTL: + case WSA881X_SWR_RESET_EN: + case WSA881X_RESET_CTL: + case WSA881X_TADC_VALUE_CTL: + case WSA881X_TEMP_DETECT_CTL: + case WSA881X_TEMP_MSB: + case WSA881X_TEMP_LSB: + case WSA881X_TEMP_CONFIG0: + case WSA881X_TEMP_CONFIG1: + case WSA881X_CDC_CLIP_CTL: + case WSA881X_SDM_PDM9_LSB: + case WSA881X_SDM_PDM9_MSB: + case WSA881X_CDC_RX_CTL: + case WSA881X_DEM_BYPASS_DATA0: + case WSA881X_DEM_BYPASS_DATA1: + case WSA881X_DEM_BYPASS_DATA2: + case WSA881X_DEM_BYPASS_DATA3: + case WSA881X_OTP_CTRL0: + case WSA881X_OTP_CTRL1: + case WSA881X_HDRIVE_CTL_GROUP1: + case WSA881X_INTR_MODE: + case WSA881X_INTR_MASK: + case WSA881X_INTR_STATUS: + case WSA881X_INTR_CLEAR: + case WSA881X_INTR_LEVEL: + case WSA881X_INTR_SET: + case WSA881X_INTR_TEST: + case WSA881X_PDM_TEST_MODE: + case WSA881X_ATE_TEST_MODE: + case WSA881X_PIN_CTL_MODE: + case WSA881X_PIN_CTL_OE: + case WSA881X_PIN_WDATA_IOPAD: + case WSA881X_PIN_STATUS: + case WSA881X_DIG_DEBUG_MODE: + case WSA881X_DIG_DEBUG_SEL: + case WSA881X_DIG_DEBUG_EN: + case WSA881X_SWR_HM_TEST1: + case WSA881X_SWR_HM_TEST2: + case WSA881X_TEMP_DETECT_DBG_CTL: + case WSA881X_TEMP_DEBUG_MSB: + case WSA881X_TEMP_DEBUG_LSB: + case WSA881X_SAMPLE_EDGE_SEL: + case WSA881X_IOPAD_CTL: + case WSA881X_SPARE_0: + case WSA881X_SPARE_1: + case WSA881X_SPARE_2: + case WSA881X_OTP_REG_0: + case WSA881X_OTP_REG_1: + case WSA881X_OTP_REG_2: + case WSA881X_OTP_REG_3: + case WSA881X_OTP_REG_4: + case WSA881X_OTP_REG_5: + case WSA881X_OTP_REG_6: + case WSA881X_OTP_REG_7: + case WSA881X_OTP_REG_8: + case WSA881X_OTP_REG_9: + case WSA881X_OTP_REG_10: + case WSA881X_OTP_REG_11: + case WSA881X_OTP_REG_12: + case WSA881X_OTP_REG_13: + case WSA881X_OTP_REG_14: + case WSA881X_OTP_REG_15: + case WSA881X_OTP_REG_16: + case WSA881X_OTP_REG_17: + case WSA881X_OTP_REG_18: + case WSA881X_OTP_REG_19: + case WSA881X_OTP_REG_20: + case WSA881X_OTP_REG_21: + case WSA881X_OTP_REG_22: + case WSA881X_OTP_REG_23: + case WSA881X_OTP_REG_24: + case WSA881X_OTP_REG_25: + case WSA881X_OTP_REG_26: + case WSA881X_OTP_REG_27: + case WSA881X_OTP_REG_28: + case WSA881X_OTP_REG_29: + case WSA881X_OTP_REG_30: + case WSA881X_OTP_REG_31: + case WSA881X_OTP_REG_63: + case WSA881X_BIAS_REF_CTRL: + case WSA881X_BIAS_TEST: + case WSA881X_BIAS_BIAS: + case WSA881X_TEMP_OP: + case WSA881X_TEMP_IREF_CTRL: + case WSA881X_TEMP_ISENS_CTRL: + case WSA881X_TEMP_CLK_CTRL: + case WSA881X_TEMP_TEST: + case WSA881X_TEMP_BIAS: + case WSA881X_TEMP_ADC_CTRL: + case WSA881X_TEMP_DOUT_MSB: + case WSA881X_TEMP_DOUT_LSB: + case WSA881X_ADC_EN_MODU_V: + case WSA881X_ADC_EN_MODU_I: + case WSA881X_ADC_EN_DET_TEST_V: + case WSA881X_ADC_EN_DET_TEST_I: + case WSA881X_ADC_SEL_IBIAS: + case WSA881X_ADC_EN_SEL_IBAIS: + case WSA881X_SPKR_DRV_EN: + case WSA881X_SPKR_DRV_GAIN: + case WSA881X_SPKR_DAC_CTL: + case WSA881X_SPKR_DRV_DBG: + case WSA881X_SPKR_PWRSTG_DBG: + case WSA881X_SPKR_OCP_CTL: + case WSA881X_SPKR_CLIP_CTL: + case WSA881X_SPKR_BBM_CTL: + case WSA881X_SPKR_MISC_CTL1: + case WSA881X_SPKR_MISC_CTL2: + case WSA881X_SPKR_BIAS_INT: + case WSA881X_SPKR_PA_INT: + case WSA881X_SPKR_BIAS_CAL: + case WSA881X_SPKR_BIAS_PSRR: + case WSA881X_SPKR_STATUS1: + case WSA881X_SPKR_STATUS2: + case WSA881X_BOOST_EN_CTL: + case WSA881X_BOOST_CURRENT_LIMIT: + case WSA881X_BOOST_PS_CTL: + case WSA881X_BOOST_PRESET_OUT1: + case WSA881X_BOOST_PRESET_OUT2: + case WSA881X_BOOST_FORCE_OUT: + case WSA881X_BOOST_LDO_PROG: + case WSA881X_BOOST_SLOPE_COMP_ISENSE_FB: + case WSA881X_BOOST_RON_CTL: + case WSA881X_BOOST_LOOP_STABILITY: + case WSA881X_BOOST_ZX_CTL: + case WSA881X_BOOST_START_CTL: + case WSA881X_BOOST_MISC1_CTL: + case WSA881X_BOOST_MISC2_CTL: + case WSA881X_BOOST_MISC3_CTL: + case WSA881X_BOOST_ATEST_CTL: + case WSA881X_SPKR_PROT_FE_GAIN: + case WSA881X_SPKR_PROT_FE_CM_LDO_SET: + case WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1: + case WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2: + case WSA881X_SPKR_PROT_ATEST1: + case WSA881X_SPKR_PROT_ATEST2: + case WSA881X_SPKR_PROT_FE_VSENSE_VCM: + case WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1: + case WSA881X_BONGO_RESRV_REG1: + case WSA881X_BONGO_RESRV_REG2: + case WSA881X_SPKR_PROT_SAR: + case WSA881X_SPKR_STATUS3: + return true; + default: + return false; + } +} + +static bool wsa881x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA881X_CHIP_ID0: + case WSA881X_CHIP_ID1: + case WSA881X_CHIP_ID2: + case WSA881X_CHIP_ID3: + case WSA881X_BUS_ID: + case WSA881X_TEMP_MSB: + case WSA881X_TEMP_LSB: + case WSA881X_SDM_PDM9_LSB: + case WSA881X_SDM_PDM9_MSB: + case WSA881X_OTP_CTRL1: + case WSA881X_INTR_STATUS: + case WSA881X_ATE_TEST_MODE: + case WSA881X_PIN_STATUS: + case WSA881X_SWR_HM_TEST2: + case WSA881X_SPKR_STATUS1: + case WSA881X_SPKR_STATUS2: + case WSA881X_SPKR_STATUS3: + case WSA881X_OTP_REG_0: + case WSA881X_OTP_REG_1: + case WSA881X_OTP_REG_2: + case WSA881X_OTP_REG_3: + case WSA881X_OTP_REG_4: + case WSA881X_OTP_REG_5: + case WSA881X_OTP_REG_31: + case WSA881X_TEMP_DOUT_MSB: + case WSA881X_TEMP_DOUT_LSB: + case WSA881X_TEMP_OP: + case WSA881X_SPKR_PROT_SAR: + return true; + default: + return false; + } +} + +static struct regmap_config wsa881x_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wsa881x_defaults, + .num_reg_defaults = ARRAY_SIZE(wsa881x_defaults), + .volatile_reg = wsa881x_volatile_register, + .readable_reg = wsa881x_readable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .can_multi_write = true, +}; + +enum { + G_18DB = 0, + G_16P5DB, + G_15DB, + G_13P5DB, + G_12DB, + G_10P5DB, + G_9DB, + G_7P5DB, + G_6DB, + G_4P5DB, + G_3DB, + G_1P5DB, + G_0DB, +}; + +/* + * Private data Structure for wsa881x. All parameters related to + * WSA881X codec needs to be defined here. + */ +struct wsa881x_priv { + struct regmap *regmap; + struct device *dev; + struct sdw_slave *slave; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WSA881X_MAX_SWR_PORTS]; + struct gpio_desc *sd_n; + int version; + int active_ports; + bool port_prepared[WSA881X_MAX_SWR_PORTS]; + bool port_enable[WSA881X_MAX_SWR_PORTS]; + u8 pa_gain; +}; + +static void wsa881x_init(struct wsa881x_priv *wsa881x) +{ + struct regmap *rm = wsa881x->regmap; + unsigned int val = 0; + + regmap_read(rm, WSA881X_CHIP_ID1, &wsa881x->version); + regmap_register_patch(wsa881x->regmap, wsa881x_rev_2_0, + ARRAY_SIZE(wsa881x_rev_2_0)); + /* Enable software reset output from soundwire slave */ + regmap_update_bits(rm, WSA881X_SWR_RESET_EN, 0x07, 0x07); + /* Bring out of analog reset */ + regmap_update_bits(rm, WSA881X_CDC_RST_CTL, 0x02, 0x02); + /* Bring out of digital reset */ + regmap_update_bits(rm, WSA881X_CDC_RST_CTL, 0x01, 0x01); + regmap_update_bits(rm, WSA881X_CLOCK_CONFIG, 0x10, 0x10); + regmap_update_bits(rm, WSA881X_SPKR_OCP_CTL, 0x02, 0x02); + regmap_update_bits(rm, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80); + regmap_update_bits(rm, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06); + regmap_update_bits(rm, WSA881X_SPKR_BIAS_INT, 0xFF, 0x00); + regmap_update_bits(rm, WSA881X_SPKR_PA_INT, 0xF0, 0x40); + regmap_update_bits(rm, WSA881X_SPKR_PA_INT, 0x0E, 0x0E); + regmap_update_bits(rm, WSA881X_BOOST_LOOP_STABILITY, 0x03, 0x03); + regmap_update_bits(rm, WSA881X_BOOST_MISC2_CTL, 0xFF, 0x14); + regmap_update_bits(rm, WSA881X_BOOST_START_CTL, 0x80, 0x80); + regmap_update_bits(rm, WSA881X_BOOST_START_CTL, 0x03, 0x00); + regmap_update_bits(rm, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x0C, 0x04); + regmap_update_bits(rm, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x03, 0x00); + + regmap_read(rm, WSA881X_OTP_REG_0, &val); + if (val) + regmap_update_bits(rm, WSA881X_BOOST_PRESET_OUT1, 0xF0, 0x70); + + regmap_update_bits(rm, WSA881X_BOOST_PRESET_OUT2, 0xF0, 0x30); + regmap_update_bits(rm, WSA881X_SPKR_DRV_EN, 0x08, 0x08); + regmap_update_bits(rm, WSA881X_BOOST_CURRENT_LIMIT, 0x0F, 0x08); + regmap_update_bits(rm, WSA881X_SPKR_OCP_CTL, 0x30, 0x30); + regmap_update_bits(rm, WSA881X_SPKR_OCP_CTL, 0x0C, 0x00); + regmap_update_bits(rm, WSA881X_OTP_REG_28, 0x3F, 0x3A); + regmap_update_bits(rm, WSA881X_BONGO_RESRV_REG1, 0xFF, 0xB2); + regmap_update_bits(rm, WSA881X_BONGO_RESRV_REG2, 0xFF, 0x05); +} + +static int wsa881x_component_probe(struct snd_soc_component *comp) +{ + struct wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp); + + snd_soc_component_init_regmap(comp, wsa881x->regmap); + + return 0; +} + +static int wsa881x_put_pa_gain(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kc); + struct wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kc->private_value; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + + /* + * program actual register just before compander enable and ensure hw + * sequence is followed + */ + wsa881x->pa_gain = (max - ucontrol->value.integer.value[0]) & mask; + + return 0; +} + +static int wsa881x_get_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa881x_priv *data = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + ucontrol->value.integer.value[0] = data->port_enable[portidx]; + + + return 0; +} + +static int wsa881x_boost_ctrl(struct snd_soc_component *comp, bool enable) +{ + if (enable) + snd_soc_component_update_bits(comp, WSA881X_BOOST_EN_CTL, + WSA881X_BOOST_EN_MASK, + WSA881X_BOOST_EN); + else + snd_soc_component_update_bits(comp, WSA881X_BOOST_EN_CTL, + WSA881X_BOOST_EN_MASK, 0); + /* + * 1.5ms sleep is needed after boost enable/disable as per + * HW requirement + */ + usleep_range(1500, 1510); + return 0; +} + +static int wsa881x_set_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa881x_priv *data = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + if (ucontrol->value.integer.value[0]) + data->port_enable[portidx] = true; + else + data->port_enable[portidx] = false; + + if (portidx == WSA881X_PORT_BOOST) /* Boost Switch */ + wsa881x_boost_ctrl(comp, data->port_enable[portidx]); + + return 0; +} + +static const char * const smart_boost_lvl_text[] = { + "6.625 V", "6.750 V", "6.875 V", "7.000 V", + "7.125 V", "7.250 V", "7.375 V", "7.500 V", + "7.625 V", "7.750 V", "7.875 V", "8.000 V", + "8.125 V", "8.250 V", "8.375 V", "8.500 V" +}; + +static const struct soc_enum smart_boost_lvl_enum = + SOC_ENUM_SINGLE(WSA881X_BOOST_PRESET_OUT1, 0, + ARRAY_SIZE(smart_boost_lvl_text), + smart_boost_lvl_text); + +static const DECLARE_TLV_DB_SCALE(pa_gain, 0, 150, 0); + +static const struct snd_kcontrol_new wsa881x_snd_controls[] = { + SOC_ENUM("Smart Boost Level", smart_boost_lvl_enum), + WSA881X_PA_GAIN_TLV("PA Volume", WSA881X_SPKR_DRV_GAIN, + 4, 0xC, 1, pa_gain), + SOC_SINGLE("PA Mute Switch", WSA881X_SPKR_DRV_EN, 7, 1, 1), + SOC_SINGLE_EXT("DAC Switch", WSA881X_PORT_DAC, 0, 1, 0, + wsa881x_get_port, wsa881x_set_port), + SOC_SINGLE_EXT("COMP Switch", WSA881X_PORT_COMP, 0, 1, 0, + wsa881x_get_port, wsa881x_set_port), + SOC_SINGLE_EXT("BOOST Switch", WSA881X_PORT_BOOST, 0, 1, 0, + wsa881x_get_port, wsa881x_set_port), + SOC_SINGLE_EXT("VISENSE Switch", WSA881X_PORT_VISENSE, 0, 1, 0, + wsa881x_get_port, wsa881x_set_port), +}; + +static const struct snd_soc_dapm_route wsa881x_audio_map[] = { + {"RDAC", NULL, "IN"}, + {"RDAC", NULL, "DCLK"}, + {"RDAC", NULL, "ACLK"}, + {"RDAC", NULL, "Bandgap"}, + {"SPKR PGA", NULL, "RDAC"}, + {"SPKR", NULL, "SPKR PGA"}, +}; + +static int wsa881x_ramp_pa_gain(struct snd_soc_component *comp, + int min_gain, int max_gain) +{ + int val; + + for (val = min_gain; max_gain <= val; val--) { + snd_soc_component_update_bits(comp, WSA881X_SPKR_DRV_GAIN, + WSA881X_SPKR_PAG_GAIN_MASK, + val << 4); + /* + * 1ms delay is needed for every step change in gain as per + * HW requirement. + */ + usleep_range(1000, 1010); + } + return 0; +} + +static int wsa881x_visense_txfe_ctrl(struct snd_soc_component *comp, + bool enable) +{ + struct wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp); + + if (enable) { + regmap_multi_reg_write(wsa881x->regmap, wsa881x_vi_txfe_en_2_0, + ARRAY_SIZE(wsa881x_vi_txfe_en_2_0)); + } else { + snd_soc_component_update_bits(comp, + WSA881X_SPKR_PROT_FE_VSENSE_VCM, + 0x08, 0x08); + /* + * 200us sleep is needed after visense txfe disable as per + * HW requirement. + */ + usleep_range(200, 210); + snd_soc_component_update_bits(comp, WSA881X_SPKR_PROT_FE_GAIN, + 0x01, 0x00); + } + return 0; +} + +static int wsa881x_visense_adc_ctrl(struct snd_soc_component *comp, + bool enable) +{ + snd_soc_component_update_bits(comp, WSA881X_ADC_EN_MODU_V, BIT(7), + (enable << 7)); + snd_soc_component_update_bits(comp, WSA881X_ADC_EN_MODU_I, BIT(7), + (enable << 7)); + return 0; +} + +static int wsa881x_spkr_pa_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 wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp); + int min_gain, max_gain; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(comp, WSA881X_SPKR_OCP_CTL, + WSA881X_SPKR_OCP_MASK, + WSA881X_SPKR_OCP_EN); + regmap_multi_reg_write(wsa881x->regmap, wsa881x_pre_pmu_pa_2_0, + ARRAY_SIZE(wsa881x_pre_pmu_pa_2_0)); + + /* Set register mode if compander is not enabled */ + if (!wsa881x->port_prepared[WSA881X_PORT_COMP]) + snd_soc_component_update_bits(comp, + WSA881X_SPKR_DRV_GAIN, + WSA881X_PA_GAIN_SEL_MASK, + WSA881X_PA_GAIN_SEL_REG); + else + snd_soc_component_update_bits(comp, + WSA881X_SPKR_DRV_GAIN, + WSA881X_PA_GAIN_SEL_MASK, + WSA881X_PA_GAIN_SEL_DRE); + break; + case SND_SOC_DAPM_POST_PMU: + if (!wsa881x->port_prepared[WSA881X_PORT_COMP]) { + max_gain = wsa881x->pa_gain; + /* + * Gain has to set incrementally in 4 steps + * as per HW sequence + */ + if (max_gain > G_4P5DB) + min_gain = G_0DB; + else + min_gain = max_gain + 3; + /* + * 1ms delay is needed before change in gain + * as per HW requirement. + */ + usleep_range(1000, 1010); + wsa881x_ramp_pa_gain(comp, min_gain, max_gain); + } + + if (wsa881x->port_prepared[WSA881X_PORT_VISENSE]) { + wsa881x_visense_txfe_ctrl(comp, true); + snd_soc_component_update_bits(comp, + WSA881X_ADC_EN_SEL_IBAIS, + 0x07, 0x01); + wsa881x_visense_adc_ctrl(comp, true); + } + + break; + case SND_SOC_DAPM_POST_PMD: + if (wsa881x->port_prepared[WSA881X_PORT_VISENSE]) { + wsa881x_visense_adc_ctrl(comp, false); + wsa881x_visense_txfe_ctrl(comp, false); + } + + snd_soc_component_update_bits(comp, WSA881X_SPKR_OCP_CTL, + WSA881X_SPKR_OCP_MASK, + WSA881X_SPKR_OCP_EN | + WSA881X_SPKR_OCP_HOLD); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_DAC_E("RDAC", NULL, WSA881X_SPKR_DAC_CTL, 7, 0, + NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("SPKR PGA", WSA881X_SPKR_DRV_EN, 7, 0, NULL, 0, + wsa881x_spkr_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DCLK", WSA881X_CDC_DIG_CLK_CTL, 0, 0, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("ACLK", WSA881X_CDC_ANA_CLK_CTL, 0, 0, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("Bandgap", WSA881X_TEMP_OP, 3, 0, + NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("SPKR"), +}; + +static int wsa881x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); + int i; + + wsa881x->active_ports = 0; + for (i = 0; i < WSA881X_MAX_SWR_PORTS; i++) { + if (!wsa881x->port_enable[i]) + continue; + + wsa881x->port_config[wsa881x->active_ports] = + wsa881x_pconfig[i]; + wsa881x->active_ports++; + } + + return sdw_stream_add_slave(wsa881x->slave, &wsa881x->sconfig, + wsa881x->port_config, wsa881x->active_ports, + wsa881x->sruntime); +} + +static int wsa881x_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); + + sdw_stream_remove_slave(wsa881x->slave, wsa881x->sruntime); + + return 0; +} + +static int wsa881x_set_sdw_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); + + wsa881x->sruntime = stream; + + return 0; +} + +static struct snd_soc_dai_ops wsa881x_dai_ops = { + .hw_params = wsa881x_hw_params, + .hw_free = wsa881x_hw_free, + .set_sdw_stream = wsa881x_set_sdw_stream, +}; + +static struct snd_soc_dai_driver wsa881x_dais[] = { + [0] = { + .name = "SPKR", + .id = 0, + .playback = { + .stream_name = "SPKR Playback", + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &wsa881x_dai_ops, + }, +}; + +static const struct snd_soc_component_driver wsa881x_component_drv = { + .name = "WSA881x", + .probe = wsa881x_component_probe, + .controls = wsa881x_snd_controls, + .num_controls = ARRAY_SIZE(wsa881x_snd_controls), + .dapm_widgets = wsa881x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets), + .dapm_routes = wsa881x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map), +}; + +static int wsa881x_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_ATTACHED && slave->dev_num > 0) + wsa881x_init(wsa881x); + + return 0; +} + +static int wsa881x_port_prep(struct sdw_slave *slave, + struct sdw_prepare_ch *prepare_ch, + enum sdw_port_prep_ops state) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(&slave->dev); + + if (state == SDW_OPS_PORT_POST_PREP) + wsa881x->port_prepared[prepare_ch->num - 1] = true; + else + wsa881x->port_prepared[prepare_ch->num - 1] = false; + + return 0; +} + +static int wsa881x_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank), + 0x01); + + return 0; +} + +static struct sdw_slave_ops wsa881x_slave_ops = { + .update_status = wsa881x_update_status, + .bus_config = wsa881x_bus_config, + .port_prep = wsa881x_port_prep, +}; + +static int wsa881x_probe(struct sdw_slave *pdev, + const struct sdw_device_id *id) +{ + struct wsa881x_priv *wsa881x; + + wsa881x = devm_kzalloc(&pdev->dev, sizeof(*wsa881x), GFP_KERNEL); + if (!wsa881x) + return -ENOMEM; + + wsa881x->sd_n = devm_gpiod_get_optional(&pdev->dev, "powerdown", + GPIOD_FLAGS_BIT_NONEXCLUSIVE); + if (IS_ERR(wsa881x->sd_n)) { + dev_err(&pdev->dev, "Shutdown Control GPIO not found\n"); + return PTR_ERR(wsa881x->sd_n); + } + + dev_set_drvdata(&pdev->dev, wsa881x); + wsa881x->slave = pdev; + wsa881x->dev = &pdev->dev; + wsa881x->sconfig.ch_count = 1; + wsa881x->sconfig.bps = 1; + wsa881x->sconfig.frame_rate = 48000; + wsa881x->sconfig.direction = SDW_DATA_DIR_RX; + wsa881x->sconfig.type = SDW_STREAM_PDM; + pdev->prop.sink_ports = GENMASK(WSA881X_MAX_SWR_PORTS, 0); + pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; + gpiod_set_value(wsa881x->sd_n, 1); + + wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config); + if (IS_ERR(wsa881x->regmap)) { + dev_err(&pdev->dev, "regmap_init failed\n"); + return PTR_ERR(wsa881x->regmap); + } + + return devm_snd_soc_register_component(&pdev->dev, + &wsa881x_component_drv, + wsa881x_dais, + ARRAY_SIZE(wsa881x_dais)); +} + +static const struct sdw_device_id wsa881x_slave_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x2010, 0), + SDW_SLAVE_ENTRY(0x0217, 0x2110, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, wsa881x_slave_id); + +static struct sdw_driver wsa881x_codec_driver = { + .probe = wsa881x_probe, + .ops = &wsa881x_slave_ops, + .id_table = wsa881x_slave_id, + .driver = { + .name = "wsa881x-codec", + } +}; +module_sdw_driver(wsa881x_codec_driver); + +MODULE_DESCRIPTION("WSA881x codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c index 94363fd6846a..2bc43618dcc2 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 = rtd->codec_dai; + 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,8 +156,15 @@ static int apq8096_platform_probe(struct platform_device *pdev) if (!card) return -ENOMEM; + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + kfree(card); + 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) { dev_err(dev, "Error parsing OF data\n"); @@ -132,16 +182,19 @@ err_card_register: kfree(card->dai_link); err: kfree(card); + kfree(data); return ret; } static int apq8096_platform_remove(struct platform_device *pdev) { struct snd_soc_card *card = dev_get_drvdata(&pdev->dev); + struct apq8096_card_data *data = snd_soc_card_get_drvdata(card); snd_soc_unregister_card(card); kfree(card->dai_link); kfree(card); + kfree(data); return 0; } diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 28f3cef696e6..1ed570c19628 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -24,6 +24,9 @@ #define RIGHT_SPK_TDM_TX_MASK 0xC0 #define SPK_TDM_RX_MASK 0x03 #define NUM_TDM_SLOTS 8 +#define SLIM_MAX_TX_PORTS 16 +#define SLIM_MAX_RX_PORTS 16 +#define WCD934X_DEFAULT_MCLK_RATE 9600000 struct sdm845_snd_data { struct snd_soc_jack jack; @@ -36,6 +39,39 @@ struct sdm845_snd_data { static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; +static int sdm845_slim_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_link *dai_link = rtd->dai_link; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret = 0, i; + + for (i = 0 ; i < dai_link->num_codecs; i++) { + ret = snd_soc_dai_get_channel_map(rtd->codec_dais[i], + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + + if (ret != 0 && ret != -ENOTSUPP) { + pr_err("failed to get codec chan map, err:%d\n", ret); + return ret; + } else if (ret == -ENOTSUPP) { + /* Ignore unsupported */ + continue; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + rx_ch_cnt, rx_ch); + else + ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt, + tx_ch, 0, NULL); + } + + return 0; +} + static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -151,6 +187,9 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream, case QUATERNARY_TDM_TX_0: ret = sdm845_tdm_snd_hw_params(substream, params); break; + case SLIMBUS_0_RX...SLIMBUS_6_TX: + ret = sdm845_slim_snd_hw_params(substream, params); + break; default: pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); break; @@ -173,7 +212,20 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card); struct snd_jack *jack; - int rval; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + /* + * Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[SLIM_MAX_RX_PORTS] = {144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156}; + unsigned int tx_ch[SLIM_MAX_TX_PORTS] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + int rval, i; + if (!pdata->jack_setup) { rval = snd_soc_card_jack_new(card, "Headset Jack", @@ -211,6 +263,21 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) return rval; } break; + case SLIMBUS_0_RX...SLIMBUS_6_TX: + for (i = 0 ; i < dai_link->num_codecs; i++) { + rval = snd_soc_dai_set_channel_map(rtd->codec_dais[i], + ARRAY_SIZE(tx_ch), + tx_ch, + ARRAY_SIZE(rx_ch), + rx_ch); + if (rval != 0 && rval != -ENOTSUPP) + return rval; + + snd_soc_dai_set_sysclk(rtd->codec_dais[i], 0, + WCD934X_DEFAULT_MCLK_RATE, + SNDRV_PCM_STREAM_PLAYBACK); + } + break; default: break; } @@ -451,6 +518,8 @@ static int sdm845_snd_platform_remove(struct platform_device *pdev) static const struct of_device_id sdm845_snd_device_id[] = { { .compatible = "qcom,sdm845-sndcard" }, + { .compatible = "qcom,db845c-sndcard" }, + { .compatible = "lenovo,yoga-c630-sndcard" }, {}, }; MODULE_DEVICE_TABLE(of, sdm845_snd_device_id); |