diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,wcd-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,wcd-pinctrl.txt new file mode 100644 index 0000000000000000000000000000000000000000..add8b7d688a84d840f52a4177a6288c42cb76af0 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,wcd-pinctrl.txt @@ -0,0 +1,138 @@ +Qualcomm Technologies, Inc. WCD GPIO block + +This binding describes the GPIO block found in the WCD934X series of +audio codec's from QTI. + +- compatible: + Usage: required + Value type: + Definition: must be "qcom,wcd-pinctrl" + +- qcom,num-gpios: + Usage: required + Value type: + Definition: Number of GPIO's supported by the controller + +- gpio-controller: + Usage: required + Value type: + Definition: Mark the device node as a GPIO controller + +- #gpio-cells: + Usage: required + Value type: + Definition: Must be 2; + the first cell will be used to define gpio number and the + second denotes the flags for this gpio + +Please refer to ../gpio/gpio.txt for a general description of GPIO bindings. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +The pin configuration nodes act as a container for an arbitrary number of +subnodes. Each of these subnodes represents some desired configuration for a +pin or a list of pins. This configuration can include the +mux function to select on those pin(s), and various pin configuration +parameters, as listed below. + + +SUBNODES: + +The name of each subnode is not important; all subnodes should be enumerated +and processed purely based on their content. + +Each subnode only affects those parameters that are explicitly listed. In +other words, a subnode that lists a mux function but no pin configuration +parameters implies no information about any pin configuration parameters. +Similarly, a pin subnode that describes a pullup parameter implies no +information about e.g. the mux function. + +The following generic properties as defined in pinctrl-bindings.txt are valid +to specify in a pin configuration subnode: + +- pins: + Usage: required + Value type: + Definition: List of gpio pins affected by the properties specified in + this subnode. Valid pins are: + gpio1-gpio5 for wcd9340 + +- bias-disable: + Usage: optional + Value type: + Definition: The specified pins should be configured as no pull. + +- bias-pull-down: + Usage: optional + Value type: + Definition: The specified pins should be configured as pull down. + +- bias-pull-up: + Usage: optional + Value type: + Definition: The specified pins should be configured as pull up. + +- qcom,pull-up-strength: + Usage: optional + Value type: + Definition: Specifies the strength to use for pull up, if selected. + +- bias-high-impedance: + Usage: optional + Value type: + Definition: The specified pins will put in high-Z mode and disabled. + +- input-enable: + Usage: optional + Value type: + Definition: The specified pins are put in input mode. + +- output-high: + Usage: optional + Value type: + Definition: The specified pins are configured in output mode, driven + high. + +- output-low: + Usage: optional + Value type: + Definition: The specified pins are configured in output mode, driven + low. + +- qcom,drive-strength: + Usage: optional + Value type: + Definition: Selects the drive strength for the specified pins. + +Example: + + wcd: wcd_pinctrl@5 { + compatible = "qcom,wcd-pinctl"; + qcom,num-gpios = <5> + gpio-controller; + #gpio-cells = <2>; + + spkr_1_wcd_en_active: spkr_1_wcd_en_active { + mux { + pins = "gpio2"; + }; + + config { + pins = "gpio2"; + output-high; + }; + }; + + spkr_1_wcd_en_sleep: spkr_1_wcd_en_sleep { + mux { + pins = "gpio2"; + }; + + config { + pins = "gpio2"; + input-enable; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index c33b79043a1f9f7fb9fdb5816c8bfd97f22ce40b..b81759b7378369bea0c5b8c7adef5592e92a31be 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -404,6 +404,12 @@ Required properties: - compatible : "qcom,wcd-dsp-glink" +* msm_ext_disp_audio_codec_rx + +Required properties: + + - compatible : "qcom,msm-ext-disp-audio-codec-rx" + Example: qcom,msm-pcm { @@ -680,6 +686,10 @@ Example: compatible = "qcom,wcd-dsp-glink"; }; + msm_ext_disp_audio_codec_rx { + compatible = "qcom,msm-ext-disp-audio-codec-rx"; + }; + * MSM8916 ASoC Machine driver @@ -2343,3 +2353,35 @@ Example: asoc-codec-names = "msm-stub-codec.1"; qcom,wsa-max-devs = <0>; }; + +* WCD DSP manager driver + +Required properties: +- compatible : "qcom,wcd-dsp-mgr" +- qcom,wdsp-components : This is phandle list containing the references to the + components of the manager driver. Manager driver will + register to component framework with these phandles. +- qcom,img-filename : String property to provide the dsp image file name that is + to be read from file system and downloaded to dsp memory +Optional properties: +- qcom,wdsp-cmpnt-dev-name : Property that manager driver will parse, but defined + in the child's DT entry that is given to manager driver + with phandle. This property will be used by the manager + driver in case the manager driver cannot match child's + of_node pointer to registered phandle. + +Example: + + qcom,wcd-dsp-mgr { + compatible = "qcom,wcd-dsp-mgr"; + qcom,wdsp-components = <&wcd934x_cdc 0>, + <&wcd_spi_0 1>, + <&glink_spi 2>; + qcom,img-filename = "cpe_9340"; + }; + +Example of child node that would have qcom,wdsp-cmpnt-dev-name property + + wcd934x_cdc: tavil_codec { + qcom,wdsp-cmpnt-dev-name = "tavil_codec"; + }; diff --git a/Documentation/devicetree/bindings/sound/wcd-spi.txt b/Documentation/devicetree/bindings/sound/wcd-spi.txt new file mode 100644 index 0000000000000000000000000000000000000000..2cd833d1851e0af77389b2964ba89cdc21c50e82 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wcd-spi.txt @@ -0,0 +1,37 @@ +WCD audio codec SPI driver support + +* wcd_spi + +The wcd_spi device can be added as child device node to the audio codec device +node if the audio codec has SPI slave hardware. The codec driver upon probe will +create spi_device and perform spi_add_device. Note that the SPI framework does +not parse this DT node and hence the DT properties defined by SPI framework +cannot be used in wcd_spi device node. + +Required properties: + +- compatible : "qcom,wcd-spi-v2" + +- qcom,master-bus-num : Bus number of the SPI master controller driver. This + will be used to get a reference to the SPI master. + +- qcom,chip-select : Specifies the chip select number used for spi device + registration. + +- qcom,max-frequency : Specifies the max frequency of the SPI interface. + +- qcom,mem-base-addr : Defines the memory base address from the SPI + memory map. This will be used as an offset to read + and write memory. + +Example: + +tavil_codec { + wcd_spi_0: wcd_spi { + compatible = "qcom,wcd-spi-v2"; + qcom,master-bus-num = <10>; + qcom,chip-select = <0>; + qcom,max-frequency = <24000000>; + qcom,mem-base-addr = <0x100000>; + }; +}; diff --git a/Documentation/devicetree/bindings/sound/wcd_codec.txt b/Documentation/devicetree/bindings/sound/wcd_codec.txt new file mode 100644 index 0000000000000000000000000000000000000000..c0a7a240b922bc622daf7f650c2cc21d1ee3b614 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wcd_codec.txt @@ -0,0 +1,640 @@ +WCD audio CODEC + +Required properties: + + - compatible : "qcom,tasha-slim-pgd" or "qcom,tasha-i2c-pgd" for Tasha Codec + or "qcom,tavil-slim-pgd" for Tavil Codec + - elemental-addr: codec slimbus slave PGD enumeration address.(48 bits) + + - qcom,cdc-reset-gpio: gpio used for codec SOC reset. + If this property is not defined, it is expected + to atleast have "qcom,wcd-rst-n-gpio" to be defined. + - qcom,wcd-rst-gpio-node: Phandle reference to the DT node having codec reset gpio + configuration. If this property is not defined, it is + expected to atleast define "qcom,cdc-reset-gpio" property. + + - cdc-vdd-buck-supply: phandle of buck supply's regulator device tree node. + - qcom,cdc-vdd-buck-voltage: buck supply's voltage level min and max in mV. + - qcom,cdc-vdd-buck-current: buck supply's max current in mA. + + - cdc-vdd-tx-h-supply: phandle of tx-h supply's regulator device tree node. + - qcom,cdc-vdd-tx-h-voltage: tx-h supply's voltage level min and max in mV. + - qcom,cdc-vdd-tx-h-current: tx-h supply's max current in mA. + + - cdc-vdd-rx-h-supply: phandle of rx-h supply's regulator device tree node. + - qcom,cdc-vdd-rx-h-voltage: rx-h supply's voltage level min and max in mV. + - qcom,cdc-vdd-rx-h-current: rx-h supply's max current in mA. + + - cdc-vddpx-1-supply: phandle of px-1 supply's regulator device tree node. + - qcom,cdc-vddpx-1-voltage: px-1 supply's voltage level min and max in mV. + - qcom,cdc-vddpx-1-current: px-1 supply's max current in mA. + + - cdc-vdd-a-1p2v-supply: phandle of 1.2v supply's regulator device tree node. + - qcom,cdc-vdd-a-1p2v-voltage: 1.2v supply's voltage level min and max in mV. + - qcom,cdc-vdd-a-1p2v-current: 1.2v supply's max current in mA. + + - cdc-vddcx-1-supply: phandle of cx-1 supply's regulator device tree node. + - qcom,cdc-vddcx-1-voltage: cx-1 supply's voltage level min and max in mV. + - qcom,cdc-vddcx-1-current: cx-1 supply's max current in mA. + + - cdc-vddcx-2-supply: phandle of cx-2 supply's regulator device tree node. + - qcom,cdc-vddcx-2-voltage: cx-2 supply's voltage level min and max in mV. + - qcom,cdc-vddcx-2-current: cx-2 supply's max current in mA. + + - cdc-vdd-buckhelper-supply: phandle of helper regulator supply's + device tree node. This supply is a helper regulator for + cdc-vdd-buck-supply regulator. + - cdc-vdd-buckhelper-voltage: helper supply's voltage level min and max in mV. + - qcom,cdc-vdd-buckhelper-current: helper supply's max current in mA. + + - qcom,cdc-static-supplies: List of supplies to be enabled prior to codec + hardware probe. Supplies in this list will be + stay enabled. + + - qcom,cdc-micbias-ldoh-v - LDOH output in volts (should be 1.95 V and 3.00 V). + + - qcom,cdc-micbias-cfilt1-mv - cfilt1 output voltage in milli volts. + - qcom,cdc-micbias-cfilt2-mv - cfilt2 output voltage in milli volts. + - qcom,cdc-micbias-cfilt3-mv - cfilt3 output voltage in milli volts. + cfilt voltage can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V. + + - qcom,cdc-micbias1-cfilt-sel = cfilt to use for micbias1 + (should be from 1 to 3). + - qcom,cdc-micbias2-cfilt-sel = cfilt to use for micbias2 + (should be from 1 to 3). + - qcom,cdc-micbias3-cfilt-sel = cfilt to use for micbias3 + (should be from 1 to 3). + - qcom,cdc-micbias4-cfilt-sel = cfilt to use for micbias4 + (should be from 1 to 3). + This value represents the connected CFLIT to MIC Bias. + + - qcom,cdc-micbias1-ext-cap: Boolean. Enable micbias 1 external capacitor mode. + - qcom,cdc-micbias2-ext-cap: Boolean. Enable micbias 2 external capacitor mode. + - qcom,cdc-micbias3-ext-cap: Boolean. Enable micbias 3 external capacitor mode. + - qcom,cdc-micbias4-ext-cap: Boolean. Enable micbias 4 external capacitor mode. + - qcom,cdc-mclk-clk-rate - Specifies the master clock rate in Hz required for + codec. + - qcom,cdc-slim-ifd-dev - namme of the codec slim interface device. + - qcom,cdc-slim-ifd-elemental-addr - codec slimbus slave interface device + enumeration address. + +Optional properties: + - cdc-dmic-sample-rate: Specifies the sample rate of digital mic in HZ. The + values for 9.6MHZ mclk can be 2400000 Hz, 3200000 Hz + and 4800000 Hz. The values for 12.288MHz mclk can be + 3072200 Hz, 4096000 Hz and 6144000 Hz. + + - qcom,cdc-mad-dmic-rate: Specifies the sample rate of digital mic in HZ to be + used by MAD (Microphone Activity Detection) hardware + block on the codec. The valid set of values are same + as that of cdc-dmic-sample-rate, but this rate will + only be used by MAD and all other audio use cases + involving DMIC will use the rate defined by + cdc-dmic-sample-rate. + + - qcom,cdc-ecpp-dmic-rate: Specifies the sample rate of digital mic in HZ to be + used by ECPP (Echo Cancellation Ping Pong) block + on the codec. The valid set of values are same + as that of cdc-dmic-sample-rate, but this rate will + only be used by ECPP and all other audio use cases + involving DMIC will use the rate defined by + cdc-dmic-sample-rate. + + - qcom,cdc-dmic-clk-drv-strength: Specifies the drive strength for digital microphone + clock in the codec. Accepted values are 2,4,8 and 16. + The clock drive strentgh is in uA. Codec driver will + choose default value for particular codec if this + value is not specified in device tree. + + - qcom,cdc-on-demand-supplies: List of supplies which can be enabled + dynamically. + Supplies in this list are off by default. + +- qcom,cdc-cp-supplies: List of supplies required for codec chargepump enable + Supplies in this list can be enabled/disabled dynamically and + are off by default. + + - qcom,cdc-micbias2-headset-only: Boolean. Allow micbias 2 only to headset mic. + + - qcom,cdc-variant: Indicates codec variant, WCD9XXX indicates all codecs till Taiko + WCD9330 indicates wcd9330 audio codec + + - qcom,cdc-micbias1-mv: micbias1 output voltage in milli volts. + This is used when cfilt is not user configurable + and micbias1 is directly controlled with a register + write. + + - qcom,cdc-micbias2-mv: micbias2 output voltage in milli volts. + This is used when cfilt is not user configurable + and micbias2 is directly controlled with a register + write. + + - qcom,cdc-micbias3-mv: micbias3 output voltage in milli volts. + This is used when cfilt is not user configurable + and micbias3 is directly controlled with a register + write. + + - qcom,cdc-micbias4-mv: micbias4 output voltage in milli volts. + This is used when cfilt is not user configurable + and micbias4 is directly controlled with a register + write. + + - clock-names : clock name defined for external clock. + - clocks : external clock defined for codec clock. + +Example: + +taiko_codec { + compatible = "qcom,taiko-slim-pgd"; + elemental-addr = [00 01 A0 00 17 02]; + + qcom,cdc-reset-gpio = <&msmgpio 63 0>; + + cdc-vdd-buck-supply = <&pm8941_s2>; + qcom,cdc-vdd-buck-voltage = <2150000 2150000>; + qcom,cdc-vdd-buck-current = <500000>; + + cdc-vdd-tx-h-supply = <&pm8941_s3>; + qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>; + qcom,cdc-vdd-tx-h-current = <200000>; + + cdc-vdd-rx-h-supply = <&pm8941_s3>; + qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>; + qcom,cdc-vdd-rx-h-current = <200000>; + + cdc-vddpx-1-supply = <&pm8941_s3>; + qcom,cdc-vddpx-1-voltage = <1800000 1800000>; + qcom,cdc-vddpx-1-current = <5000>; + + cdc-vdd-a-1p2v-supply = <&pm8941_l1>; + qcom,cdc-vdd-a-1p2v-voltage = <1225000 1225000>; + qcom,cdc-vdd-a-1p2v-current = <5000>; + + cdc-vddcx-1-supply = <&pm8941_l1>; + qcom,cdc-vddcx-1-voltage = <1225000 1225000>; + qcom,cdc-vddcx-1-current = <5000>; + + cdc-vddcx-2-supply = <&pm8941_l1>; + qcom,cdc-vddcx-2-voltage = <1225000 1225000>; + qcom,cdc-vddcx-2-current = <5000>; + + qcom,cdc-static-supplies = "cdc-vdd-buck", + "cdc-vdd-tx-h", + "cdc-vdd-rx-h", + "cdc-vddpx-1", + "cdc-vdd-a-1p2v", + "cdc-vddcx-1", + "cdc-vddcx-2"; + + com,cdc-on-demand-supplies = "cdc-vdd-spkdrv"; + + qcom,cdc-micbias-ldoh-v = <0x3>; + qcom,cdc-micbias-cfilt1-mv = <1800>; + qcom,cdc-micbias-cfilt2-mv = <2700>; + qcom,cdc-micbias-cfilt3-mv = <1800>; + qcom,cdc-micbias1-cfilt-sel = <0x0>; + qcom,cdc-micbias2-cfilt-sel = <0x1>; + qcom,cdc-micbias3-cfilt-sel = <0x2>; + qcom,cdc-micbias4-cfilt-sel = <0x2>; + qcom,cdc-micbias1-ext-cap; + qcom,cdc-micbias2-ext-cap; + qcom,cdc-micbias3-ext-cap; + qcom,cdc-micbias4-ext-cap; + qcom,cdc-mclk-clk-rate = <9600000>; + qcom,cdc-slim-ifd = "taiko-slim-ifd"; + qcom,cdc-slim-ifd-elemental-addr = [00 00 A0 00 17 02]; + qcom,cdc-dmic-sample-rate = <4800000>; + qcom,cdc-micbias2-headset-only; +}; + +Wcd9xxx audio CODEC in I2C mode + + - compatible = "qcom,wcd9xxx-i2c-device"; + - reg: represents the slave address provided to the I2C driver. + - qcom,cdc-reset-gpio: gpio used for codec SOC reset. + + - cdc-vdd-buck-supply: phandle of buck supply's regulator device tree node. + - qcom,cdc-vdd-buck-voltage: buck supply's voltage level min and max in mV. + - qcom,cdc-vdd-buck-current: buck supply's max current in mA. + + - cdc-vdd-tx-h-supply: phandle of tx-h supply's regulator device tree node. + - qcom,cdc-vdd-tx-h-voltage: tx-h supply's voltage level min and max in mV. + - qcom,cdc-vdd-tx-h-current: tx-h supply's max current in mA. + + - cdc-vdd-rx-h-supply: phandle of rx-h supply's regulator device tree node. + - qcom,cdc-vdd-rx-h-voltage: rx-h supply's voltage level min and max in mV. + - qcom,cdc-vdd-rx-h-current: rx-h supply's max current in mA. + + - cdc-vddpx-1-supply: phandle of px-1 supply's regulator device tree node. + - qcom,cdc-vddpx-1-voltage: px-1 supply's voltage level min and max in mV. + - qcom,cdc-vddpx-1-current: px-1 supply's max current in mA. + + - cdc-vdd-a-1p2v-supply: phandle of 1.2v supply's regulator device tree node. + - qcom,cdc-vdd-a-1p2v-voltage: 1.2v supply's voltage level min and max in mV. + - qcom,cdc-vdd-a-1p2v-current: 1.2v supply's max current in mA. + + - cdc-vddcx-1-supply: phandle of cx-1 supply's regulator device tree node. + - qcom,cdc-vddcx-1-voltage: cx-1 supply's voltage level min and max in mV. + - qcom,cdc-vddcx-1-current: cx-1 supply's max current in mA. + + - cdc-vddcx-2-supply: phandle of cx-2 supply's regulator device tree node. + - qcom,cdc-vddcx-2-voltage: cx-2 supply's voltage level min and max in mV. + - qcom,cdc-vddcx-2-current: cx-2 supply's max current in mA. + + - qcom,cdc-static-supplies: List of supplies to be enabled prior to codec + hardware probe. Supplies in this list will be + stay enabled. + + - qcom,cdc-micbias-ldoh-v - LDOH output in volts (should be 1.95 V and 3.00 V). + + - qcom,cdc-micbias-cfilt1-mv - cfilt1 output voltage in milli volts. + - qcom,cdc-micbias-cfilt2-mv - cfilt2 output voltage in milli volts. + - qcom,cdc-micbias-cfilt3-mv - cfilt3 output voltage in milli volts. + cfilt voltage can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V. + + - qcom,cdc-micbias1-cfilt-sel = cfilt to use for micbias1 + (should be from 1 to 3). + - qcom,cdc-micbias2-cfilt-sel = cfilt to use for micbias2 + (should be from 1 to 3). + - qcom,cdc-micbias3-cfilt-sel = cfilt to use for micbias3 + (should be from 1 to 3). + - qcom,cdc-micbias4-cfilt-sel = cfilt to use for micbias4 + (should be from 1 to 3). + This value represents the connected CFLIT to MIC Bias. + + - qcom,cdc-micbias1-ext-cap: Boolean. Enable micbias 1 external capacitor mode. + - qcom,cdc-micbias2-ext-cap: Boolean. Enable micbias 2 external capacitor mode. + - qcom,cdc-micbias3-ext-cap: Boolean. Enable micbias 3 external capacitor mode. + - qcom,cdc-micbias4-ext-cap: Boolean. Enable micbias 4 external capacitor mode. + - qcom,cdc-mclk-clk-rate - Specifies the master clock rate in Hz required for + codec. + +Optional properties: + + - cdc-vdd-spkdrv-supply: phandle of spkdrv supply's regulator device tree node. + - qcom,cdc-vdd-spkdrv-voltage: spkdrv supply voltage level min and max in mV. + - qcom,cdc-vdd-spkdrv-current: spkdrv supply max current in mA. + + - cdc-vdd-spkdrv-supply: phandle of spkdrv supply's regulator device tree node. + - qcom,cdc-vdd-spkdrv-voltage: spkdrv supply voltage level min and max in mV. + - qcom,cdc-vdd-spkdrv-current: spkdrv supply max current in mA. + + - cdc-vdd-spkdrv-2-supply: phandle of spkdrv2 supply's regulator device tree node. + - qcom,cdc-vdd-spkdrv-2-voltage: spkdrv2 supply voltage level min and max in mV. + - qcom,cdc-vdd-spkdrv-2-current: spkdrv2 supply max current in mA. + + - qcom,cdc-on-demand-supplies: List of supplies which can be enabled + dynamically. + Supplies in this list are off by default. + +Example: +i2c@f9925000 { + cell-index = <3>; + compatible = "qcom,i2c-qup"; + reg = <0xf9925000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "qup_phys_addr"; + interrupts = <0 97 0>; + interrupt-names = "qup_err_intr"; + qcom,i2c-bus-freq = <100000>; + qcom,i2c-src-freq = <24000000>; + + wcd9xxx_codec@0d{ + compatible = "qcom,wcd9xxx-i2c"; + reg = <0x0d>; + qcom,cdc-reset-gpio = <&msmgpio 22 0>; + interrupt-parent = <&wcd9xxx_intc>; + interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28>; + + cdc-vdd-buck-supply = <&pm8019_l11>; + qcom,cdc-vdd-buck-voltage = <1800000 1800000>; + qcom,cdc-vdd-buck-current = <25000>; + + cdc-vdd-tx-h-supply = <&pm8019_l11>; + qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>; + qcom,cdc-vdd-tx-h-current = <25000>; + + cdc-vdd-rx-h-supply = <&pm8019_l11>; + qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>; + qcom,cdc-vdd-rx-h-current = <25000>; + + cdc-vddpx-1-supply = <&pm8019_l11>; + qcom,cdc-vddpx-1-voltage = <1800000 1800000>; + qcom,cdc-vddpx-1-current = <10000>; + + cdc-vdd-a-1p2v-supply = <&pm8019_l9>; + qcom,cdc-vdd-a-1p2v-voltage = <1200000 1200000>; + qcom,cdc-vdd-a-1p2v-current = <10000>; + + cdc-vddcx-1-supply = <&pm8019_l9>; + qcom,cdc-vddcx-1-voltage = <1200000 1200000>; + qcom,cdc-vddcx-1-current = <10000>; + + cdc-vddcx-2-supply = <&pm8019_l9>; + qcom,cdc-vddcx-2-voltage = <1200000 1200000>; + qcom,cdc-vddcx-2-current = <10000>; + + qcom,cdc-static-supplies = "cdc-vdd-buck", + "cdc-vdd-tx-h", + "cdc-vdd-rx-h", + "cdc-vddpx-1", + "cdc-vdd-a-1p2v", + "cdc-vddcx-1", + "cdc-vddcx-2"; + + cdc-vdd-spkdrv-supply = <&pmi8994_boost>; + qcom,cdc-vdd-spkdrv-voltage = <5000000 5000000>; + qcom,cdc-vdd-spkdrv-current = <600000>; + + cdc-vdd-spkdrv-2-supply = <&pmi8994_boost>; + qcom,cdc-vdd-spkdrv-2-voltage = <5000000 5000000>; + qcom,cdc-vdd-spkdrv-2-current = <600000>; + + qcom,cdc-on-demand-supplies = "cdc-vdd-spkdrv", + "cdc-vdd-spkdrv-2"; + + qcom,cdc-micbias-ldoh-v = <0x3>; + qcom,cdc-micbias-cfilt1-mv = <1800>; + qcom,cdc-micbias-cfilt2-mv = <2700>; + qcom,cdc-micbias-cfilt3-mv = <1800>; + qcom,cdc-micbias1-cfilt-sel = <0x0>; + qcom,cdc-micbias2-cfilt-sel = <0x1>; + qcom,cdc-micbias3-cfilt-sel = <0x2>; + qcom,cdc-micbias4-cfilt-sel = <0x2>; + qcom,cdc-mclk-clk-rate = <12288000>; + }; + + wcd9xxx_codec@77{ + compatible = "qcom,wcd9xxx-i2c"; + reg = <0x77>; + }; + + wcd9xxx_codec@66{ + compatible = "qcom,wcd9xxx-i2c"; + reg = <0x66>; + }; + wcd9xxx_codec@55{ + compatible = "qcom,wcd9xxx-i2c"; + reg = <0x55>; + }; +}; + +Tombak audio CODEC in SPMI mode + + - compatible = "qcom,msm-codec-core", + - compatible = "qcom,pmic-codec-digital" + - compatible = "qcom,pmic-codec-analog" + - reg: represents the slave base address provided to the peripheral. + - interrupt-parent : The parent interrupt controller. + - interrupts: List of interrupts in given SPMI peripheral. + - interrupt-names: Names specificed to above list of interrupts in same + order. + +Optional properties: + + - cdc-vdda-cp-supply: phandle of cp supply's regulator device tree node. + - qcom,cdc-vdda-cp-voltage: cp supply's voltage level min and max in mV. + - qcom,cdc-vdda-cp-current: cp supply's max current in mA. + + - cdc-vdda-rx-h-supply: phandle of vdda-rx-h supply's regulator device tree node. + - qcom,cdc-vdda-rx-h-voltage: vdda-rx-h supply's voltage level min and max in mV. + - qcom,cdc-vdda-rx-h-current: vdda-rx-h supply's max current in mA. + + - cdc-vdda-tx-h-supply: phandle of vdda-tx-h supply's regulator device tree node. + - qcom,cdc-vdda-tx-h-voltage: vdda-tx-h supply's voltage level min and max in mV. + - qcom,cdc-vdda-tx-h-current: vdda-tx-h supply's max current in mA. + + - cdc-vdd-px-supply: phandle of vdd-px supply's regulator device tree node. + - qcom,cdc-vdd-px-voltage: vdd-px supply's voltage level min and max in mV. + - qcom,cdc-vdd-px-current: vdd-px supply's max current in mA. + + - cdc-vdd-pa-supply: phandle of vdd-pa supply's regulator device tree node. + - qcom,cdc-vdd-pa-voltage: vdd-pa supply's voltage level min and max in mV. + - qcom,cdc-vdd-pa-current: vdd-pa supply's max current in mA. + + - cdc-vdd-mic-bias-supply: phandle of mic-bias supply's regulator device tree + node. + - qcom,cdc-vdd-mic-bias-voltage: mic-bias supply's voltage level min and max + in mV. + - qcom,cdc-vdd-mic-bias-current: mic-bias supply's max current in mA. + + - qcom,cdc-mclk-clk-rate: Specifies the master clock rate in Hz required for + codec. + + - qcom,cdc-static-supplies: List of supplies to be enabled prior to codec + hardware probe. Supplies in this list will be + stay enabled. + + - qcom,cdc-on-demand-supplies: List of supplies which can be enabled + dynamically. + Supplies in this list are off by default. + - qcom,cdc-micbias-cfilt-mv : MICBIAS voltage value + - qcom,cdc-boost-voltage: Specifies the analog boost output voltage value. + Value from 4000 to 5550 in mV in steps of 50 mV can be given. + - qcom,dig-cdc-base-addr: Specifies the digital codec base address for MSM digital + core register writes. + +Example: + +msm_dig_codec: qcom,msm-int-codec { + compatible = "qcom,msm_int_core_codec"; + qcom,dig-cdc-base-addr = <0xc0f0000>; +}; + +msm8x16_wcd_codec@f100 { + compatible = "qcom,msm_int_pmic_analog_codec"; + reg = <0xf100 0x100>; +}; + +msm8x16_wcd_codec@f000{ + compatible = "qcom,msm_int_pmic_digital_codec"; + reg = <0xf000 0x100>; + interrupt-parent = <&spmi_bus>; + interrupts = <0x1 0xf0 0x0>, + <0x1 0xf0 0x1>, + <0x1 0xf0 0x2>, + <0x1 0xf0 0x3>, + <0x1 0xf0 0x4>, + <0x1 0xf0 0x5>, + <0x1 0xf0 0x6>, + <0x1 0xf0 0x7>; + interrupt-names = "spk_cnp_int", + "spk_clip_int", + "spk_ocp_int", + "ins_rem_det1", + "but_rel_det", + "but_press_det", + "ins_rem_det", + "mbhc_int"; + + cdc-vdda-cp-supply = <&pm8916_s4>; + qcom,cdc-vdda-cp-voltage = <1800000 2200000>; + qcom,cdc-vdda-cp-current = <770000>; + + cdc-vdda-rx-h-supply = <&pm8916_l5>; + qcom,cdc-vdda-rx-h-voltage = <1800000 1800000>; + qcom,cdc-vdda-rx-h-current = <20000>; + + cdc-vdda-tx-h-supply = <&pm8916_l5>; + qcom,cdc-vdda-tx-h-voltage = <1800000 1800000>; + qcom,cdc-vdda-tx-h-current = <20000>; + + cdc-vdd-px-supply = <&pm8916_s4>; + qcom,cdc-vdd-px-voltage = <1800000 2200000>; + qcom,cdc-vdd-px-current = <770000>; + + cdc-vdd-pa-supply = <&pm8916_l5>; + qcom,cdc-vdd-pa-voltage = <1800000 1800000>; + qcom,cdc-vdd-pa-current = <5000>; + + cdc-vdd-mic-bias-supply = <&pm8916_l13>; + qcom,cdc-vdd-mic-bias-voltage = <3075000 3075000>; + qcom,cdc-vdd-mic-bias-current = <25000>; + + qcom,cdc-mclk-clk-rate = <9600000>; + + qcom,cdc-static-supplies = "cdc-vdda-h", + "cdc-vdd-px", + "cdc-vdd-pa", + "cdc-vdda-cp"; + + qcom,cdc-on-demand-supplies = "cdc-vdd-mic-bias"; + qcom,dig-cdc-base-addr = <0xc0f0000>; +}; + +Tasha audio CODEC in I2C mode + + - compatible = "qcom,tasha-i2c-pgd"; + - reg: represents the slave address provided to the I2C driver. + - qcom,cdc-reset-gpio: gpio used for codec SOC reset. + + - cdc-vdd-buck-supply: phandle of buck supply's regulator device tree node. + - qcom,cdc-vdd-buck-voltage: buck supply's voltage level min and max in mV. + - qcom,cdc-vdd-buck-current: buck supply's max current in mA. + + - cdc-buck-sido-supply: phandle of sido supply's regulator device tree node. + - qcom,cdc-buck-sido-voltage = sido supply's voltage level min and max in mV. + - qcom,cdc-buck-sido-current = sido supply's max current in mA. + + - cdc-vdd-tx-h-supply: phandle of tx-h supply's regulator device tree node. + - qcom,cdc-vdd-tx-h-voltage: tx-h supply's voltage level min and max in mV. + - qcom,cdc-vdd-tx-h-current: tx-h supply's max current in mA. + + - cdc-vdd-rx-h-supply: phandle of rx-h supply's regulator device tree node. + - qcom,cdc-vdd-rx-h-voltage: rx-h supply's voltage level min and max in mV. + - qcom,cdc-vdd-rx-h-current: rx-h supply's max current in mA. + + - cdc-vddpx-1-supply: phandle of px-1 supply's regulator device tree node. + - qcom,cdc-vddpx-1-voltage: px-1 supply's voltage level min and max in mV. + - qcom,cdc-vddpx-1-current: px-1 supply's max current in mA. + + - qcom,cdc-static-supplies: List of supplies to be enabled prior to codec + hardware probe. Supplies in this list will be + stay enabled. + + - qcom,cdc-micbias1-mv - micbias1 output voltage in milli volts. + - qcom,cdc-micbias2-mv - micbias2 output voltage in milli volts. + - qcom,cdc-micbias3-mv - micbias3 output voltage in milli volts. + - qcom,cdc-micbias4-mv - micbias4 output voltage in milli volts. + + - qcom,cdc-mclk-clk-rate - Specifies the master clock rate in Hz required for + codec. + + - qcom,cdc-dmic-sample-rate - Specifies dmic sample rate. + - qcom,cdc-variant - Specifies codec variant. + +Example: +i2c_3: i2c@78B7000 { /* BLSP1 QUP3 */ + compatible = "qcom,i2c-msm-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "qup_phys_addr"; + reg = <0x78B7000 0x600>; + interrupt-names = "qup_irq"; + interrupts = <0 97 0>; + dmas = <&dma_blsp1 12 64 0x20000020 0x20>, + <&dma_blsp1 13 32 0x20000020 0x20>; + dma-names = "tx", "rx"; + qcom,master-id = <86>; + qcom,clk-freq-out = <400000>; + qcom,clk-freq-in = <19200000>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>, + <&clock_gcc clk_gcc_blsp1_qup3_i2c_apps_clk>; + pinctrl-names = "i2c_active", "i2c_sleep"; + pinctrl-0 = <&i2c_3_active>; + pinctrl-1 = <&i2c_3_sleep>; + status = "disabled"; + + wcd9xxx_codec@d{ + compatible = "qcom,tasha-i2c-pgd"; + reg = <0x0d>; + + interrupt-parent = <&wcd9xxx_intc>; + interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + 17 18 19 20 21 22 23 24 25 26 27 28 29 + 30>; + + qcom,cdc-reset-gpio = <&tlmm_pinmux 90 0>; + + cdc-vdd-buck-supply = <&codec_buck_vreg>; + qcom,cdc-vdd-buck-voltage = <1800000 1800000>; + qcom,cdc-vdd-buck-current = <650000>; + + cdc-buck-sido-supply = <&codec_buck_vreg>; + qcom,cdc-buck-sido-voltage = <1800000 1800000>; + qcom,cdc-buck-sido-current = <200000>; + + cdc-vdd-tx-h-supply = <&pmdcalifornium_l6>; + qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>; + qcom,cdc-vdd-tx-h-current = <25000>; + + cdc-vdd-rx-h-supply = <&pmdcalifornium_l6>; + qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>; + qcom,cdc-vdd-rx-h-current = <25000>; + + cdc-vddpx-1-supply = <&pmdcalifornium_l6>; + qcom,cdc-vddpx-1-voltage = <1800000 1800000>; + qcom,cdc-vddpx-1-current = <10000>; + + qcom,cdc-static-supplies = "cdc-vdd-buck", + "cdc-buck-sido", + "cdc-vdd-tx-h", + "cdc-vdd-rx-h", + "cdc-vddpx-1"; + + qcom,cdc-micbias1-mv = <1800>; + qcom,cdc-micbias2-mv = <1800>; + qcom,cdc-micbias3-mv = <1800>; + qcom,cdc-micbias4-mv = <1800>; + qcom,cdc-mclk-clk-rate = <12288000>; + qcom,cdc-dmic-sample-rate = <4800000>; + + swr_master { + compatible = "qcom,swr-wcd"; + #address-cells = <2>; + #size-cells = <0>; + + wsa881x_1: wsa881x@20170212 { + compatible = "qcom,wsa881x"; + reg = <0x00 0x20170212>; + qcom,spkr-sd-n-gpio = <&tlmm_pinmux 80 0>; + + cdc-vdd-d-supply = <&pmdcalifornium_l6>; + qcom,cdc-vdd-d-voltage = <1800000 + 1800000>; + qcom,cdc-vdd-d-current = <2000>; + + cdc-vdd-a-supply = <&pmdcalifornium_l6>; + qcom,cdc-vdd-a-voltage = <1800000 + 1800000>; + qcom,cdc-vdd-a-current = <2000>; + + qcom,cdc-static-supplies = "cdc-vdd-d", + "cdc-vdd-a"; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/sound/wsa881x-analog.txt b/Documentation/devicetree/bindings/sound/wsa881x-analog.txt new file mode 100644 index 0000000000000000000000000000000000000000..ca7bc045f01eada68a41e70d60f68b6c63ef52d5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wsa881x-analog.txt @@ -0,0 +1,88 @@ +wsa881x Audio CODEC + +wsa881x Audio CODEC can support either I2C interface or Soundwire interface. +In Analog mode, this codec will operate as I2C slave device and +interacts with I2C controller for status, control and interrupt management. +The wsa881x device nodes will be represented as child nodes to I2C +master device node in the devicetree. Currently, the below devicetree +bindings are for I2C mode only and does not include soundwire mode. + +Required properties: + + - compatible : "qcom,wsa881x-i2c-codec" + - reg : Unique device ID of I2C slave device. + +Optional properties: +- qcom,msm-gpios : Lists down all the gpio sets that are supported. +- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets +mentioned in qcom,msm-gpios. Say we have 2^N combinations for N GPIOs, this +list all 2^N combinations. +- pinctrl-names : The combinations of gpio sets from above that are supported in +the flavor. This can be sometimes same as qcom, pinctrl-names i.e with 2^N +combinations or will have less incase if some combination is not supported. +- pinctrl-# : Pinctrl states as mentioned in pinctrl-names. + +* wsa_intc + +Required properties: + + - compatible : "qcom,wsa-irq" + - interrupt-controller : Mark this device node as an + interrupt controller + - #interrupt-cells : Should be 1 + - interrupt-parent : Parent interrupt controller + - interrupts : Interrupt number on the parent + interrupt controller + - interrupt-names : Name of interrupt on the parent + interrupt controller +Example: + +wsa881x-i2c-codec@0e { + compatible = "qcom,wsa881x-i2c-codec"; + reg = <0x0e>; + qcom,msm-gpios = "wsa_clk", + "wsa_reset"; + qcom,pinctrl-names = "all_off", + "wsa_clk", + "wsa_active", + "wsa_clk_active"; + pinctrl-names = "all_off", + "wsa_clk", + "wsa_active", + "wsa_clk_active"; + pinctrl-0 = <&wsa_clk_off &wsa_suspend>; + pinctrl-1 = <&wsa_clk_on &wsa_suspend>; + pinctrl-2 = <&wsa_clk_off &wsa_active>; + pinctrl-3 = <&wsa_clk_on &wsa_active>; +}; + +wsa881x-i2c-codec@44 { + compatible = "qcom,wsa881x-i2c-codec"; + reg = <0x44>; +}; + +wsa881x-i2c-codec@0f { + compatible = "qcom,wsa881x-i2c-codec"; + reg = <0x0f>; + qcom,msm-gpios = "wsa_clk", + "wsa_reset"; + qcom,pinctrl-names = "all_off", + "wsa_clk", + "wsa_active", + "wsa_clk_active"; + pinctrl-names = "all_off", + "wsa_active", + "wsa_clk_active"; + pinctrl-0 = <&wsa_clk_off &wsa_suspend>; + pinctrl-1 = <&wsa_clk_off &wsa_active>; + pinctrl-2 = <&wsa_clk_on &wsa_active>; +}; + +wsa_intc: wsa-irq { + compatible = "qcom,wsa-irq"; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&msm_gpio>; + interrupts = <97 0>; + interrupt-names = "wsa-int"; +}; diff --git a/Documentation/devicetree/bindings/sound/wsa881x.txt b/Documentation/devicetree/bindings/sound/wsa881x.txt new file mode 100644 index 0000000000000000000000000000000000000000..e58ead5d9e4bd9baee7e320a5fc0d4c8ae577b59 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wsa881x.txt @@ -0,0 +1,45 @@ +wsa881x Audio CODEC + +wsa881x Audio CODEC can support either Soundwire interface or I2C interface. +In Soundwire mode, this codec will operate as soundwire slave device and +uses soundwire framework to interact with soundwire master controller. +The wsa881x device nodes will be represented as child nodes to soundwire +master device node in the devicetree. Currently, the below devicetree +bindings are for soundwire mode only and does not include I2C mode. + +Required properties: + + - compatible : "qcom,wsa881x" + - reg : Unique device ID of soundwire slave device(48 bits). + - qcom,spkr-sd-n-gpio : shutdown gpio pin to keep wsa881x in low power mode. + - pinctrl-names : Pincntrl entries to configure the PDM gpio lines and + cross connection switch gpio accordingly + - pinctrl-0 : This explains the active state of the shutdown gpio pin + to keep wsa881x in low power mode + - pinctrl-1 : This explains the suspend state of the shutdown gpio pin + to keep wsa881x in low power mode + +Optional properties: + +Example: + +wsa881x@32000 { + compatible = "qcom,wsa881x"; + reg = <0x00 0x032000>; + qcom,spkr-sd-n-gpio = <&pmi8994_gpios 2 0>; + pinctrl-names = "wsa_spkr_sd_act", + "wsa_spkr_sd_sus"; + pinctrl-0 = <&wsa_spkr_sd_act>; + pinctrl-1 = <&wsa_spkr_sd_sus>; +}; + +wsa881x@42000 { + compatible = "qcom,wsa881x"; + reg = <0x00 0x042000>; + qcom,spkr-sd-n-gpio = <&pmi8994_gpios 3 0>; + pinctrl-names = "wsa_spkr_sd_act", + "wsa_spkr_sd_sus"; + pinctrl-0 = <&wsa_spkr_sd_act>; + pinctrl-1 = <&wsa_spkr_sd_sus>; + +}; diff --git a/Documentation/devicetree/bindings/soundwire/swr-wcd-ctrl.txt b/Documentation/devicetree/bindings/soundwire/swr-wcd-ctrl.txt new file mode 100644 index 0000000000000000000000000000000000000000..757ca9a34e58e0a6d82aada046c6b7d2412f5347 --- /dev/null +++ b/Documentation/devicetree/bindings/soundwire/swr-wcd-ctrl.txt @@ -0,0 +1,31 @@ +Qualcomm technologies inc WCD Codec SoundWire controller + +Required properties: + + - compatible : should be "qcom,swr-wcd" + - reg : Unique device ID(48 bits) of Soundwire slave device node. + In the below example, wsa881x is soundwire slave device for + which the swr-devid is <0x0 0x032000> where 0x03 represents + device Unique_ID, 0x20 represents Part_Id1 and 0x00 + represents part_Id2. +- #address-cells = <2>; +- #size-cells = <0>; + +Optional property: + + Example: + swr_master { + compatible = "qcom,swr-wcd"; + #address-cells = <2>; + #size-cells = <0>; + + wsa881x@32000 { + compatible = "qcom,wsa881x"; + reg = <0x00 0x032000>; + }; + + wsa881x@42000 { + compatible = "qcom,wsa881x"; + reg = <0x00 0x042000>; + }; + }; diff --git a/drivers/Kconfig b/drivers/Kconfig index cc113029a4f8629b62b7e3594fe29794534521ae..cf03bd74946da5f8bea5dba034c892747f030b50 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -54,6 +54,8 @@ source "drivers/char/Kconfig" source "drivers/i2c/Kconfig" +source "drivers/soundwire/Kconfig" + source "drivers/spi/Kconfig" source "drivers/spmi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index cf401948f3af4b9beca09cb502ab97fd176e2cc1..98dc622ffe708cd3637412199c88ab961b3575d1 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_TARGET_CORE) += target/ obj-$(CONFIG_MTD) += mtd/ obj-$(CONFIG_SPI) += spi/ obj-$(CONFIG_SPMI) += spmi/ +obj-$(CONFIG_SOUNDWIRE) += soundwire/ obj-$(CONFIG_HSI) += hsi/ obj-y += net/ obj-$(CONFIG_ATM) += atm/ diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c6df6442ba2b1b378386db03cb530fa6b6e3e723..5f6083d147412a15ec7ff31b0b5b69d87fd6a602 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1607,6 +1607,80 @@ config MFD_STW481X in various ST Microelectronics and ST-Ericsson embedded Nomadik series. +config MSM_CDC_PINCTRL + tristate "MSM Codec Pinctrl" + select MFD_CORE + help + Enables msm codec pinctrl driver. The pinctrl driver + provides support for handling WCD and WSA MSM gpios. This + pinctrl driver will handle WCD and WSA gpios pinctrl states. + This driver acts as interface between codec and pinctrl + framework. + +config MSM_CDC_SUPPLY + tristate "MSM Codec Power Supply" + help + Enables msm codec power supply driver. The power supply + driver provides API support for handling WCD and WSA codec + power supply enable or disable. This driver acts as interface + between codec and regulator framework. + +config WCD9XXX_CODEC_UTIL + tristate "WCD9XXX Codec Utils" + select MFD_CORE + help + WCD9XXX Util driver provides APIs for WCD drivers to reset, + suspend/resume, regmap bus callback functions and read/write + functions. This driver also hides the underlying bus related + functionalities. + +config WCD9330_CODEC + tristate "WCD9330 Codec" + select SLIMBUS + select MFD_CORE + select WCD9XXX_CODEC_UTIL + select MSM_CDC_SUPPLY + select REGMAP_ALLOW_WRITE_DEBUGFS + help + Enables the WCD9xxx codec core driver. The core driver provides + read/write capability to registers which are part of the + WCD9330 core and gives the ability to use the WCD9330 codec. + The WCD9330 codec support either I2C/I2S or Slimbus for + control and data exchnage with master processor. + +config WCD9335_CODEC + tristate "WCD9335 Codec" + select SLIMBUS + select SOUNDWIRE_WCD_CTRL + select MFD_CORE + select WCD9XXX_CODEC_UTIL + select MSM_CDC_SUPPLY + select MSM_CDC_PINCTRL + select REGMAP_ALLOW_WRITE_DEBUGFS + help + Enables the WCD9xxx codec core driver. The core driver provides + read/write capability to registers which are part of the + WCD9335 core and gives the ability to use the WCD9335 codec. + The WCD9335 codec support either I2C/I2S or Slimbus for + control and data exchnage with master processor. + +config WCD934X_CODEC + tristate "WCD934X Codec" + depends on SLIMBUS + select SOUNDWIRE_WCD_CTRL + select MFD_CORE + select WCD9XXX_CODEC_UTIL + select MSM_CDC_SUPPLY + select MSM_CDC_PINCTRL + select REGMAP_ALLOW_WRITE_DEBUGFS + select PINCTRL_WCD + help + Enables the WCD9xxx codec core driver. The core driver provides + read/write capability to registers which are part of the + WCD934X core and gives the ability to use the WCD934X codec. + The WCD934X codec supports either I2C/I2S or Slimbus for + control and data exchange with master processor. + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9834e669d98573ce8c0b0a15b3f167dbf3485d87..cdae17f8f62f120221c6e626d3eb6c07a3e9654c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -204,6 +204,15 @@ obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o obj-$(CONFIG_MFD_DLN2) += dln2.o obj-$(CONFIG_MFD_RT5033) += rt5033.o obj-$(CONFIG_MFD_SKY81452) += sky81452.o +obj-$(CONFIG_MSM_CDC_PINCTRL) += msm-cdc-pinctrl.o +obj-$(CONFIG_MSM_CDC_SUPPLY) += msm-cdc-supply.o +obj-$(CONFIG_WCD9XXX_CODEC_UTIL) += wcd9xxx-utils.o +obj-$(CONFIG_WCD9330_CODEC) += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o\ + wcd9330-regmap.o +obj-$(CONFIG_WCD9335_CODEC) += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o\ + wcd9335-regmap.o wcd9335-tables.o +obj-$(CONFIG_WCD934X_CODEC) += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o\ + wcd934x-regmap.o wcd934x-tables.o intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC) += intel_soc_pmic_bxtwc.o diff --git a/drivers/mfd/msm-cdc-pinctrl.c b/drivers/mfd/msm-cdc-pinctrl.c new file mode 100644 index 0000000000000000000000000000000000000000..3ffd202458772bd51b795b52d616198261267e7e --- /dev/null +++ b/drivers/mfd/msm-cdc-pinctrl.c @@ -0,0 +1,243 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct msm_cdc_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_active; + struct pinctrl_state *pinctrl_sleep; + int gpio; + bool state; +}; + +static struct msm_cdc_pinctrl_info *msm_cdc_pinctrl_get_gpiodata( + struct device_node *np) +{ + struct platform_device *pdev; + struct msm_cdc_pinctrl_info *gpio_data; + + if (!np) { + pr_err("%s: device node is null\n", __func__); + return NULL; + } + + pdev = of_find_device_by_node(np); + if (!pdev) { + pr_err("%s: platform device not found!\n", __func__); + return NULL; + } + + gpio_data = dev_get_drvdata(&pdev->dev); + if (!gpio_data) + dev_err(&pdev->dev, "%s: cannot find cdc gpio info\n", + __func__); + + return gpio_data; +} + +/* + * msm_cdc_get_gpio_state: select pinctrl sleep state + * @np: pointer to struct device_node + * + * Returns error code for failure and GPIO value on success + */ +int msm_cdc_get_gpio_state(struct device_node *np) +{ + struct msm_cdc_pinctrl_info *gpio_data; + int value = -EINVAL; + + gpio_data = msm_cdc_pinctrl_get_gpiodata(np); + if (!gpio_data) + return value; + + if (gpio_is_valid(gpio_data->gpio)) + value = gpio_get_value_cansleep(gpio_data->gpio); + + return value; +} +EXPORT_SYMBOL(msm_cdc_get_gpio_state); + +/* + * msm_cdc_pinctrl_select_sleep_state: select pinctrl sleep state + * @np: pointer to struct device_node + * + * Returns error code for failure + */ +int msm_cdc_pinctrl_select_sleep_state(struct device_node *np) +{ + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = msm_cdc_pinctrl_get_gpiodata(np); + if (!gpio_data) + return -EINVAL; + + if (!gpio_data->pinctrl_sleep) { + pr_err("%s: pinctrl sleep state is null\n", __func__); + return -EINVAL; + } + gpio_data->state = false; + + return pinctrl_select_state(gpio_data->pinctrl, + gpio_data->pinctrl_sleep); +} +EXPORT_SYMBOL(msm_cdc_pinctrl_select_sleep_state); + +/* + * msm_cdc_pinctrl_select_active_state: select pinctrl active state + * @np: pointer to struct device_node + * + * Returns error code for failure + */ +int msm_cdc_pinctrl_select_active_state(struct device_node *np) +{ + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = msm_cdc_pinctrl_get_gpiodata(np); + if (!gpio_data) + return -EINVAL; + + if (!gpio_data->pinctrl_active) { + pr_err("%s: pinctrl active state is null\n", __func__); + return -EINVAL; + } + gpio_data->state = true; + + return pinctrl_select_state(gpio_data->pinctrl, + gpio_data->pinctrl_active); +} +EXPORT_SYMBOL(msm_cdc_pinctrl_select_active_state); + +/* + * msm_cdc_pinctrl_get_state: get curren pinctrl state + * @np: pointer to struct device_node + * + * Returns 0 for sleep state, 1 for active state + */ +bool msm_cdc_pinctrl_get_state(struct device_node *np) +{ + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = msm_cdc_pinctrl_get_gpiodata(np); + if (!gpio_data) + return -EINVAL; + + return gpio_data->state; +} +EXPORT_SYMBOL(msm_cdc_pinctrl_get_state); + +static int msm_cdc_pinctrl_probe(struct platform_device *pdev) +{ + int ret = 0; + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = devm_kzalloc(&pdev->dev, + sizeof(struct msm_cdc_pinctrl_info), + GFP_KERNEL); + if (!gpio_data) + return -ENOMEM; + + gpio_data->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(gpio_data->pinctrl)) { + dev_err(&pdev->dev, "%s: Cannot get cdc gpio pinctrl:%ld\n", + __func__, PTR_ERR(gpio_data->pinctrl)); + ret = PTR_ERR(gpio_data->pinctrl); + goto err_pctrl_get; + } + + gpio_data->pinctrl_active = pinctrl_lookup_state( + gpio_data->pinctrl, "aud_active"); + if (IS_ERR_OR_NULL(gpio_data->pinctrl_active)) { + dev_err(&pdev->dev, "%s: Cannot get aud_active pinctrl state:%ld\n", + __func__, PTR_ERR(gpio_data->pinctrl_active)); + ret = PTR_ERR(gpio_data->pinctrl_active); + goto err_lookup_state; + } + + gpio_data->pinctrl_sleep = pinctrl_lookup_state( + gpio_data->pinctrl, "aud_sleep"); + if (IS_ERR_OR_NULL(gpio_data->pinctrl_sleep)) { + dev_err(&pdev->dev, "%s: Cannot get aud_sleep pinctrl state:%ld\n", + __func__, PTR_ERR(gpio_data->pinctrl_sleep)); + ret = PTR_ERR(gpio_data->pinctrl_sleep); + goto err_lookup_state; + } + + /* Set pinctrl state to aud_sleep by default */ + ret = pinctrl_select_state(gpio_data->pinctrl, + gpio_data->pinctrl_sleep); + if (ret) + dev_err(&pdev->dev, "%s: set cdc gpio sleep state fail: %d\n", + __func__, ret); + + gpio_data->gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,cdc-rst-n-gpio", 0); + if (gpio_is_valid(gpio_data->gpio)) { + ret = gpio_request(gpio_data->gpio, "MSM_CDC_RESET"); + if (ret) { + dev_err(&pdev->dev, "%s: Failed to request gpio %d\n", + __func__, gpio_data->gpio); + goto err_lookup_state; + } + } + + dev_set_drvdata(&pdev->dev, gpio_data); + return 0; + +err_lookup_state: + devm_pinctrl_put(gpio_data->pinctrl); +err_pctrl_get: + devm_kfree(&pdev->dev, gpio_data); + return ret; +} + +static int msm_cdc_pinctrl_remove(struct platform_device *pdev) +{ + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = dev_get_drvdata(&pdev->dev); + + if (gpio_data && gpio_data->pinctrl) + devm_pinctrl_put(gpio_data->pinctrl); + + devm_kfree(&pdev->dev, gpio_data); + + return 0; +} + +static const struct of_device_id msm_cdc_pinctrl_match[] = { + {.compatible = "qcom,msm-cdc-pinctrl"}, + {} +}; + +static struct platform_driver msm_cdc_pinctrl_driver = { + .driver = { + .name = "msm-cdc-pinctrl", + .owner = THIS_MODULE, + .of_match_table = msm_cdc_pinctrl_match, + }, + .probe = msm_cdc_pinctrl_probe, + .remove = msm_cdc_pinctrl_remove, +}; +module_platform_driver(msm_cdc_pinctrl_driver); + +MODULE_DESCRIPTION("MSM CODEC pin control platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/msm-cdc-supply.c b/drivers/mfd/msm-cdc-supply.c new file mode 100644 index 0000000000000000000000000000000000000000..36c58e0fc092b2bba8d5dc0209870d7c91e9b5da --- /dev/null +++ b/drivers/mfd/msm-cdc-supply.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define CODEC_DT_MAX_PROP_SIZE 40 + +static int msm_cdc_dt_parse_vreg_info(struct device *dev, + struct cdc_regulator *cdc_vreg, + const char *name, bool is_ond) +{ + char prop_name[CODEC_DT_MAX_PROP_SIZE]; + struct device_node *regulator_node = NULL; + const __be32 *prop; + int len, rc; + u32 prop_val; + + /* Parse supply name */ + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply", name); + + regulator_node = of_parse_phandle(dev->of_node, prop_name, 0); + if (!regulator_node) { + dev_err(dev, "%s: Looking up %s property in node %s failed", + __func__, prop_name, dev->of_node->full_name); + rc = -EINVAL; + goto done; + } + cdc_vreg->name = name; + cdc_vreg->ondemand = is_ond; + + /* Parse supply - voltage */ + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-voltage", name); + prop = of_get_property(dev->of_node, prop_name, &len); + if (!prop || (len != (2 * sizeof(__be32)))) { + dev_err(dev, "%s: %s %s property\n", __func__, + prop ? "invalid format" : "no", prop_name); + rc = -EINVAL; + goto done; + } else { + cdc_vreg->min_uV = be32_to_cpup(&prop[0]); + cdc_vreg->max_uV = be32_to_cpup(&prop[1]); + } + + /* Parse supply - current */ + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-current", name); + rc = of_property_read_u32(dev->of_node, prop_name, &prop_val); + if (rc) { + dev_err(dev, "%s: Looking up %s property in node %s failed", + __func__, prop_name, dev->of_node->full_name); + goto done; + } + cdc_vreg->optimum_uA = prop_val; + + dev_info(dev, "%s: %s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n", + __func__, cdc_vreg->name, cdc_vreg->min_uV, cdc_vreg->max_uV, + cdc_vreg->optimum_uA, cdc_vreg->ondemand); + +done: + return rc; +} + +static int msm_cdc_parse_supplies(struct device *dev, + struct cdc_regulator *cdc_reg, + const char *sup_list, int sup_cnt, + bool is_ond) +{ + int idx, rc = 0; + const char *name = NULL; + + for (idx = 0; idx < sup_cnt; idx++) { + rc = of_property_read_string_index(dev->of_node, sup_list, idx, + &name); + if (rc) { + dev_err(dev, "%s: read string %s[%d] error (%d)\n", + __func__, sup_list, idx, rc); + goto done; + } + + dev_dbg(dev, "%s: Found cdc supply %s as part of %s\n", + __func__, name, sup_list); + + rc = msm_cdc_dt_parse_vreg_info(dev, &cdc_reg[idx], name, + is_ond); + if (rc) { + dev_err(dev, "%s: parse %s vreg info failed (%d)\n", + __func__, name, rc); + goto done; + } + } + +done: + return rc; +} + +static int msm_cdc_check_supply_param(struct device *dev, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + if (!dev) { + pr_err("%s: device is NULL\n", __func__); + return -ENODEV; + } + + if (!cdc_vreg || (num_supplies <= 0)) { + dev_err(dev, "%s: supply check failed: vreg: %pK, num_supplies: %d\n", + __func__, cdc_vreg, num_supplies); + return -EINVAL; + } + + return 0; +} + +/* + * msm_cdc_disable_static_supplies: + * Disable codec static supplies + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * + * Return error code if supply disable is failed + */ +int msm_cdc_disable_static_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + int rc, i; + + if ((!dev) || (!supplies) || (!cdc_vreg)) { + pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + for (i = 0; i < num_supplies; i++) { + if (cdc_vreg[i].ondemand) + continue; + + rc = regulator_disable(supplies[i].consumer); + if (rc) + dev_err(dev, "%s: failed to disable supply %s, err:%d\n", + __func__, supplies[i].supply, rc); + else + dev_dbg(dev, "%s: disabled regulator %s\n", + __func__, supplies[i].supply); + } + + return rc; +} +EXPORT_SYMBOL(msm_cdc_disable_static_supplies); + +/* + * msm_cdc_release_supplies: + * Release codec power supplies + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * + * Return error code if supply disable is failed + */ +int msm_cdc_release_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + int rc = 0; + int i; + + if ((!dev) || (!supplies) || (!cdc_vreg)) { + pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + msm_cdc_disable_static_supplies(dev, supplies, cdc_vreg, + num_supplies); + for (i = 0; i < num_supplies; i++) { + if (regulator_count_voltages(supplies[i].consumer) < 0) + continue; + + regulator_set_voltage(supplies[i].consumer, 0, + cdc_vreg[i].max_uV); + regulator_set_load(supplies[i].consumer, 0); + devm_regulator_put(supplies[i].consumer); + supplies[i].consumer = NULL; + } + devm_kfree(dev, supplies); + + return rc; +} +EXPORT_SYMBOL(msm_cdc_release_supplies); + +/* + * msm_cdc_enable_static_supplies: + * Enable codec static supplies + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * + * Return error code if supply enable is failed + */ +int msm_cdc_enable_static_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + int rc, i; + + if ((!dev) || (!supplies) || (!cdc_vreg)) { + pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + for (i = 0; i < num_supplies; i++) { + if (cdc_vreg[i].ondemand) + continue; + + rc = regulator_enable(supplies[i].consumer); + if (rc) { + dev_err(dev, "%s: failed to enable supply %s, rc: %d\n", + __func__, supplies[i].supply, rc); + break; + } + } + + while (rc && i--) + if (!cdc_vreg[i].ondemand) + regulator_disable(supplies[i].consumer); + + return rc; +} +EXPORT_SYMBOL(msm_cdc_enable_static_supplies); + +/* + * msm_cdc_init_supplies: + * Initialize codec static supplies with regulator get + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * + * Return error code if supply init is failed + */ +int msm_cdc_init_supplies(struct device *dev, + struct regulator_bulk_data **supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + struct regulator_bulk_data *vsup; + int rc; + int i; + + if (!dev || !cdc_vreg) { + pr_err("%s: device pointer or dce_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + vsup = devm_kcalloc(dev, num_supplies, + sizeof(struct regulator_bulk_data), + GFP_KERNEL); + if (!vsup) + return -ENOMEM; + + for (i = 0; i < num_supplies; i++) { + if (!cdc_vreg[i].name) { + dev_err(dev, "%s: supply name not defined\n", + __func__); + rc = -EINVAL; + goto err_supply; + } + vsup[i].supply = cdc_vreg[i].name; + } + + rc = devm_regulator_bulk_get(dev, num_supplies, vsup); + if (rc) { + dev_err(dev, "%s: failed to get supplies (%d)\n", + __func__, rc); + goto err_supply; + } + + /* Set voltage and current on regulators */ + for (i = 0; i < num_supplies; i++) { + if (regulator_count_voltages(vsup[i].consumer) < 0) + continue; + + rc = regulator_set_voltage(vsup[i].consumer, + cdc_vreg[i].min_uV, + cdc_vreg[i].max_uV); + if (rc) { + dev_err(dev, "%s: set regulator voltage failed for %s, err:%d\n", + __func__, vsup[i].supply, rc); + goto err_set_supply; + } + rc = regulator_set_load(vsup[i].consumer, + cdc_vreg[i].optimum_uA); + if (rc < 0) { + dev_err(dev, "%s: set regulator optimum mode failed for %s, err:%d\n", + __func__, vsup[i].supply, rc); + goto err_set_supply; + } + } + + *supplies = vsup; + + return 0; + +err_set_supply: + for (i = 0; i < num_supplies; i++) + devm_regulator_put(vsup[i].consumer); +err_supply: + devm_kfree(dev, vsup); + return rc; +} +EXPORT_SYMBOL(msm_cdc_init_supplies); + +/* + * msm_cdc_get_power_supplies: + * Get codec power supplies from device tree. + * Allocate memory to hold regulator data for + * all power supplies. + * + * @dev: pointer to codec device + * @cdc_vreg: pointer to codec regulator + * @total_num_supplies: total number of supplies read from DT + * + * Return error code if supply disable is failed + */ +int msm_cdc_get_power_supplies(struct device *dev, + struct cdc_regulator **cdc_vreg, + int *total_num_supplies) +{ + const char *static_prop_name = "qcom,cdc-static-supplies"; + const char *ond_prop_name = "qcom,cdc-on-demand-supplies"; + const char *cp_prop_name = "qcom,cdc-cp-supplies"; + int static_sup_cnt = 0; + int ond_sup_cnt = 0; + int cp_sup_cnt = 0; + int num_supplies = 0; + struct cdc_regulator *cdc_reg; + int rc; + + if (!dev) { + pr_err("%s: device pointer is NULL\n", __func__); + return -EINVAL; + } + static_sup_cnt = of_property_count_strings(dev->of_node, + static_prop_name); + if (IS_ERR_VALUE(static_sup_cnt)) { + dev_err(dev, "%s: Failed to get static supplies(%d)\n", + __func__, static_sup_cnt); + rc = static_sup_cnt; + goto err_supply_cnt; + } + ond_sup_cnt = of_property_count_strings(dev->of_node, ond_prop_name); + if (IS_ERR_VALUE(ond_sup_cnt)) + ond_sup_cnt = 0; + + cp_sup_cnt = of_property_count_strings(dev->of_node, + cp_prop_name); + if (IS_ERR_VALUE(cp_sup_cnt)) + cp_sup_cnt = 0; + + num_supplies = static_sup_cnt + ond_sup_cnt + cp_sup_cnt; + if (num_supplies <= 0) { + dev_err(dev, "%s: supply count is 0 or negative\n", __func__); + rc = -EINVAL; + goto err_supply_cnt; + } + + cdc_reg = devm_kcalloc(dev, num_supplies, + sizeof(struct cdc_regulator), + GFP_KERNEL); + if (!cdc_reg) { + rc = -ENOMEM; + goto err_mem_alloc; + } + + rc = msm_cdc_parse_supplies(dev, cdc_reg, static_prop_name, + static_sup_cnt, false); + if (rc) { + dev_err(dev, "%s: failed to parse static supplies(%d)\n", + __func__, rc); + goto err_sup; + } + + rc = msm_cdc_parse_supplies(dev, &cdc_reg[static_sup_cnt], + ond_prop_name, ond_sup_cnt, + true); + if (rc) { + dev_err(dev, "%s: failed to parse demand supplies(%d)\n", + __func__, rc); + goto err_sup; + } + + rc = msm_cdc_parse_supplies(dev, + &cdc_reg[static_sup_cnt + ond_sup_cnt], + cp_prop_name, cp_sup_cnt, true); + if (rc) { + dev_err(dev, "%s: failed to parse cp supplies(%d)\n", + __func__, rc); + goto err_sup; + } + + *cdc_vreg = cdc_reg; + *total_num_supplies = num_supplies; + + return 0; + +err_sup: + devm_kfree(dev, cdc_reg); +err_supply_cnt: +err_mem_alloc: + return rc; +} +EXPORT_SYMBOL(msm_cdc_get_power_supplies); diff --git a/drivers/mfd/wcd9330-regmap.c b/drivers/mfd/wcd9330-regmap.c new file mode 100644 index 0000000000000000000000000000000000000000..878ea72f1b1dc839201437b0c2880802414b678c --- /dev/null +++ b/drivers/mfd/wcd9330-regmap.c @@ -0,0 +1,990 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include "wcd9xxx-regmap.h" + +static struct reg_default wcd9330_defaults[] = { + { TOMTOM_A_CHIP_CTL, TOMTOM_A_CHIP_CTL__POR }, + { TOMTOM_A_CHIP_STATUS, TOMTOM_A_CHIP_STATUS__POR }, + { TOMTOM_A_CHIP_ID_BYTE_0, TOMTOM_A_CHIP_ID_BYTE_0__POR }, + { TOMTOM_A_CHIP_ID_BYTE_1, TOMTOM_A_CHIP_ID_BYTE_1__POR }, + { TOMTOM_A_CHIP_ID_BYTE_2, TOMTOM_A_CHIP_ID_BYTE_2__POR }, + { TOMTOM_A_CHIP_ID_BYTE_3, TOMTOM_A_CHIP_ID_BYTE_3__POR }, + { TOMTOM_A_CHIP_I2C_SLAVE_ID, TOMTOM_A_CHIP_I2C_SLAVE_ID__POR }, + { TOMTOM_A_SLAVE_ID_1, TOMTOM_A_SLAVE_ID_1__POR }, + { TOMTOM_A_SLAVE_ID_2, TOMTOM_A_SLAVE_ID_2__POR }, + { TOMTOM_A_SLAVE_ID_3, TOMTOM_A_SLAVE_ID_3__POR }, + { TOMTOM_A_PIN_CTL_OE0, TOMTOM_A_PIN_CTL_OE0__POR }, + { TOMTOM_A_PIN_CTL_OE1, TOMTOM_A_PIN_CTL_OE1__POR }, + { TOMTOM_A_PIN_CTL_OE2, TOMTOM_A_PIN_CTL_OE2__POR }, + { TOMTOM_A_PIN_CTL_DATA0, TOMTOM_A_PIN_CTL_DATA0__POR }, + { TOMTOM_A_PIN_CTL_DATA1, TOMTOM_A_PIN_CTL_DATA1__POR }, + { TOMTOM_A_PIN_CTL_DATA2, TOMTOM_A_PIN_CTL_DATA2__POR }, + { TOMTOM_A_HDRIVE_GENERIC, TOMTOM_A_HDRIVE_GENERIC__POR }, + { TOMTOM_A_HDRIVE_OVERRIDE, TOMTOM_A_HDRIVE_OVERRIDE__POR }, + { TOMTOM_A_ANA_CSR_WAIT_STATE, TOMTOM_A_ANA_CSR_WAIT_STATE__POR }, + { TOMTOM_A_PROCESS_MONITOR_CTL0, TOMTOM_A_PROCESS_MONITOR_CTL0__POR }, + { TOMTOM_A_PROCESS_MONITOR_CTL1, TOMTOM_A_PROCESS_MONITOR_CTL1__POR }, + { TOMTOM_A_PROCESS_MONITOR_CTL2, TOMTOM_A_PROCESS_MONITOR_CTL2__POR }, + { TOMTOM_A_PROCESS_MONITOR_CTL3, TOMTOM_A_PROCESS_MONITOR_CTL3__POR }, + { TOMTOM_A_QFUSE_CTL, TOMTOM_A_QFUSE_CTL__POR }, + { TOMTOM_A_QFUSE_STATUS, TOMTOM_A_QFUSE_STATUS__POR }, + { TOMTOM_A_QFUSE_DATA_OUT0, TOMTOM_A_QFUSE_DATA_OUT0__POR }, + { TOMTOM_A_QFUSE_DATA_OUT1, TOMTOM_A_QFUSE_DATA_OUT1__POR }, + { TOMTOM_A_QFUSE_DATA_OUT2, TOMTOM_A_QFUSE_DATA_OUT2__POR }, + { TOMTOM_A_QFUSE_DATA_OUT3, TOMTOM_A_QFUSE_DATA_OUT3__POR }, + { TOMTOM_A_QFUSE_DATA_OUT4, TOMTOM_A_QFUSE_DATA_OUT4__POR }, + { TOMTOM_A_QFUSE_DATA_OUT5, TOMTOM_A_QFUSE_DATA_OUT5__POR }, + { TOMTOM_A_QFUSE_DATA_OUT6, TOMTOM_A_QFUSE_DATA_OUT6__POR }, + { TOMTOM_A_QFUSE_DATA_OUT7, TOMTOM_A_QFUSE_DATA_OUT7__POR }, + { TOMTOM_A_CDC_CTL, TOMTOM_A_CDC_CTL__POR }, + { TOMTOM_A_LEAKAGE_CTL, TOMTOM_A_LEAKAGE_CTL__POR }, + { TOMTOM_A_SVASS_MEM_PTR0, TOMTOM_A_SVASS_MEM_PTR0__POR }, + { TOMTOM_A_SVASS_MEM_PTR1, TOMTOM_A_SVASS_MEM_PTR1__POR }, + { TOMTOM_A_SVASS_MEM_PTR2, TOMTOM_A_SVASS_MEM_PTR2__POR }, + { TOMTOM_A_SVASS_MEM_CTL, TOMTOM_A_SVASS_MEM_CTL__POR }, + { TOMTOM_A_SVASS_MEM_BANK, TOMTOM_A_SVASS_MEM_BANK__POR }, + { TOMTOM_A_DMIC_B1_CTL, TOMTOM_A_DMIC_B1_CTL__POR }, + { TOMTOM_A_DMIC_B2_CTL, TOMTOM_A_DMIC_B2_CTL__POR }, + { TOMTOM_A_SVASS_CLKRST_CTL, TOMTOM_A_SVASS_CLKRST_CTL__POR }, + { TOMTOM_A_SVASS_CPAR_CFG, TOMTOM_A_SVASS_CPAR_CFG__POR }, + { TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD, + TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD__POR }, + { TOMTOM_A_SVASS_CPAR_WDOG_CFG, TOMTOM_A_SVASS_CPAR_WDOG_CFG__POR }, + { TOMTOM_A_SVASS_CFG, TOMTOM_A_SVASS_CFG__POR }, + { TOMTOM_A_SVASS_SPE_CFG, TOMTOM_A_SVASS_SPE_CFG__POR }, + { TOMTOM_A_SVASS_STATUS, TOMTOM_A_SVASS_STATUS__POR }, + { TOMTOM_A_SVASS_INT_MASK, TOMTOM_A_SVASS_INT_MASK__POR }, + { TOMTOM_A_SVASS_INT_STATUS, TOMTOM_A_SVASS_INT_STATUS__POR }, + { TOMTOM_A_SVASS_INT_CLR, TOMTOM_A_SVASS_INT_CLR__POR }, + { TOMTOM_A_SVASS_DEBUG, TOMTOM_A_SVASS_DEBUG__POR }, + { TOMTOM_A_SVASS_SPE_BKUP_INT, TOMTOM_A_SVASS_SPE_BKUP_INT__POR }, + { TOMTOM_A_SVASS_MEM_ACC, TOMTOM_A_SVASS_MEM_ACC__POR }, + { TOMTOM_A_MEM_LEAKAGE_CTL, TOMTOM_A_MEM_LEAKAGE_CTL__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_TRG, TOMTOM_A_SVASS_SPE_INBOX_TRG__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_0, TOMTOM_A_SVASS_SPE_INBOX_0__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_1, TOMTOM_A_SVASS_SPE_INBOX_1__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_2, TOMTOM_A_SVASS_SPE_INBOX_2__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_3, TOMTOM_A_SVASS_SPE_INBOX_3__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_4, TOMTOM_A_SVASS_SPE_INBOX_4__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_5, TOMTOM_A_SVASS_SPE_INBOX_5__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_6, TOMTOM_A_SVASS_SPE_INBOX_6__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_7, TOMTOM_A_SVASS_SPE_INBOX_7__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_8, TOMTOM_A_SVASS_SPE_INBOX_8__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_9, TOMTOM_A_SVASS_SPE_INBOX_9__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_10, TOMTOM_A_SVASS_SPE_INBOX_10__POR }, + { TOMTOM_A_SVASS_SPE_INBOX_11, TOMTOM_A_SVASS_SPE_INBOX_11__POR }, + { TOMTOM_A_SVASS_SPE_OUTBOX_0, TOMTOM_A_SVASS_SPE_OUTBOX_0__POR }, + { TOMTOM_A_SVASS_SPE_OUTBOX_1, TOMTOM_A_SVASS_SPE_OUTBOX_1__POR }, + { TOMTOM_A_SVASS_SPE_OUTBOX_2, TOMTOM_A_SVASS_SPE_OUTBOX_2__POR }, + { TOMTOM_A_SVASS_SPE_OUTBOX_3, TOMTOM_A_SVASS_SPE_OUTBOX_3__POR }, + { TOMTOM_A_SVASS_SPE_OUTBOX_4, TOMTOM_A_SVASS_SPE_OUTBOX_4__POR }, + { TOMTOM_A_SVASS_SPE_OUTBOX_5, TOMTOM_A_SVASS_SPE_OUTBOX_5__POR }, + { TOMTOM_A_SVASS_SPE_OUTBOX_6, TOMTOM_A_SVASS_SPE_OUTBOX_6__POR }, + { TOMTOM_A_SVASS_SPE_OUTBOX_7, TOMTOM_A_SVASS_SPE_OUTBOX_7__POR }, + { TOMTOM_A_SVASS_SPE_OUTBOX_8, TOMTOM_A_SVASS_SPE_OUTBOX_8__POR }, + { TOMTOM_A_SVASS_SPE_OUTBOX_9, TOMTOM_A_SVASS_SPE_OUTBOX_9__POR }, + { TOMTOM_A_SVASS_SPE_OUTBOX_10, TOMTOM_A_SVASS_SPE_OUTBOX_10__POR }, + { TOMTOM_A_SVASS_SPE_OUTBOX_11, TOMTOM_A_SVASS_SPE_OUTBOX_11__POR }, + { TOMTOM_A_INTR_MODE, TOMTOM_A_INTR_MODE__POR }, + { TOMTOM_A_INTR1_MASK0, TOMTOM_A_INTR1_MASK0__POR }, + { TOMTOM_A_INTR1_MASK1, TOMTOM_A_INTR1_MASK1__POR }, + { TOMTOM_A_INTR1_MASK2, TOMTOM_A_INTR1_MASK2__POR }, + { TOMTOM_A_INTR1_MASK3, TOMTOM_A_INTR1_MASK3__POR }, + { TOMTOM_A_INTR1_STATUS0, TOMTOM_A_INTR1_STATUS0__POR }, + { TOMTOM_A_INTR1_STATUS1, TOMTOM_A_INTR1_STATUS1__POR }, + { TOMTOM_A_INTR1_STATUS2, TOMTOM_A_INTR1_STATUS2__POR }, + { TOMTOM_A_INTR1_STATUS3, TOMTOM_A_INTR1_STATUS3__POR }, + { TOMTOM_A_INTR1_CLEAR0, TOMTOM_A_INTR1_CLEAR0__POR }, + { TOMTOM_A_INTR1_CLEAR1, TOMTOM_A_INTR1_CLEAR1__POR }, + { TOMTOM_A_INTR1_CLEAR2, TOMTOM_A_INTR1_CLEAR2__POR }, + { TOMTOM_A_INTR1_CLEAR3, TOMTOM_A_INTR1_CLEAR3__POR }, + { TOMTOM_A_INTR1_LEVEL0, TOMTOM_A_INTR1_LEVEL0__POR }, + { TOMTOM_A_INTR1_LEVEL1, TOMTOM_A_INTR1_LEVEL1__POR }, + { TOMTOM_A_INTR1_LEVEL2, TOMTOM_A_INTR1_LEVEL2__POR }, + { TOMTOM_A_INTR1_LEVEL3, TOMTOM_A_INTR1_LEVEL3__POR }, + { TOMTOM_A_INTR1_TEST0, TOMTOM_A_INTR1_TEST0__POR }, + { TOMTOM_A_INTR1_TEST1, TOMTOM_A_INTR1_TEST1__POR }, + { TOMTOM_A_INTR1_TEST2, TOMTOM_A_INTR1_TEST2__POR }, + { TOMTOM_A_INTR1_TEST3, TOMTOM_A_INTR1_TEST3__POR }, + { TOMTOM_A_INTR1_SET0, TOMTOM_A_INTR1_SET0__POR }, + { TOMTOM_A_INTR1_SET1, TOMTOM_A_INTR1_SET1__POR }, + { TOMTOM_A_INTR1_SET2, TOMTOM_A_INTR1_SET2__POR }, + { TOMTOM_A_INTR1_SET3, TOMTOM_A_INTR1_SET3__POR }, + { TOMTOM_A_INTR2_MASK0, TOMTOM_A_INTR2_MASK0__POR }, + { TOMTOM_A_INTR2_STATUS0, TOMTOM_A_INTR2_STATUS0__POR }, + { TOMTOM_A_INTR2_CLEAR0, TOMTOM_A_INTR2_CLEAR0__POR }, + { TOMTOM_A_INTR2_LEVEL0, TOMTOM_A_INTR2_LEVEL0__POR }, + { TOMTOM_A_INTR2_TEST0, TOMTOM_A_INTR2_TEST0__POR }, + { TOMTOM_A_INTR2_SET0, TOMTOM_A_INTR2_SET0__POR }, + { TOMTOM_A_CDC_TX_I2S_SCK_MODE, TOMTOM_A_CDC_TX_I2S_SCK_MODE__POR }, + { TOMTOM_A_CDC_TX_I2S_WS_MODE, TOMTOM_A_CDC_TX_I2S_WS_MODE__POR }, + { TOMTOM_A_CDC_DMIC_DATA0_MODE, TOMTOM_A_CDC_DMIC_DATA0_MODE__POR }, + { TOMTOM_A_CDC_DMIC_CLK0_MODE, TOMTOM_A_CDC_DMIC_CLK0_MODE__POR }, + { TOMTOM_A_CDC_DMIC_DATA1_MODE, TOMTOM_A_CDC_DMIC_DATA1_MODE__POR }, + { TOMTOM_A_CDC_DMIC_CLK1_MODE, TOMTOM_A_CDC_DMIC_CLK1_MODE__POR }, + { TOMTOM_A_CDC_RX_I2S_SCK_MODE, TOMTOM_A_CDC_RX_I2S_SCK_MODE__POR }, + { TOMTOM_A_CDC_RX_I2S_WS_MODE, TOMTOM_A_CDC_RX_I2S_WS_MODE__POR }, + { TOMTOM_A_CDC_DMIC_DATA2_MODE, TOMTOM_A_CDC_DMIC_DATA2_MODE__POR }, + { TOMTOM_A_CDC_DMIC_CLK2_MODE, TOMTOM_A_CDC_DMIC_CLK2_MODE__POR }, + { TOMTOM_A_CDC_INTR1_MODE, TOMTOM_A_CDC_INTR1_MODE__POR }, + { TOMTOM_A_CDC_SB_NRZ_SEL_MODE, TOMTOM_A_CDC_SB_NRZ_SEL_MODE__POR }, + { TOMTOM_A_CDC_INTR2_MODE, TOMTOM_A_CDC_INTR2_MODE__POR }, + { TOMTOM_A_CDC_RF_PA_ON_MODE, TOMTOM_A_CDC_RF_PA_ON_MODE__POR }, + { TOMTOM_A_CDC_BOOST_MODE, TOMTOM_A_CDC_BOOST_MODE__POR }, + { TOMTOM_A_CDC_JTCK_MODE, TOMTOM_A_CDC_JTCK_MODE__POR }, + { TOMTOM_A_CDC_JTDI_MODE, TOMTOM_A_CDC_JTDI_MODE__POR }, + { TOMTOM_A_CDC_JTMS_MODE, TOMTOM_A_CDC_JTMS_MODE__POR }, + { TOMTOM_A_CDC_JTDO_MODE, TOMTOM_A_CDC_JTDO_MODE__POR }, + { TOMTOM_A_CDC_JTRST_MODE, TOMTOM_A_CDC_JTRST_MODE__POR }, + { TOMTOM_A_CDC_BIST_MODE_MODE, TOMTOM_A_CDC_BIST_MODE_MODE__POR }, + { TOMTOM_A_CDC_MAD_MAIN_CTL_1, TOMTOM_A_CDC_MAD_MAIN_CTL_1__POR }, + { TOMTOM_A_CDC_MAD_MAIN_CTL_2, TOMTOM_A_CDC_MAD_MAIN_CTL_2__POR }, + { TOMTOM_A_CDC_MAD_AUDIO_CTL_1, TOMTOM_A_CDC_MAD_AUDIO_CTL_1__POR }, + { TOMTOM_A_CDC_MAD_AUDIO_CTL_2, TOMTOM_A_CDC_MAD_AUDIO_CTL_2__POR }, + { TOMTOM_A_CDC_MAD_AUDIO_CTL_3, TOMTOM_A_CDC_MAD_AUDIO_CTL_3__POR }, + { TOMTOM_A_CDC_MAD_AUDIO_CTL_4, TOMTOM_A_CDC_MAD_AUDIO_CTL_4__POR }, + { TOMTOM_A_CDC_MAD_AUDIO_CTL_5, TOMTOM_A_CDC_MAD_AUDIO_CTL_5__POR }, + { TOMTOM_A_CDC_MAD_AUDIO_CTL_6, TOMTOM_A_CDC_MAD_AUDIO_CTL_6__POR }, + { TOMTOM_A_CDC_MAD_AUDIO_CTL_7, TOMTOM_A_CDC_MAD_AUDIO_CTL_7__POR }, + { TOMTOM_A_CDC_MAD_AUDIO_CTL_8, TOMTOM_A_CDC_MAD_AUDIO_CTL_8__POR }, + { TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR, + TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR__POR }, + { TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL, + TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL__POR }, + { TOMTOM_A_CDC_MAD_ULTR_CTL_1, TOMTOM_A_CDC_MAD_ULTR_CTL_1__POR }, + { TOMTOM_A_CDC_MAD_ULTR_CTL_2, TOMTOM_A_CDC_MAD_ULTR_CTL_2__POR }, + { TOMTOM_A_CDC_MAD_ULTR_CTL_3, TOMTOM_A_CDC_MAD_ULTR_CTL_3__POR }, + { TOMTOM_A_CDC_MAD_ULTR_CTL_4, TOMTOM_A_CDC_MAD_ULTR_CTL_4__POR }, + { TOMTOM_A_CDC_MAD_ULTR_CTL_5, TOMTOM_A_CDC_MAD_ULTR_CTL_5__POR }, + { TOMTOM_A_CDC_MAD_ULTR_CTL_6, TOMTOM_A_CDC_MAD_ULTR_CTL_6__POR }, + { TOMTOM_A_CDC_MAD_ULTR_CTL_7, TOMTOM_A_CDC_MAD_ULTR_CTL_7__POR }, + { TOMTOM_A_CDC_MAD_BEACON_CTL_1, TOMTOM_A_CDC_MAD_BEACON_CTL_1__POR }, + { TOMTOM_A_CDC_MAD_BEACON_CTL_2, TOMTOM_A_CDC_MAD_BEACON_CTL_2__POR }, + { TOMTOM_A_CDC_MAD_BEACON_CTL_3, TOMTOM_A_CDC_MAD_BEACON_CTL_3__POR }, + { TOMTOM_A_CDC_MAD_BEACON_CTL_4, TOMTOM_A_CDC_MAD_BEACON_CTL_4__POR }, + { TOMTOM_A_CDC_MAD_BEACON_CTL_5, TOMTOM_A_CDC_MAD_BEACON_CTL_5__POR }, + { TOMTOM_A_CDC_MAD_BEACON_CTL_6, TOMTOM_A_CDC_MAD_BEACON_CTL_6__POR }, + { TOMTOM_A_CDC_MAD_BEACON_CTL_7, TOMTOM_A_CDC_MAD_BEACON_CTL_7__POR }, + { TOMTOM_A_CDC_MAD_BEACON_CTL_8, TOMTOM_A_CDC_MAD_BEACON_CTL_8__POR }, + { TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR, + TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR__POR }, + { TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL, + TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL__POR }, + { TOMTOM_A_CDC_MAD_INP_SEL, TOMTOM_A_CDC_MAD_INP_SEL__POR }, + { TOMTOM_A_BIAS_REF_CTL, TOMTOM_A_BIAS_REF_CTL__POR }, + { TOMTOM_A_BIAS_CENTRAL_BG_CTL, TOMTOM_A_BIAS_CENTRAL_BG_CTL__POR }, + { TOMTOM_A_BIAS_PRECHRG_CTL, TOMTOM_A_BIAS_PRECHRG_CTL__POR }, + { TOMTOM_A_BIAS_CURR_CTL_1, TOMTOM_A_BIAS_CURR_CTL_1__POR }, + { TOMTOM_A_BIAS_CURR_CTL_2, TOMTOM_A_BIAS_CURR_CTL_2__POR }, + { TOMTOM_A_BIAS_OSC_BG_CTL, TOMTOM_A_BIAS_OSC_BG_CTL__POR }, + { TOMTOM_A_CLK_BUFF_EN1, TOMTOM_A_CLK_BUFF_EN1__POR }, + { TOMTOM_A_CLK_BUFF_EN2, TOMTOM_A_CLK_BUFF_EN2__POR }, + { TOMTOM_A_LDO_L_MODE_1, TOMTOM_A_LDO_L_MODE_1__POR }, + { TOMTOM_A_LDO_L_MODE_2, TOMTOM_A_LDO_L_MODE_2__POR }, + { TOMTOM_A_LDO_L_CTRL_1, TOMTOM_A_LDO_L_CTRL_1__POR }, + { TOMTOM_A_LDO_L_CTRL_2, TOMTOM_A_LDO_L_CTRL_2__POR }, + { TOMTOM_A_LDO_L_CTRL_3, TOMTOM_A_LDO_L_CTRL_3__POR }, + { TOMTOM_A_LDO_L_CTRL_4, TOMTOM_A_LDO_L_CTRL_4__POR }, + { TOMTOM_A_LDO_H_MODE_1, TOMTOM_A_LDO_H_MODE_1__POR }, + { TOMTOM_A_LDO_H_MODE_2, TOMTOM_A_LDO_H_MODE_2__POR }, + { TOMTOM_A_LDO_H_LOOP_CTL, TOMTOM_A_LDO_H_LOOP_CTL__POR }, + { TOMTOM_A_LDO_H_COMP_1, TOMTOM_A_LDO_H_COMP_1__POR }, + { TOMTOM_A_LDO_H_COMP_2, TOMTOM_A_LDO_H_COMP_2__POR }, + { TOMTOM_A_LDO_H_BIAS_1, TOMTOM_A_LDO_H_BIAS_1__POR }, + { TOMTOM_A_LDO_H_BIAS_2, TOMTOM_A_LDO_H_BIAS_2__POR }, + { TOMTOM_A_LDO_H_BIAS_3, TOMTOM_A_LDO_H_BIAS_3__POR }, + { TOMTOM_A_VBAT_CLK, TOMTOM_A_VBAT_CLK__POR }, + { TOMTOM_A_VBAT_LOOP, TOMTOM_A_VBAT_LOOP__POR }, + { TOMTOM_A_VBAT_REF, TOMTOM_A_VBAT_REF__POR }, + { TOMTOM_A_VBAT_ADC_TEST, TOMTOM_A_VBAT_ADC_TEST__POR }, + { TOMTOM_A_VBAT_FE, TOMTOM_A_VBAT_FE__POR }, + { TOMTOM_A_VBAT_BIAS_1, TOMTOM_A_VBAT_BIAS_1__POR }, + { TOMTOM_A_VBAT_BIAS_2, TOMTOM_A_VBAT_BIAS_2__POR }, + { TOMTOM_A_VBAT_ADC_DATA_MSB, TOMTOM_A_VBAT_ADC_DATA_MSB__POR }, + { TOMTOM_A_VBAT_ADC_DATA_LSB, TOMTOM_A_VBAT_ADC_DATA_LSB__POR }, + { TOMTOM_A_FLL_NREF, TOMTOM_A_FLL_NREF__POR }, + { TOMTOM_A_FLL_KDCO_TUNE, TOMTOM_A_FLL_KDCO_TUNE__POR }, + { TOMTOM_A_FLL_LOCK_THRESH, TOMTOM_A_FLL_LOCK_THRESH__POR }, + { TOMTOM_A_FLL_LOCK_DET_COUNT, TOMTOM_A_FLL_LOCK_DET_COUNT__POR }, + { TOMTOM_A_FLL_DAC_THRESHOLD, TOMTOM_A_FLL_DAC_THRESHOLD__POR }, + { TOMTOM_A_FLL_TEST_DCO_FREERUN, TOMTOM_A_FLL_TEST_DCO_FREERUN__POR }, + { TOMTOM_A_FLL_TEST_ENABLE, TOMTOM_A_FLL_TEST_ENABLE__POR }, + { TOMTOM_A_MICB_CFILT_1_CTL, TOMTOM_A_MICB_CFILT_1_CTL__POR }, + { TOMTOM_A_MICB_CFILT_1_VAL, TOMTOM_A_MICB_CFILT_1_VAL__POR }, + { TOMTOM_A_MICB_CFILT_1_PRECHRG, TOMTOM_A_MICB_CFILT_1_PRECHRG__POR }, + { TOMTOM_A_MICB_1_CTL, TOMTOM_A_MICB_1_CTL__POR }, + { TOMTOM_A_MICB_1_INT_RBIAS, TOMTOM_A_MICB_1_INT_RBIAS__POR }, + { TOMTOM_A_MICB_1_MBHC, TOMTOM_A_MICB_1_MBHC__POR }, + { TOMTOM_A_MICB_CFILT_2_CTL, TOMTOM_A_MICB_CFILT_2_CTL__POR }, + { TOMTOM_A_MICB_CFILT_2_VAL, TOMTOM_A_MICB_CFILT_2_VAL__POR }, + { TOMTOM_A_MICB_CFILT_2_PRECHRG, TOMTOM_A_MICB_CFILT_2_PRECHRG__POR }, + { TOMTOM_A_MICB_2_CTL, TOMTOM_A_MICB_2_CTL__POR }, + { TOMTOM_A_MICB_2_INT_RBIAS, TOMTOM_A_MICB_2_INT_RBIAS__POR }, + { TOMTOM_A_MICB_2_MBHC, TOMTOM_A_MICB_2_MBHC__POR }, + { TOMTOM_A_MICB_CFILT_3_CTL, TOMTOM_A_MICB_CFILT_3_CTL__POR }, + { TOMTOM_A_MICB_CFILT_3_VAL, TOMTOM_A_MICB_CFILT_3_VAL__POR }, + { TOMTOM_A_MICB_CFILT_3_PRECHRG, TOMTOM_A_MICB_CFILT_3_PRECHRG__POR }, + { TOMTOM_A_MICB_3_CTL, TOMTOM_A_MICB_3_CTL__POR }, + { TOMTOM_A_MICB_3_INT_RBIAS, TOMTOM_A_MICB_3_INT_RBIAS__POR }, + { TOMTOM_A_MICB_3_MBHC, TOMTOM_A_MICB_3_MBHC__POR }, + { TOMTOM_A_MICB_4_CTL, TOMTOM_A_MICB_4_CTL__POR }, + { TOMTOM_A_MICB_4_INT_RBIAS, TOMTOM_A_MICB_4_INT_RBIAS__POR }, + { TOMTOM_A_MICB_4_MBHC, TOMTOM_A_MICB_4_MBHC__POR }, + { TOMTOM_A_SPKR_DRV2_EN, TOMTOM_A_SPKR_DRV2_EN__POR }, + { TOMTOM_A_SPKR_DRV2_GAIN, TOMTOM_A_SPKR_DRV2_GAIN__POR }, + { TOMTOM_A_SPKR_DRV2_DAC_CTL, TOMTOM_A_SPKR_DRV2_DAC_CTL__POR }, + { TOMTOM_A_SPKR_DRV2_OCP_CTL, TOMTOM_A_SPKR_DRV2_OCP_CTL__POR }, + { TOMTOM_A_SPKR_DRV2_CLIP_DET, TOMTOM_A_SPKR_DRV2_CLIP_DET__POR }, + { TOMTOM_A_SPKR_DRV2_DBG_DAC, TOMTOM_A_SPKR_DRV2_DBG_DAC__POR }, + { TOMTOM_A_SPKR_DRV2_DBG_PA, TOMTOM_A_SPKR_DRV2_DBG_PA__POR }, + { TOMTOM_A_SPKR_DRV2_DBG_PWRSTG, TOMTOM_A_SPKR_DRV2_DBG_PWRSTG__POR }, + { TOMTOM_A_SPKR_DRV2_BIAS_LDO, TOMTOM_A_SPKR_DRV2_BIAS_LDO__POR }, + { TOMTOM_A_SPKR_DRV2_BIAS_INT, TOMTOM_A_SPKR_DRV2_BIAS_INT__POR }, + { TOMTOM_A_SPKR_DRV2_BIAS_PA, TOMTOM_A_SPKR_DRV2_BIAS_PA__POR }, + { TOMTOM_A_SPKR_DRV2_STATUS_OCP, TOMTOM_A_SPKR_DRV2_STATUS_OCP__POR }, + { TOMTOM_A_SPKR_DRV2_STATUS_PA, TOMTOM_A_SPKR_DRV2_STATUS_PA__POR }, + { TOMTOM_A_MBHC_INSERT_DETECT, TOMTOM_A_MBHC_INSERT_DETECT__POR }, + { TOMTOM_A_MBHC_INSERT_DET_STATUS, + TOMTOM_A_MBHC_INSERT_DET_STATUS__POR }, + { TOMTOM_A_TX_COM_BIAS, TOMTOM_A_TX_COM_BIAS__POR }, + { TOMTOM_A_MBHC_INSERT_DETECT2, TOMTOM_A_MBHC_INSERT_DETECT2__POR }, + { TOMTOM_A_MBHC_SCALING_MUX_1, TOMTOM_A_MBHC_SCALING_MUX_1__POR }, + { TOMTOM_A_MBHC_SCALING_MUX_2, TOMTOM_A_MBHC_SCALING_MUX_2__POR }, + { TOMTOM_A_MAD_ANA_CTRL, TOMTOM_A_MAD_ANA_CTRL__POR }, + { TOMTOM_A_TX_SUP_SWITCH_CTRL_1, TOMTOM_A_TX_SUP_SWITCH_CTRL_1__POR }, + { TOMTOM_A_TX_SUP_SWITCH_CTRL_2, TOMTOM_A_TX_SUP_SWITCH_CTRL_2__POR }, + { TOMTOM_A_TX_1_GAIN, TOMTOM_A_TX_1_GAIN__POR }, + { TOMTOM_A_TX_1_2_TEST_EN, TOMTOM_A_TX_1_2_TEST_EN__POR }, + { TOMTOM_A_TX_2_GAIN, TOMTOM_A_TX_2_GAIN__POR }, + { TOMTOM_A_TX_1_2_ADC_IB, TOMTOM_A_TX_1_2_ADC_IB__POR }, + { TOMTOM_A_TX_1_2_ATEST_REFCTRL, TOMTOM_A_TX_1_2_ATEST_REFCTRL__POR }, + { TOMTOM_A_TX_1_2_TEST_CTL, TOMTOM_A_TX_1_2_TEST_CTL__POR }, + { TOMTOM_A_TX_1_2_TEST_BLOCK_EN, TOMTOM_A_TX_1_2_TEST_BLOCK_EN__POR }, + { TOMTOM_A_TX_1_2_TXFE_CLKDIV, TOMTOM_A_TX_1_2_TXFE_CLKDIV__POR }, + { TOMTOM_A_TX_1_2_SAR_ERR_CH1, TOMTOM_A_TX_1_2_SAR_ERR_CH1__POR }, + { TOMTOM_A_TX_1_2_SAR_ERR_CH2, TOMTOM_A_TX_1_2_SAR_ERR_CH2__POR }, + { TOMTOM_A_TX_3_GAIN, TOMTOM_A_TX_3_GAIN__POR }, + { TOMTOM_A_TX_3_4_TEST_EN, TOMTOM_A_TX_3_4_TEST_EN__POR }, + { TOMTOM_A_TX_4_GAIN, TOMTOM_A_TX_4_GAIN__POR }, + { TOMTOM_A_TX_3_4_ADC_IB, TOMTOM_A_TX_3_4_ADC_IB__POR }, + { TOMTOM_A_TX_3_4_ATEST_REFCTRL, TOMTOM_A_TX_3_4_ATEST_REFCTRL__POR }, + { TOMTOM_A_TX_3_4_TEST_CTL, TOMTOM_A_TX_3_4_TEST_CTL__POR }, + { TOMTOM_A_TX_3_4_TEST_BLOCK_EN, TOMTOM_A_TX_3_4_TEST_BLOCK_EN__POR }, + { TOMTOM_A_TX_3_4_TXFE_CKDIV, TOMTOM_A_TX_3_4_TXFE_CKDIV__POR }, + { TOMTOM_A_TX_3_4_SAR_ERR_CH3, TOMTOM_A_TX_3_4_SAR_ERR_CH3__POR }, + { TOMTOM_A_TX_3_4_SAR_ERR_CH4, TOMTOM_A_TX_3_4_SAR_ERR_CH4__POR }, + { TOMTOM_A_TX_5_GAIN, TOMTOM_A_TX_5_GAIN__POR }, + { TOMTOM_A_TX_5_6_TEST_EN, TOMTOM_A_TX_5_6_TEST_EN__POR }, + { TOMTOM_A_TX_6_GAIN, TOMTOM_A_TX_6_GAIN__POR }, + { TOMTOM_A_TX_5_6_ADC_IB, TOMTOM_A_TX_5_6_ADC_IB__POR }, + { TOMTOM_A_TX_5_6_ATEST_REFCTRL, TOMTOM_A_TX_5_6_ATEST_REFCTRL__POR }, + { TOMTOM_A_TX_5_6_TEST_CTL, TOMTOM_A_TX_5_6_TEST_CTL__POR }, + { TOMTOM_A_TX_5_6_TEST_BLOCK_EN, TOMTOM_A_TX_5_6_TEST_BLOCK_EN__POR }, + { TOMTOM_A_TX_5_6_TXFE_CKDIV, TOMTOM_A_TX_5_6_TXFE_CKDIV__POR }, + { TOMTOM_A_TX_5_6_SAR_ERR_CH5, TOMTOM_A_TX_5_6_SAR_ERR_CH5__POR }, + { TOMTOM_A_TX_5_6_SAR_ERR_CH6, TOMTOM_A_TX_5_6_SAR_ERR_CH6__POR }, + { TOMTOM_A_TX_7_MBHC_EN, TOMTOM_A_TX_7_MBHC_EN__POR }, + { TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL, + TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL__POR }, + { TOMTOM_A_TX_7_MBHC_ADC, TOMTOM_A_TX_7_MBHC_ADC__POR }, + { TOMTOM_A_TX_7_MBHC_TEST_CTL, TOMTOM_A_TX_7_MBHC_TEST_CTL__POR }, + { TOMTOM_A_TX_7_MBHC_SAR_ERR, TOMTOM_A_TX_7_MBHC_SAR_ERR__POR }, + { TOMTOM_A_TX_7_TXFE_CLKDIV, TOMTOM_A_TX_7_TXFE_CLKDIV__POR }, + { TOMTOM_A_RCO_CTRL, TOMTOM_A_RCO_CTRL__POR }, + { TOMTOM_A_RCO_CALIBRATION_CTRL1, TOMTOM_A_RCO_CALIBRATION_CTRL1__POR }, + { TOMTOM_A_RCO_CALIBRATION_CTRL2, TOMTOM_A_RCO_CALIBRATION_CTRL2__POR }, + { TOMTOM_A_RCO_CALIBRATION_CTRL3, TOMTOM_A_RCO_CALIBRATION_CTRL3__POR }, + { TOMTOM_A_RCO_TEST_CTRL, TOMTOM_A_RCO_TEST_CTRL__POR }, + { TOMTOM_A_RCO_CALIBRATION_RESULT1, + TOMTOM_A_RCO_CALIBRATION_RESULT1__POR }, + { TOMTOM_A_RCO_CALIBRATION_RESULT2, + TOMTOM_A_RCO_CALIBRATION_RESULT2__POR }, + { TOMTOM_A_BUCK_MODE_1, TOMTOM_A_BUCK_MODE_1__POR }, + { TOMTOM_A_BUCK_MODE_2, TOMTOM_A_BUCK_MODE_2__POR }, + { TOMTOM_A_BUCK_MODE_3, TOMTOM_A_BUCK_MODE_3__POR }, + { TOMTOM_A_BUCK_MODE_4, TOMTOM_A_BUCK_MODE_4__POR }, + { TOMTOM_A_BUCK_MODE_5, TOMTOM_A_BUCK_MODE_5__POR }, + { TOMTOM_A_BUCK_CTRL_VCL_1, TOMTOM_A_BUCK_CTRL_VCL_1__POR }, + { TOMTOM_A_BUCK_CTRL_VCL_2, TOMTOM_A_BUCK_CTRL_VCL_2__POR }, + { TOMTOM_A_BUCK_CTRL_VCL_3, TOMTOM_A_BUCK_CTRL_VCL_3__POR }, + { TOMTOM_A_BUCK_CTRL_CCL_1, TOMTOM_A_BUCK_CTRL_CCL_1__POR }, + { TOMTOM_A_BUCK_CTRL_CCL_2, TOMTOM_A_BUCK_CTRL_CCL_2__POR }, + { TOMTOM_A_BUCK_CTRL_CCL_3, TOMTOM_A_BUCK_CTRL_CCL_3__POR }, + { TOMTOM_A_BUCK_CTRL_CCL_4, TOMTOM_A_BUCK_CTRL_CCL_4__POR }, + { TOMTOM_A_BUCK_CTRL_PWM_DRVR_1, TOMTOM_A_BUCK_CTRL_PWM_DRVR_1__POR }, + { TOMTOM_A_BUCK_CTRL_PWM_DRVR_2, TOMTOM_A_BUCK_CTRL_PWM_DRVR_2__POR }, + { TOMTOM_A_BUCK_CTRL_PWM_DRVR_3, TOMTOM_A_BUCK_CTRL_PWM_DRVR_3__POR }, + { TOMTOM_A_BUCK_TMUX_A_D, TOMTOM_A_BUCK_TMUX_A_D__POR }, + { TOMTOM_A_NCP_BUCKREF, TOMTOM_A_NCP_BUCKREF__POR }, + { TOMTOM_A_NCP_EN, TOMTOM_A_NCP_EN__POR }, + { TOMTOM_A_NCP_CLK, TOMTOM_A_NCP_CLK__POR }, + { TOMTOM_A_NCP_STATIC, TOMTOM_A_NCP_STATIC__POR }, + { TOMTOM_A_NCP_VTH_LOW, TOMTOM_A_NCP_VTH_LOW__POR }, + { TOMTOM_A_NCP_VTH_HIGH, TOMTOM_A_NCP_VTH_HIGH__POR }, + { TOMTOM_A_NCP_ATEST, TOMTOM_A_NCP_ATEST__POR }, + { TOMTOM_A_NCP_DTEST, TOMTOM_A_NCP_DTEST__POR }, + { TOMTOM_A_NCP_DLY1, TOMTOM_A_NCP_DLY1__POR }, + { TOMTOM_A_NCP_DLY2, TOMTOM_A_NCP_DLY2__POR }, + { TOMTOM_A_RX_AUX_SW_CTL, TOMTOM_A_RX_AUX_SW_CTL__POR }, + { TOMTOM_A_RX_PA_AUX_IN_CONN, TOMTOM_A_RX_PA_AUX_IN_CONN__POR }, + { TOMTOM_A_RX_COM_TIMER_DIV, TOMTOM_A_RX_COM_TIMER_DIV__POR }, + { TOMTOM_A_RX_COM_OCP_CTL, TOMTOM_A_RX_COM_OCP_CTL__POR }, + { TOMTOM_A_RX_COM_OCP_COUNT, TOMTOM_A_RX_COM_OCP_COUNT__POR }, + { TOMTOM_A_RX_COM_DAC_CTL, TOMTOM_A_RX_COM_DAC_CTL__POR }, + { TOMTOM_A_RX_COM_BIAS, TOMTOM_A_RX_COM_BIAS__POR }, + { TOMTOM_A_RX_HPH_AUTO_CHOP, TOMTOM_A_RX_HPH_AUTO_CHOP__POR }, + { TOMTOM_A_RX_HPH_CHOP_CTL, TOMTOM_A_RX_HPH_CHOP_CTL__POR }, + { TOMTOM_A_RX_HPH_BIAS_PA, TOMTOM_A_RX_HPH_BIAS_PA__POR }, + { TOMTOM_A_RX_HPH_BIAS_LDO, TOMTOM_A_RX_HPH_BIAS_LDO__POR }, + { TOMTOM_A_RX_HPH_BIAS_CNP, TOMTOM_A_RX_HPH_BIAS_CNP__POR }, + { TOMTOM_A_RX_HPH_BIAS_WG_OCP, TOMTOM_A_RX_HPH_BIAS_WG_OCP__POR }, + { TOMTOM_A_RX_HPH_OCP_CTL, TOMTOM_A_RX_HPH_OCP_CTL__POR }, + { TOMTOM_A_RX_HPH_CNP_EN, TOMTOM_A_RX_HPH_CNP_EN__POR }, + { TOMTOM_A_RX_HPH_CNP_WG_CTL, TOMTOM_A_RX_HPH_CNP_WG_CTL__POR }, + { TOMTOM_A_RX_HPH_CNP_WG_TIME, TOMTOM_A_RX_HPH_CNP_WG_TIME__POR }, + { TOMTOM_A_RX_HPH_L_GAIN, TOMTOM_A_RX_HPH_L_GAIN__POR }, + { TOMTOM_A_RX_HPH_L_TEST, TOMTOM_A_RX_HPH_L_TEST__POR }, + { TOMTOM_A_RX_HPH_L_PA_CTL, TOMTOM_A_RX_HPH_L_PA_CTL__POR }, + { TOMTOM_A_RX_HPH_L_DAC_CTL, TOMTOM_A_RX_HPH_L_DAC_CTL__POR }, + { TOMTOM_A_RX_HPH_L_ATEST, TOMTOM_A_RX_HPH_L_ATEST__POR }, + { TOMTOM_A_RX_HPH_L_STATUS, TOMTOM_A_RX_HPH_L_STATUS__POR }, + { TOMTOM_A_RX_HPH_R_GAIN, TOMTOM_A_RX_HPH_R_GAIN__POR }, + { TOMTOM_A_RX_HPH_R_TEST, TOMTOM_A_RX_HPH_R_TEST__POR }, + { TOMTOM_A_RX_HPH_R_PA_CTL, TOMTOM_A_RX_HPH_R_PA_CTL__POR }, + { TOMTOM_A_RX_HPH_R_DAC_CTL, TOMTOM_A_RX_HPH_R_DAC_CTL__POR }, + { TOMTOM_A_RX_HPH_R_ATEST, TOMTOM_A_RX_HPH_R_ATEST__POR }, + { TOMTOM_A_RX_HPH_R_STATUS, TOMTOM_A_RX_HPH_R_STATUS__POR }, + { TOMTOM_A_RX_EAR_BIAS_PA, TOMTOM_A_RX_EAR_BIAS_PA__POR }, + { TOMTOM_A_RX_EAR_BIAS_CMBUFF, TOMTOM_A_RX_EAR_BIAS_CMBUFF__POR }, + { TOMTOM_A_RX_EAR_EN, TOMTOM_A_RX_EAR_EN__POR }, + { TOMTOM_A_RX_EAR_GAIN, TOMTOM_A_RX_EAR_GAIN__POR }, + { TOMTOM_A_RX_EAR_CMBUFF, TOMTOM_A_RX_EAR_CMBUFF__POR }, + { TOMTOM_A_RX_EAR_ICTL, TOMTOM_A_RX_EAR_ICTL__POR }, + { TOMTOM_A_RX_EAR_CCOMP, TOMTOM_A_RX_EAR_CCOMP__POR }, + { TOMTOM_A_RX_EAR_VCM, TOMTOM_A_RX_EAR_VCM__POR }, + { TOMTOM_A_RX_EAR_CNP, TOMTOM_A_RX_EAR_CNP__POR }, + { TOMTOM_A_RX_EAR_DAC_CTL_ATEST, TOMTOM_A_RX_EAR_DAC_CTL_ATEST__POR }, + { TOMTOM_A_RX_EAR_STATUS, TOMTOM_A_RX_EAR_STATUS__POR }, + { TOMTOM_A_RX_LINE_BIAS_PA, TOMTOM_A_RX_LINE_BIAS_PA__POR }, + { TOMTOM_A_RX_BUCK_BIAS1, TOMTOM_A_RX_BUCK_BIAS1__POR }, + { TOMTOM_A_RX_BUCK_BIAS2, TOMTOM_A_RX_BUCK_BIAS2__POR }, + { TOMTOM_A_RX_LINE_COM, TOMTOM_A_RX_LINE_COM__POR }, + { TOMTOM_A_RX_LINE_CNP_EN, TOMTOM_A_RX_LINE_CNP_EN__POR }, + { TOMTOM_A_RX_LINE_CNP_WG_CTL, TOMTOM_A_RX_LINE_CNP_WG_CTL__POR }, + { TOMTOM_A_RX_LINE_CNP_WG_TIME, TOMTOM_A_RX_LINE_CNP_WG_TIME__POR }, + { TOMTOM_A_RX_LINE_1_GAIN, TOMTOM_A_RX_LINE_1_GAIN__POR }, + { TOMTOM_A_RX_LINE_1_TEST, TOMTOM_A_RX_LINE_1_TEST__POR }, + { TOMTOM_A_RX_LINE_1_DAC_CTL, TOMTOM_A_RX_LINE_1_DAC_CTL__POR }, + { TOMTOM_A_RX_LINE_1_STATUS, TOMTOM_A_RX_LINE_1_STATUS__POR }, + { TOMTOM_A_RX_LINE_2_GAIN, TOMTOM_A_RX_LINE_2_GAIN__POR }, + { TOMTOM_A_RX_LINE_2_TEST, TOMTOM_A_RX_LINE_2_TEST__POR }, + { TOMTOM_A_RX_LINE_2_DAC_CTL, TOMTOM_A_RX_LINE_2_DAC_CTL__POR }, + { TOMTOM_A_RX_LINE_2_STATUS, TOMTOM_A_RX_LINE_2_STATUS__POR }, + { TOMTOM_A_RX_LINE_3_GAIN, TOMTOM_A_RX_LINE_3_GAIN__POR }, + { TOMTOM_A_RX_LINE_3_TEST, TOMTOM_A_RX_LINE_3_TEST__POR }, + { TOMTOM_A_RX_LINE_3_DAC_CTL, TOMTOM_A_RX_LINE_3_DAC_CTL__POR }, + { TOMTOM_A_RX_LINE_3_STATUS, TOMTOM_A_RX_LINE_3_STATUS__POR }, + { TOMTOM_A_RX_LINE_4_GAIN, TOMTOM_A_RX_LINE_4_GAIN__POR }, + { TOMTOM_A_RX_LINE_4_TEST, TOMTOM_A_RX_LINE_4_TEST__POR }, + { TOMTOM_A_RX_LINE_4_DAC_CTL, TOMTOM_A_RX_LINE_4_DAC_CTL__POR }, + { TOMTOM_A_RX_LINE_4_STATUS, TOMTOM_A_RX_LINE_4_STATUS__POR }, + { TOMTOM_A_RX_LINE_CNP_DBG, TOMTOM_A_RX_LINE_CNP_DBG__POR }, + { TOMTOM_A_SPKR_DRV1_EN, TOMTOM_A_SPKR_DRV1_EN__POR }, + { TOMTOM_A_SPKR_DRV1_GAIN, TOMTOM_A_SPKR_DRV1_GAIN__POR }, + { TOMTOM_A_SPKR_DRV1_DAC_CTL, TOMTOM_A_SPKR_DRV1_DAC_CTL__POR }, + { TOMTOM_A_SPKR_DRV1_OCP_CTL, TOMTOM_A_SPKR_DRV1_OCP_CTL__POR }, + { TOMTOM_A_SPKR_DRV1_CLIP_DET, TOMTOM_A_SPKR_DRV1_CLIP_DET__POR }, + { TOMTOM_A_SPKR_DRV1_IEC, TOMTOM_A_SPKR_DRV1_IEC__POR }, + { TOMTOM_A_SPKR_DRV1_DBG_DAC, TOMTOM_A_SPKR_DRV1_DBG_DAC__POR }, + { TOMTOM_A_SPKR_DRV1_DBG_PA, TOMTOM_A_SPKR_DRV1_DBG_PA__POR }, + { TOMTOM_A_SPKR_DRV1_DBG_PWRSTG, TOMTOM_A_SPKR_DRV1_DBG_PWRSTG__POR }, + { TOMTOM_A_SPKR_DRV1_BIAS_LDO, TOMTOM_A_SPKR_DRV1_BIAS_LDO__POR }, + { TOMTOM_A_SPKR_DRV1_BIAS_INT, TOMTOM_A_SPKR_DRV1_BIAS_INT__POR }, + { TOMTOM_A_SPKR_DRV1_BIAS_PA, TOMTOM_A_SPKR_DRV1_BIAS_PA__POR }, + { TOMTOM_A_SPKR_DRV1_STATUS_OCP, TOMTOM_A_SPKR_DRV1_STATUS_OCP__POR }, + { TOMTOM_A_SPKR_DRV1_STATUS_PA, TOMTOM_A_SPKR_DRV1_STATUS_PA__POR }, + { TOMTOM_A_SPKR1_PROT_EN, TOMTOM_A_SPKR1_PROT_EN__POR }, + { TOMTOM_A_SPKR1_PROT_ADC_TEST_EN, + TOMTOM_A_SPKR1_PROT_ADC_TEST_EN__POR }, + { TOMTOM_A_SPKR1_PROT_ATEST, TOMTOM_A_SPKR1_PROT_ATEST__POR }, + { TOMTOM_A_SPKR1_PROT_LDO_CTRL, TOMTOM_A_SPKR1_PROT_LDO_CTRL__POR }, + { TOMTOM_A_SPKR1_PROT_ISENSE_CTRL, + TOMTOM_A_SPKR1_PROT_ISENSE_CTRL__POR }, + { TOMTOM_A_SPKR1_PROT_VSENSE_CTRL, + TOMTOM_A_SPKR1_PROT_VSENSE_CTRL__POR }, + { TOMTOM_A_SPKR2_PROT_EN, TOMTOM_A_SPKR2_PROT_EN__POR }, + { TOMTOM_A_SPKR2_PROT_ADC_TEST_EN, + TOMTOM_A_SPKR2_PROT_ADC_TEST_EN__POR }, + { TOMTOM_A_SPKR2_PROT_ATEST, TOMTOM_A_SPKR2_PROT_ATEST__POR }, + { TOMTOM_A_SPKR2_PROT_LDO_CTRL, TOMTOM_A_SPKR2_PROT_LDO_CTRL__POR }, + { TOMTOM_A_SPKR2_PROT_ISENSE_CTRL, + TOMTOM_A_SPKR2_PROT_ISENSE_CTRL__POR }, + { TOMTOM_A_SPKR2_PROT_VSENSE_CTRL, + TOMTOM_A_SPKR2_PROT_VSENSE_CTRL__POR }, + { TOMTOM_A_MBHC_HPH, TOMTOM_A_MBHC_HPH__POR }, + { TOMTOM_A_CDC_ANC1_B1_CTL, TOMTOM_A_CDC_ANC1_B1_CTL__POR }, + { TOMTOM_A_CDC_ANC2_B1_CTL, TOMTOM_A_CDC_ANC2_B1_CTL__POR }, + { TOMTOM_A_CDC_ANC1_SHIFT, TOMTOM_A_CDC_ANC1_SHIFT__POR }, + { TOMTOM_A_CDC_ANC2_SHIFT, TOMTOM_A_CDC_ANC2_SHIFT__POR }, + { TOMTOM_A_CDC_ANC1_IIR_B1_CTL, TOMTOM_A_CDC_ANC1_IIR_B1_CTL__POR }, + { TOMTOM_A_CDC_ANC2_IIR_B1_CTL, TOMTOM_A_CDC_ANC2_IIR_B1_CTL__POR }, + { TOMTOM_A_CDC_ANC1_IIR_B2_CTL, TOMTOM_A_CDC_ANC1_IIR_B2_CTL__POR }, + { TOMTOM_A_CDC_ANC2_IIR_B2_CTL, TOMTOM_A_CDC_ANC2_IIR_B2_CTL__POR }, + { TOMTOM_A_CDC_ANC1_IIR_B3_CTL, TOMTOM_A_CDC_ANC1_IIR_B3_CTL__POR }, + { TOMTOM_A_CDC_ANC2_IIR_B3_CTL, TOMTOM_A_CDC_ANC2_IIR_B3_CTL__POR }, + { TOMTOM_A_CDC_ANC1_LPF_B1_CTL, TOMTOM_A_CDC_ANC1_LPF_B1_CTL__POR }, + { TOMTOM_A_CDC_ANC2_LPF_B1_CTL, TOMTOM_A_CDC_ANC2_LPF_B1_CTL__POR }, + { TOMTOM_A_CDC_ANC1_LPF_B2_CTL, TOMTOM_A_CDC_ANC1_LPF_B2_CTL__POR }, + { TOMTOM_A_CDC_ANC2_LPF_B2_CTL, TOMTOM_A_CDC_ANC2_LPF_B2_CTL__POR }, + { TOMTOM_A_CDC_ANC1_SPARE, TOMTOM_A_CDC_ANC1_SPARE__POR }, + { TOMTOM_A_CDC_ANC2_SPARE, TOMTOM_A_CDC_ANC2_SPARE__POR }, + { TOMTOM_A_CDC_ANC1_SMLPF_CTL, TOMTOM_A_CDC_ANC1_SMLPF_CTL__POR }, + { TOMTOM_A_CDC_ANC2_SMLPF_CTL, TOMTOM_A_CDC_ANC2_SMLPF_CTL__POR }, + { TOMTOM_A_CDC_ANC1_DCFLT_CTL, TOMTOM_A_CDC_ANC1_DCFLT_CTL__POR }, + { TOMTOM_A_CDC_ANC2_DCFLT_CTL, TOMTOM_A_CDC_ANC2_DCFLT_CTL__POR }, + { TOMTOM_A_CDC_ANC1_GAIN_CTL, TOMTOM_A_CDC_ANC1_GAIN_CTL__POR }, + { TOMTOM_A_CDC_ANC2_GAIN_CTL, TOMTOM_A_CDC_ANC2_GAIN_CTL__POR }, + { TOMTOM_A_CDC_ANC1_B2_CTL, TOMTOM_A_CDC_ANC1_B2_CTL__POR }, + { TOMTOM_A_CDC_ANC2_B2_CTL, TOMTOM_A_CDC_ANC2_B2_CTL__POR }, + { TOMTOM_A_CDC_TX1_VOL_CTL_TIMER, TOMTOM_A_CDC_TX1_VOL_CTL_TIMER__POR }, + { TOMTOM_A_CDC_TX2_VOL_CTL_TIMER, TOMTOM_A_CDC_TX2_VOL_CTL_TIMER__POR }, + { TOMTOM_A_CDC_TX3_VOL_CTL_TIMER, TOMTOM_A_CDC_TX3_VOL_CTL_TIMER__POR }, + { TOMTOM_A_CDC_TX4_VOL_CTL_TIMER, TOMTOM_A_CDC_TX4_VOL_CTL_TIMER__POR }, + { TOMTOM_A_CDC_TX5_VOL_CTL_TIMER, TOMTOM_A_CDC_TX5_VOL_CTL_TIMER__POR }, + { TOMTOM_A_CDC_TX6_VOL_CTL_TIMER, TOMTOM_A_CDC_TX6_VOL_CTL_TIMER__POR }, + { TOMTOM_A_CDC_TX7_VOL_CTL_TIMER, TOMTOM_A_CDC_TX7_VOL_CTL_TIMER__POR }, + { TOMTOM_A_CDC_TX8_VOL_CTL_TIMER, TOMTOM_A_CDC_TX8_VOL_CTL_TIMER__POR }, + { TOMTOM_A_CDC_TX9_VOL_CTL_TIMER, TOMTOM_A_CDC_TX9_VOL_CTL_TIMER__POR }, + { TOMTOM_A_CDC_TX10_VOL_CTL_TIMER, + TOMTOM_A_CDC_TX10_VOL_CTL_TIMER__POR }, + { TOMTOM_A_CDC_TX1_VOL_CTL_GAIN, TOMTOM_A_CDC_TX1_VOL_CTL_GAIN__POR }, + { TOMTOM_A_CDC_TX2_VOL_CTL_GAIN, TOMTOM_A_CDC_TX2_VOL_CTL_GAIN__POR }, + { TOMTOM_A_CDC_TX3_VOL_CTL_GAIN, TOMTOM_A_CDC_TX3_VOL_CTL_GAIN__POR }, + { TOMTOM_A_CDC_TX4_VOL_CTL_GAIN, TOMTOM_A_CDC_TX4_VOL_CTL_GAIN__POR }, + { TOMTOM_A_CDC_TX5_VOL_CTL_GAIN, TOMTOM_A_CDC_TX5_VOL_CTL_GAIN__POR }, + { TOMTOM_A_CDC_TX6_VOL_CTL_GAIN, TOMTOM_A_CDC_TX6_VOL_CTL_GAIN__POR }, + { TOMTOM_A_CDC_TX7_VOL_CTL_GAIN, TOMTOM_A_CDC_TX7_VOL_CTL_GAIN__POR }, + { TOMTOM_A_CDC_TX8_VOL_CTL_GAIN, TOMTOM_A_CDC_TX8_VOL_CTL_GAIN__POR }, + { TOMTOM_A_CDC_TX9_VOL_CTL_GAIN, TOMTOM_A_CDC_TX9_VOL_CTL_GAIN__POR }, + { TOMTOM_A_CDC_TX10_VOL_CTL_GAIN, TOMTOM_A_CDC_TX10_VOL_CTL_GAIN__POR }, + { TOMTOM_A_CDC_TX1_VOL_CTL_CFG, TOMTOM_A_CDC_TX1_VOL_CTL_CFG__POR }, + { TOMTOM_A_CDC_TX2_VOL_CTL_CFG, TOMTOM_A_CDC_TX2_VOL_CTL_CFG__POR }, + { TOMTOM_A_CDC_TX3_VOL_CTL_CFG, TOMTOM_A_CDC_TX3_VOL_CTL_CFG__POR }, + { TOMTOM_A_CDC_TX4_VOL_CTL_CFG, TOMTOM_A_CDC_TX4_VOL_CTL_CFG__POR }, + { TOMTOM_A_CDC_TX5_VOL_CTL_CFG, TOMTOM_A_CDC_TX5_VOL_CTL_CFG__POR }, + { TOMTOM_A_CDC_TX6_VOL_CTL_CFG, TOMTOM_A_CDC_TX6_VOL_CTL_CFG__POR }, + { TOMTOM_A_CDC_TX7_VOL_CTL_CFG, TOMTOM_A_CDC_TX7_VOL_CTL_CFG__POR }, + { TOMTOM_A_CDC_TX8_VOL_CTL_CFG, TOMTOM_A_CDC_TX8_VOL_CTL_CFG__POR }, + { TOMTOM_A_CDC_TX9_VOL_CTL_CFG, TOMTOM_A_CDC_TX9_VOL_CTL_CFG__POR }, + { TOMTOM_A_CDC_TX10_VOL_CTL_CFG, TOMTOM_A_CDC_TX10_VOL_CTL_CFG__POR }, + { TOMTOM_A_CDC_TX1_MUX_CTL, TOMTOM_A_CDC_TX1_MUX_CTL__POR }, + { TOMTOM_A_CDC_TX2_MUX_CTL, TOMTOM_A_CDC_TX2_MUX_CTL__POR }, + { TOMTOM_A_CDC_TX3_MUX_CTL, TOMTOM_A_CDC_TX3_MUX_CTL__POR }, + { TOMTOM_A_CDC_TX4_MUX_CTL, TOMTOM_A_CDC_TX4_MUX_CTL__POR }, + { TOMTOM_A_CDC_TX5_MUX_CTL, TOMTOM_A_CDC_TX5_MUX_CTL__POR }, + { TOMTOM_A_CDC_TX6_MUX_CTL, TOMTOM_A_CDC_TX6_MUX_CTL__POR }, + { TOMTOM_A_CDC_TX7_MUX_CTL, TOMTOM_A_CDC_TX7_MUX_CTL__POR }, + { TOMTOM_A_CDC_TX8_MUX_CTL, TOMTOM_A_CDC_TX8_MUX_CTL__POR }, + { TOMTOM_A_CDC_TX9_MUX_CTL, TOMTOM_A_CDC_TX9_MUX_CTL__POR }, + { TOMTOM_A_CDC_TX10_MUX_CTL, TOMTOM_A_CDC_TX10_MUX_CTL__POR }, + { TOMTOM_A_CDC_TX1_CLK_FS_CTL, TOMTOM_A_CDC_TX1_CLK_FS_CTL__POR }, + { TOMTOM_A_CDC_TX2_CLK_FS_CTL, TOMTOM_A_CDC_TX2_CLK_FS_CTL__POR }, + { TOMTOM_A_CDC_TX3_CLK_FS_CTL, TOMTOM_A_CDC_TX3_CLK_FS_CTL__POR }, + { TOMTOM_A_CDC_TX4_CLK_FS_CTL, TOMTOM_A_CDC_TX4_CLK_FS_CTL__POR }, + { TOMTOM_A_CDC_TX5_CLK_FS_CTL, TOMTOM_A_CDC_TX5_CLK_FS_CTL__POR }, + { TOMTOM_A_CDC_TX6_CLK_FS_CTL, TOMTOM_A_CDC_TX6_CLK_FS_CTL__POR }, + { TOMTOM_A_CDC_TX7_CLK_FS_CTL, TOMTOM_A_CDC_TX7_CLK_FS_CTL__POR }, + { TOMTOM_A_CDC_TX8_CLK_FS_CTL, TOMTOM_A_CDC_TX8_CLK_FS_CTL__POR }, + { TOMTOM_A_CDC_TX9_CLK_FS_CTL, TOMTOM_A_CDC_TX9_CLK_FS_CTL__POR }, + { TOMTOM_A_CDC_TX10_CLK_FS_CTL, TOMTOM_A_CDC_TX10_CLK_FS_CTL__POR }, + { TOMTOM_A_CDC_TX1_DMIC_CTL, TOMTOM_A_CDC_TX1_DMIC_CTL__POR }, + { TOMTOM_A_CDC_TX2_DMIC_CTL, TOMTOM_A_CDC_TX2_DMIC_CTL__POR }, + { TOMTOM_A_CDC_TX3_DMIC_CTL, TOMTOM_A_CDC_TX3_DMIC_CTL__POR }, + { TOMTOM_A_CDC_TX4_DMIC_CTL, TOMTOM_A_CDC_TX4_DMIC_CTL__POR }, + { TOMTOM_A_CDC_TX5_DMIC_CTL, TOMTOM_A_CDC_TX5_DMIC_CTL__POR }, + { TOMTOM_A_CDC_TX6_DMIC_CTL, TOMTOM_A_CDC_TX6_DMIC_CTL__POR }, + { TOMTOM_A_CDC_TX7_DMIC_CTL, TOMTOM_A_CDC_TX7_DMIC_CTL__POR }, + { TOMTOM_A_CDC_TX8_DMIC_CTL, TOMTOM_A_CDC_TX8_DMIC_CTL__POR }, + { TOMTOM_A_CDC_TX9_DMIC_CTL, TOMTOM_A_CDC_TX9_DMIC_CTL__POR }, + { TOMTOM_A_CDC_TX10_DMIC_CTL, TOMTOM_A_CDC_TX10_DMIC_CTL__POR }, + { TOMTOM_A_CDC_SPKR_CLIPDET_VAL0, TOMTOM_A_CDC_SPKR_CLIPDET_VAL0__POR }, + { TOMTOM_A_CDC_SPKR_CLIPDET_VAL1, TOMTOM_A_CDC_SPKR_CLIPDET_VAL1__POR }, + { TOMTOM_A_CDC_SPKR_CLIPDET_VAL2, TOMTOM_A_CDC_SPKR_CLIPDET_VAL2__POR }, + { TOMTOM_A_CDC_SPKR_CLIPDET_VAL3, TOMTOM_A_CDC_SPKR_CLIPDET_VAL3__POR }, + { TOMTOM_A_CDC_SPKR_CLIPDET_VAL4, TOMTOM_A_CDC_SPKR_CLIPDET_VAL4__POR }, + { TOMTOM_A_CDC_SPKR_CLIPDET_VAL5, TOMTOM_A_CDC_SPKR_CLIPDET_VAL5__POR }, + { TOMTOM_A_CDC_SPKR_CLIPDET_VAL6, TOMTOM_A_CDC_SPKR_CLIPDET_VAL6__POR }, + { TOMTOM_A_CDC_SPKR_CLIPDET_VAL7, TOMTOM_A_CDC_SPKR_CLIPDET_VAL7__POR }, + { TOMTOM_A_CDC_DEBUG_B1_CTL, TOMTOM_A_CDC_DEBUG_B1_CTL__POR }, + { TOMTOM_A_CDC_DEBUG_B2_CTL, TOMTOM_A_CDC_DEBUG_B2_CTL__POR }, + { TOMTOM_A_CDC_DEBUG_B3_CTL, TOMTOM_A_CDC_DEBUG_B3_CTL__POR }, + { TOMTOM_A_CDC_DEBUG_B4_CTL, TOMTOM_A_CDC_DEBUG_B4_CTL__POR }, + { TOMTOM_A_CDC_DEBUG_B5_CTL, TOMTOM_A_CDC_DEBUG_B5_CTL__POR }, + { TOMTOM_A_CDC_DEBUG_B6_CTL, TOMTOM_A_CDC_DEBUG_B6_CTL__POR }, + { TOMTOM_A_CDC_DEBUG_B7_CTL, TOMTOM_A_CDC_DEBUG_B7_CTL__POR }, + { TOMTOM_A_CDC_SRC1_PDA_CFG, TOMTOM_A_CDC_SRC1_PDA_CFG__POR }, + { TOMTOM_A_CDC_SRC2_PDA_CFG, TOMTOM_A_CDC_SRC2_PDA_CFG__POR }, + { TOMTOM_A_CDC_SRC1_FS_CTL, TOMTOM_A_CDC_SRC1_FS_CTL__POR }, + { TOMTOM_A_CDC_SRC2_FS_CTL, TOMTOM_A_CDC_SRC2_FS_CTL__POR }, + { TOMTOM_A_CDC_RX1_B1_CTL, TOMTOM_A_CDC_RX1_B1_CTL__POR }, + { TOMTOM_A_CDC_RX2_B1_CTL, TOMTOM_A_CDC_RX2_B1_CTL__POR }, + { TOMTOM_A_CDC_RX3_B1_CTL, TOMTOM_A_CDC_RX3_B1_CTL__POR }, + { TOMTOM_A_CDC_RX4_B1_CTL, TOMTOM_A_CDC_RX4_B1_CTL__POR }, + { TOMTOM_A_CDC_RX5_B1_CTL, TOMTOM_A_CDC_RX5_B1_CTL__POR }, + { TOMTOM_A_CDC_RX6_B1_CTL, TOMTOM_A_CDC_RX6_B1_CTL__POR }, + { TOMTOM_A_CDC_RX7_B1_CTL, TOMTOM_A_CDC_RX7_B1_CTL__POR }, + { TOMTOM_A_CDC_RX1_B2_CTL, TOMTOM_A_CDC_RX1_B2_CTL__POR }, + { TOMTOM_A_CDC_RX2_B2_CTL, TOMTOM_A_CDC_RX2_B2_CTL__POR }, + { TOMTOM_A_CDC_RX3_B2_CTL, TOMTOM_A_CDC_RX3_B2_CTL__POR }, + { TOMTOM_A_CDC_RX4_B2_CTL, TOMTOM_A_CDC_RX4_B2_CTL__POR }, + { TOMTOM_A_CDC_RX5_B2_CTL, TOMTOM_A_CDC_RX5_B2_CTL__POR }, + { TOMTOM_A_CDC_RX6_B2_CTL, TOMTOM_A_CDC_RX6_B2_CTL__POR }, + { TOMTOM_A_CDC_RX7_B2_CTL, TOMTOM_A_CDC_RX7_B2_CTL__POR }, + { TOMTOM_A_CDC_RX1_B3_CTL, TOMTOM_A_CDC_RX1_B3_CTL__POR }, + { TOMTOM_A_CDC_RX2_B3_CTL, TOMTOM_A_CDC_RX2_B3_CTL__POR }, + { TOMTOM_A_CDC_RX3_B3_CTL, TOMTOM_A_CDC_RX3_B3_CTL__POR }, + { TOMTOM_A_CDC_RX4_B3_CTL, TOMTOM_A_CDC_RX4_B3_CTL__POR }, + { TOMTOM_A_CDC_RX5_B3_CTL, TOMTOM_A_CDC_RX5_B3_CTL__POR }, + { TOMTOM_A_CDC_RX6_B3_CTL, TOMTOM_A_CDC_RX6_B3_CTL__POR }, + { TOMTOM_A_CDC_RX7_B3_CTL, TOMTOM_A_CDC_RX7_B3_CTL__POR }, + { TOMTOM_A_CDC_RX1_B4_CTL, TOMTOM_A_CDC_RX1_B4_CTL__POR }, + { TOMTOM_A_CDC_RX2_B4_CTL, TOMTOM_A_CDC_RX2_B4_CTL__POR }, + { TOMTOM_A_CDC_RX3_B4_CTL, TOMTOM_A_CDC_RX3_B4_CTL__POR }, + { TOMTOM_A_CDC_RX4_B4_CTL, TOMTOM_A_CDC_RX4_B4_CTL__POR }, + { TOMTOM_A_CDC_RX5_B4_CTL, TOMTOM_A_CDC_RX5_B4_CTL__POR }, + { TOMTOM_A_CDC_RX6_B4_CTL, TOMTOM_A_CDC_RX6_B4_CTL__POR }, + { TOMTOM_A_CDC_RX7_B4_CTL, TOMTOM_A_CDC_RX7_B4_CTL__POR }, + { TOMTOM_A_CDC_RX1_B5_CTL, TOMTOM_A_CDC_RX1_B5_CTL__POR }, + { TOMTOM_A_CDC_RX2_B5_CTL, TOMTOM_A_CDC_RX2_B5_CTL__POR }, + { TOMTOM_A_CDC_RX3_B5_CTL, TOMTOM_A_CDC_RX3_B5_CTL__POR }, + { TOMTOM_A_CDC_RX4_B5_CTL, TOMTOM_A_CDC_RX4_B5_CTL__POR }, + { TOMTOM_A_CDC_RX5_B5_CTL, TOMTOM_A_CDC_RX5_B5_CTL__POR }, + { TOMTOM_A_CDC_RX6_B5_CTL, TOMTOM_A_CDC_RX6_B5_CTL__POR }, + { TOMTOM_A_CDC_RX7_B5_CTL, TOMTOM_A_CDC_RX7_B5_CTL__POR }, + { TOMTOM_A_CDC_RX1_B6_CTL, TOMTOM_A_CDC_RX1_B6_CTL__POR }, + { TOMTOM_A_CDC_RX2_B6_CTL, TOMTOM_A_CDC_RX2_B6_CTL__POR }, + { TOMTOM_A_CDC_RX3_B6_CTL, TOMTOM_A_CDC_RX3_B6_CTL__POR }, + { TOMTOM_A_CDC_RX4_B6_CTL, TOMTOM_A_CDC_RX4_B6_CTL__POR }, + { TOMTOM_A_CDC_RX5_B6_CTL, TOMTOM_A_CDC_RX5_B6_CTL__POR }, + { TOMTOM_A_CDC_RX6_B6_CTL, TOMTOM_A_CDC_RX6_B6_CTL__POR }, + { TOMTOM_A_CDC_RX7_B6_CTL, TOMTOM_A_CDC_RX7_B6_CTL__POR }, + { TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL, + TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL__POR }, + { TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL, + TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL__POR }, + { TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL, + TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL__POR }, + { TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL, + TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL__POR }, + { TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL, + TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL__POR }, + { TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL, + TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL__POR }, + { TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL, + TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL__POR }, + { TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL__POR }, + { TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL__POR }, + { TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL__POR }, + { TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL__POR }, + { TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL__POR }, + { TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL__POR }, + { TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL__POR }, + { TOMTOM_A_CDC_VBAT_CFG, TOMTOM_A_CDC_VBAT_CFG__POR }, + { TOMTOM_A_CDC_VBAT_ADC_CAL1, TOMTOM_A_CDC_VBAT_ADC_CAL1__POR }, + { TOMTOM_A_CDC_VBAT_ADC_CAL2, TOMTOM_A_CDC_VBAT_ADC_CAL2__POR }, + { TOMTOM_A_CDC_VBAT_ADC_CAL3, TOMTOM_A_CDC_VBAT_ADC_CAL3__POR }, + { TOMTOM_A_CDC_VBAT_PK_EST1, TOMTOM_A_CDC_VBAT_PK_EST1__POR }, + { TOMTOM_A_CDC_VBAT_PK_EST2, TOMTOM_A_CDC_VBAT_PK_EST2__POR }, + { TOMTOM_A_CDC_VBAT_PK_EST3, TOMTOM_A_CDC_VBAT_PK_EST3__POR }, + { TOMTOM_A_CDC_VBAT_RF_PROC1, TOMTOM_A_CDC_VBAT_RF_PROC1__POR }, + { TOMTOM_A_CDC_VBAT_RF_PROC2, TOMTOM_A_CDC_VBAT_RF_PROC2__POR }, + { TOMTOM_A_CDC_VBAT_TAC1, TOMTOM_A_CDC_VBAT_TAC1__POR }, + { TOMTOM_A_CDC_VBAT_TAC2, TOMTOM_A_CDC_VBAT_TAC2__POR }, + { TOMTOM_A_CDC_VBAT_TAC3, TOMTOM_A_CDC_VBAT_TAC3__POR }, + { TOMTOM_A_CDC_VBAT_TAC4, TOMTOM_A_CDC_VBAT_TAC4__POR }, + { TOMTOM_A_CDC_VBAT_GAIN_UPD1, TOMTOM_A_CDC_VBAT_GAIN_UPD1__POR }, + { TOMTOM_A_CDC_VBAT_GAIN_UPD2, TOMTOM_A_CDC_VBAT_GAIN_UPD2__POR }, + { TOMTOM_A_CDC_VBAT_GAIN_UPD3, TOMTOM_A_CDC_VBAT_GAIN_UPD3__POR }, + { TOMTOM_A_CDC_VBAT_GAIN_UPD4, TOMTOM_A_CDC_VBAT_GAIN_UPD4__POR }, + { TOMTOM_A_CDC_VBAT_DEBUG1, TOMTOM_A_CDC_VBAT_DEBUG1__POR }, + { TOMTOM_A_CDC_VBAT_GAIN_UPD_MON, TOMTOM_A_CDC_VBAT_GAIN_UPD_MON__POR }, + { TOMTOM_A_CDC_VBAT_GAIN_MON_VAL, TOMTOM_A_CDC_VBAT_GAIN_MON_VAL__POR }, + { TOMTOM_A_CDC_CLK_ANC_RESET_CTL, TOMTOM_A_CDC_CLK_ANC_RESET_CTL__POR }, + { TOMTOM_A_CDC_CLK_RX_RESET_CTL, TOMTOM_A_CDC_CLK_RX_RESET_CTL__POR }, + { TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL, + TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL__POR }, + { TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL, + TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL__POR }, + { TOMTOM_A_CDC_CLK_RX_I2S_CTL, TOMTOM_A_CDC_CLK_RX_I2S_CTL__POR }, + { TOMTOM_A_CDC_CLK_TX_I2S_CTL, TOMTOM_A_CDC_CLK_TX_I2S_CTL__POR }, + { TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL, + TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL__POR }, + { TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL, + TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL__POR }, + { TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, + TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR }, + { TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL, + TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL__POR }, + { TOMTOM_A_CDC_CLK_OTHR_CTL, TOMTOM_A_CDC_CLK_OTHR_CTL__POR }, + { TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL, + TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL__POR }, + { TOMTOM_A_CDC_CLK_RX_B1_CTL, TOMTOM_A_CDC_CLK_RX_B1_CTL__POR }, + { TOMTOM_A_CDC_CLK_RX_B2_CTL, TOMTOM_A_CDC_CLK_RX_B2_CTL__POR }, + { TOMTOM_A_CDC_CLK_MCLK_CTL, TOMTOM_A_CDC_CLK_MCLK_CTL__POR }, + { TOMTOM_A_CDC_CLK_PDM_CTL, TOMTOM_A_CDC_CLK_PDM_CTL__POR }, + { TOMTOM_A_CDC_CLK_SD_CTL, TOMTOM_A_CDC_CLK_SD_CTL__POR }, + { TOMTOM_A_CDC_CLSH_B1_CTL, TOMTOM_A_CDC_CLSH_B1_CTL__POR }, + { TOMTOM_A_CDC_CLSH_B2_CTL, TOMTOM_A_CDC_CLSH_B2_CTL__POR }, + { TOMTOM_A_CDC_CLSH_B3_CTL, TOMTOM_A_CDC_CLSH_B3_CTL__POR }, + { TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS, + TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS__POR }, + { TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD, + TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD__POR }, + { TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD, + TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD__POR }, + { TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD, + TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR }, + { TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD, + TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR }, + { TOMTOM_A_CDC_CLSH_K_ADDR, TOMTOM_A_CDC_CLSH_K_ADDR__POR }, + { TOMTOM_A_CDC_CLSH_K_DATA, TOMTOM_A_CDC_CLSH_K_DATA__POR }, + { TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L, + TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L__POR }, + { TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U, + TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U__POR }, + { TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L, + TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L__POR }, + { TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U, + TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U__POR }, + { TOMTOM_A_CDC_CLSH_V_PA_HD_EAR, TOMTOM_A_CDC_CLSH_V_PA_HD_EAR__POR }, + { TOMTOM_A_CDC_CLSH_V_PA_HD_HPH, TOMTOM_A_CDC_CLSH_V_PA_HD_HPH__POR }, + { TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR, TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR__POR }, + { TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH, TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH__POR }, + { TOMTOM_A_CDC_IIR1_GAIN_B1_CTL, TOMTOM_A_CDC_IIR1_GAIN_B1_CTL__POR }, + { TOMTOM_A_CDC_IIR2_GAIN_B1_CTL, TOMTOM_A_CDC_IIR2_GAIN_B1_CTL__POR }, + { TOMTOM_A_CDC_IIR1_GAIN_B2_CTL, TOMTOM_A_CDC_IIR1_GAIN_B2_CTL__POR }, + { TOMTOM_A_CDC_IIR2_GAIN_B2_CTL, TOMTOM_A_CDC_IIR2_GAIN_B2_CTL__POR }, + { TOMTOM_A_CDC_IIR1_GAIN_B3_CTL, TOMTOM_A_CDC_IIR1_GAIN_B3_CTL__POR }, + { TOMTOM_A_CDC_IIR2_GAIN_B3_CTL, TOMTOM_A_CDC_IIR2_GAIN_B3_CTL__POR }, + { TOMTOM_A_CDC_IIR1_GAIN_B4_CTL, TOMTOM_A_CDC_IIR1_GAIN_B4_CTL__POR }, + { TOMTOM_A_CDC_IIR2_GAIN_B4_CTL, TOMTOM_A_CDC_IIR2_GAIN_B4_CTL__POR }, + { TOMTOM_A_CDC_IIR1_GAIN_B5_CTL, TOMTOM_A_CDC_IIR1_GAIN_B5_CTL__POR }, + { TOMTOM_A_CDC_IIR2_GAIN_B5_CTL, TOMTOM_A_CDC_IIR2_GAIN_B5_CTL__POR }, + { TOMTOM_A_CDC_IIR1_GAIN_B6_CTL, TOMTOM_A_CDC_IIR1_GAIN_B6_CTL__POR }, + { TOMTOM_A_CDC_IIR2_GAIN_B6_CTL, TOMTOM_A_CDC_IIR2_GAIN_B6_CTL__POR }, + { TOMTOM_A_CDC_IIR1_GAIN_B7_CTL, TOMTOM_A_CDC_IIR1_GAIN_B7_CTL__POR }, + { TOMTOM_A_CDC_IIR2_GAIN_B7_CTL, TOMTOM_A_CDC_IIR2_GAIN_B7_CTL__POR }, + { TOMTOM_A_CDC_IIR1_GAIN_B8_CTL, TOMTOM_A_CDC_IIR1_GAIN_B8_CTL__POR }, + { TOMTOM_A_CDC_IIR2_GAIN_B8_CTL, TOMTOM_A_CDC_IIR2_GAIN_B8_CTL__POR }, + { TOMTOM_A_CDC_IIR1_CTL, TOMTOM_A_CDC_IIR1_CTL__POR }, + { TOMTOM_A_CDC_IIR2_CTL, TOMTOM_A_CDC_IIR2_CTL__POR }, + { TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL, + TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL__POR }, + { TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL, + TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL__POR }, + { TOMTOM_A_CDC_IIR1_COEF_B1_CTL, TOMTOM_A_CDC_IIR1_COEF_B1_CTL__POR }, + { TOMTOM_A_CDC_IIR2_COEF_B1_CTL, TOMTOM_A_CDC_IIR2_COEF_B1_CTL__POR }, + { TOMTOM_A_CDC_IIR1_COEF_B2_CTL, TOMTOM_A_CDC_IIR1_COEF_B2_CTL__POR }, + { TOMTOM_A_CDC_IIR2_COEF_B2_CTL, TOMTOM_A_CDC_IIR2_COEF_B2_CTL__POR }, + { TOMTOM_A_CDC_TOP_GAIN_UPDATE, TOMTOM_A_CDC_TOP_GAIN_UPDATE__POR }, + { TOMTOM_A_CDC_PA_RAMP_B1_CTL, TOMTOM_A_CDC_PA_RAMP_B1_CTL__POR }, + { TOMTOM_A_CDC_PA_RAMP_B2_CTL, TOMTOM_A_CDC_PA_RAMP_B2_CTL__POR }, + { TOMTOM_A_CDC_PA_RAMP_B3_CTL, TOMTOM_A_CDC_PA_RAMP_B3_CTL__POR }, + { TOMTOM_A_CDC_PA_RAMP_B4_CTL, TOMTOM_A_CDC_PA_RAMP_B4_CTL__POR }, + { TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL, + TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL__POR }, + { TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL, + TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL__POR }, + { TOMTOM_A_CDC_COMP0_B1_CTL, TOMTOM_A_CDC_COMP0_B1_CTL__POR }, + { TOMTOM_A_CDC_COMP1_B1_CTL, TOMTOM_A_CDC_COMP1_B1_CTL__POR }, + { TOMTOM_A_CDC_COMP2_B1_CTL, TOMTOM_A_CDC_COMP2_B1_CTL__POR }, + { TOMTOM_A_CDC_COMP0_B2_CTL, TOMTOM_A_CDC_COMP0_B2_CTL__POR }, + { TOMTOM_A_CDC_COMP1_B2_CTL, TOMTOM_A_CDC_COMP1_B2_CTL__POR }, + { TOMTOM_A_CDC_COMP2_B2_CTL, TOMTOM_A_CDC_COMP2_B2_CTL__POR }, + { TOMTOM_A_CDC_COMP0_B3_CTL, TOMTOM_A_CDC_COMP0_B3_CTL__POR }, + { TOMTOM_A_CDC_COMP1_B3_CTL, TOMTOM_A_CDC_COMP1_B3_CTL__POR }, + { TOMTOM_A_CDC_COMP2_B3_CTL, TOMTOM_A_CDC_COMP2_B3_CTL__POR }, + { TOMTOM_A_CDC_COMP0_B4_CTL, TOMTOM_A_CDC_COMP0_B4_CTL__POR }, + { TOMTOM_A_CDC_COMP1_B4_CTL, TOMTOM_A_CDC_COMP1_B4_CTL__POR }, + { TOMTOM_A_CDC_COMP2_B4_CTL, TOMTOM_A_CDC_COMP2_B4_CTL__POR }, + { TOMTOM_A_CDC_COMP0_B5_CTL, TOMTOM_A_CDC_COMP0_B5_CTL__POR }, + { TOMTOM_A_CDC_COMP1_B5_CTL, TOMTOM_A_CDC_COMP1_B5_CTL__POR }, + { TOMTOM_A_CDC_COMP2_B5_CTL, TOMTOM_A_CDC_COMP2_B5_CTL__POR }, + { TOMTOM_A_CDC_COMP0_B6_CTL, TOMTOM_A_CDC_COMP0_B6_CTL__POR }, + { TOMTOM_A_CDC_COMP1_B6_CTL, TOMTOM_A_CDC_COMP1_B6_CTL__POR }, + { TOMTOM_A_CDC_COMP2_B6_CTL, TOMTOM_A_CDC_COMP2_B6_CTL__POR }, + { TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS, + TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS__POR }, + { TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS, + TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS__POR }, + { TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS, + TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS__POR }, + { TOMTOM_A_CDC_COMP0_FS_CFG, TOMTOM_A_CDC_COMP0_FS_CFG__POR }, + { TOMTOM_A_CDC_COMP1_FS_CFG, TOMTOM_A_CDC_COMP1_FS_CFG__POR }, + { TOMTOM_A_CDC_COMP2_FS_CFG, TOMTOM_A_CDC_COMP2_FS_CFG__POR }, + { TOMTOM_A_CDC_CONN_RX1_B1_CTL, TOMTOM_A_CDC_CONN_RX1_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX1_B2_CTL, TOMTOM_A_CDC_CONN_RX1_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX1_B3_CTL, TOMTOM_A_CDC_CONN_RX1_B3_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX2_B1_CTL, TOMTOM_A_CDC_CONN_RX2_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX2_B2_CTL, TOMTOM_A_CDC_CONN_RX2_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX2_B3_CTL, TOMTOM_A_CDC_CONN_RX2_B3_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX3_B1_CTL, TOMTOM_A_CDC_CONN_RX3_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX3_B2_CTL, TOMTOM_A_CDC_CONN_RX3_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX4_B1_CTL, TOMTOM_A_CDC_CONN_RX4_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX4_B2_CTL, TOMTOM_A_CDC_CONN_RX4_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX5_B1_CTL, TOMTOM_A_CDC_CONN_RX5_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX5_B2_CTL, TOMTOM_A_CDC_CONN_RX5_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX6_B1_CTL, TOMTOM_A_CDC_CONN_RX6_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX6_B2_CTL, TOMTOM_A_CDC_CONN_RX6_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX7_B1_CTL, TOMTOM_A_CDC_CONN_RX7_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX7_B2_CTL, TOMTOM_A_CDC_CONN_RX7_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX7_B3_CTL, TOMTOM_A_CDC_CONN_RX7_B3_CTL__POR }, + { TOMTOM_A_CDC_CONN_ANC_B1_CTL, TOMTOM_A_CDC_CONN_ANC_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_ANC_B2_CTL, TOMTOM_A_CDC_CONN_ANC_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_B1_CTL, TOMTOM_A_CDC_CONN_TX_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_B2_CTL, TOMTOM_A_CDC_CONN_TX_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_B3_CTL, TOMTOM_A_CDC_CONN_TX_B3_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_B4_CTL, TOMTOM_A_CDC_CONN_TX_B4_CTL__POR }, + { TOMTOM_A_CDC_CONN_EQ1_B1_CTL, TOMTOM_A_CDC_CONN_EQ1_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_EQ1_B2_CTL, TOMTOM_A_CDC_CONN_EQ1_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_EQ1_B3_CTL, TOMTOM_A_CDC_CONN_EQ1_B3_CTL__POR }, + { TOMTOM_A_CDC_CONN_EQ1_B4_CTL, TOMTOM_A_CDC_CONN_EQ1_B4_CTL__POR }, + { TOMTOM_A_CDC_CONN_EQ2_B1_CTL, TOMTOM_A_CDC_CONN_EQ2_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_EQ2_B2_CTL, TOMTOM_A_CDC_CONN_EQ2_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_EQ2_B3_CTL, TOMTOM_A_CDC_CONN_EQ2_B3_CTL__POR }, + { TOMTOM_A_CDC_CONN_EQ2_B4_CTL, TOMTOM_A_CDC_CONN_EQ2_B4_CTL__POR }, + { TOMTOM_A_CDC_CONN_SRC1_B1_CTL, TOMTOM_A_CDC_CONN_SRC1_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_SRC1_B2_CTL, TOMTOM_A_CDC_CONN_SRC1_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_SRC2_B1_CTL, TOMTOM_A_CDC_CONN_SRC2_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_SRC2_B2_CTL, TOMTOM_A_CDC_CONN_SRC2_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_SB_B1_CTL, TOMTOM_A_CDC_CONN_TX_SB_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_SB_B2_CTL, TOMTOM_A_CDC_CONN_TX_SB_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_SB_B3_CTL, TOMTOM_A_CDC_CONN_TX_SB_B3_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_SB_B4_CTL, TOMTOM_A_CDC_CONN_TX_SB_B4_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_SB_B5_CTL, TOMTOM_A_CDC_CONN_TX_SB_B5_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_SB_B6_CTL, TOMTOM_A_CDC_CONN_TX_SB_B6_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_SB_B7_CTL, TOMTOM_A_CDC_CONN_TX_SB_B7_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_SB_B8_CTL, TOMTOM_A_CDC_CONN_TX_SB_B8_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_SB_B9_CTL, TOMTOM_A_CDC_CONN_TX_SB_B9_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_SB_B10_CTL, + TOMTOM_A_CDC_CONN_TX_SB_B10_CTL__POR }, + { TOMTOM_A_CDC_CONN_TX_SB_B11_CTL, + TOMTOM_A_CDC_CONN_TX_SB_B11_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX_SB_B1_CTL, TOMTOM_A_CDC_CONN_RX_SB_B1_CTL__POR }, + { TOMTOM_A_CDC_CONN_RX_SB_B2_CTL, TOMTOM_A_CDC_CONN_RX_SB_B2_CTL__POR }, + { TOMTOM_A_CDC_CONN_CLSH_CTL, TOMTOM_A_CDC_CONN_CLSH_CTL__POR }, + { TOMTOM_A_CDC_CONN_MISC, TOMTOM_A_CDC_CONN_MISC__POR }, + { TOMTOM_A_CDC_CONN_RX8_B1_CTL, TOMTOM_A_CDC_CONN_RX8_B1_CTL__POR }, + { TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL, + TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL__POR }, + { TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST, + TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST__POR }, + { TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD, + TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD__POR }, + { TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS, + TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS__POR }, + { TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK, + TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK__POR }, + { TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING, + TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING__POR }, + { TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL, + TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL__POR }, + { TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST, + TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST__POR }, + { TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD, + TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD__POR }, + { TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS, + TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS__POR }, + { TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK, + TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK__POR }, + { TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING, + TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING__POR }, + { TOMTOM_A_CDC_MBHC_EN_CTL, TOMTOM_A_CDC_MBHC_EN_CTL__POR }, + { TOMTOM_A_CDC_MBHC_FIR_B1_CFG, TOMTOM_A_CDC_MBHC_FIR_B1_CFG__POR }, + { TOMTOM_A_CDC_MBHC_FIR_B2_CFG, TOMTOM_A_CDC_MBHC_FIR_B2_CFG__POR }, + { TOMTOM_A_CDC_MBHC_TIMER_B1_CTL, TOMTOM_A_CDC_MBHC_TIMER_B1_CTL__POR }, + { TOMTOM_A_CDC_MBHC_TIMER_B2_CTL, TOMTOM_A_CDC_MBHC_TIMER_B2_CTL__POR }, + { TOMTOM_A_CDC_MBHC_TIMER_B3_CTL, TOMTOM_A_CDC_MBHC_TIMER_B3_CTL__POR }, + { TOMTOM_A_CDC_MBHC_TIMER_B4_CTL, TOMTOM_A_CDC_MBHC_TIMER_B4_CTL__POR }, + { TOMTOM_A_CDC_MBHC_TIMER_B5_CTL, TOMTOM_A_CDC_MBHC_TIMER_B5_CTL__POR }, + { TOMTOM_A_CDC_MBHC_TIMER_B6_CTL, TOMTOM_A_CDC_MBHC_TIMER_B6_CTL__POR }, + { TOMTOM_A_CDC_MBHC_B1_STATUS, TOMTOM_A_CDC_MBHC_B1_STATUS__POR }, + { TOMTOM_A_CDC_MBHC_B2_STATUS, TOMTOM_A_CDC_MBHC_B2_STATUS__POR }, + { TOMTOM_A_CDC_MBHC_B3_STATUS, TOMTOM_A_CDC_MBHC_B3_STATUS__POR }, + { TOMTOM_A_CDC_MBHC_B4_STATUS, TOMTOM_A_CDC_MBHC_B4_STATUS__POR }, + { TOMTOM_A_CDC_MBHC_B5_STATUS, TOMTOM_A_CDC_MBHC_B5_STATUS__POR }, + { TOMTOM_A_CDC_MBHC_B1_CTL, TOMTOM_A_CDC_MBHC_B1_CTL__POR }, + { TOMTOM_A_CDC_MBHC_B2_CTL, TOMTOM_A_CDC_MBHC_B2_CTL__POR }, + { TOMTOM_A_CDC_MBHC_VOLT_B1_CTL, TOMTOM_A_CDC_MBHC_VOLT_B1_CTL__POR }, + { TOMTOM_A_CDC_MBHC_VOLT_B2_CTL, TOMTOM_A_CDC_MBHC_VOLT_B2_CTL__POR }, + { TOMTOM_A_CDC_MBHC_VOLT_B3_CTL, TOMTOM_A_CDC_MBHC_VOLT_B3_CTL__POR }, + { TOMTOM_A_CDC_MBHC_VOLT_B4_CTL, TOMTOM_A_CDC_MBHC_VOLT_B4_CTL__POR }, + { TOMTOM_A_CDC_MBHC_VOLT_B5_CTL, TOMTOM_A_CDC_MBHC_VOLT_B5_CTL__POR }, + { TOMTOM_A_CDC_MBHC_VOLT_B6_CTL, TOMTOM_A_CDC_MBHC_VOLT_B6_CTL__POR }, + { TOMTOM_A_CDC_MBHC_VOLT_B7_CTL, TOMTOM_A_CDC_MBHC_VOLT_B7_CTL__POR }, + { TOMTOM_A_CDC_MBHC_VOLT_B8_CTL, TOMTOM_A_CDC_MBHC_VOLT_B8_CTL__POR }, + { TOMTOM_A_CDC_MBHC_VOLT_B9_CTL, TOMTOM_A_CDC_MBHC_VOLT_B9_CTL__POR }, + { TOMTOM_A_CDC_MBHC_VOLT_B10_CTL, TOMTOM_A_CDC_MBHC_VOLT_B10_CTL__POR }, + { TOMTOM_A_CDC_MBHC_VOLT_B11_CTL, TOMTOM_A_CDC_MBHC_VOLT_B11_CTL__POR }, + { TOMTOM_A_CDC_MBHC_VOLT_B12_CTL, TOMTOM_A_CDC_MBHC_VOLT_B12_CTL__POR }, + { TOMTOM_A_CDC_MBHC_CLK_CTL, TOMTOM_A_CDC_MBHC_CLK_CTL__POR }, + { TOMTOM_A_CDC_MBHC_INT_CTL, TOMTOM_A_CDC_MBHC_INT_CTL__POR }, + { TOMTOM_A_CDC_MBHC_DEBUG_CTL, TOMTOM_A_CDC_MBHC_DEBUG_CTL__POR }, + { TOMTOM_A_CDC_MBHC_SPARE, TOMTOM_A_CDC_MBHC_SPARE__POR }, + { TOMTOM_A_CDC_RX8_B1_CTL, TOMTOM_A_CDC_RX8_B1_CTL__POR }, + { TOMTOM_A_CDC_RX8_B2_CTL, TOMTOM_A_CDC_RX8_B2_CTL__POR }, + { TOMTOM_A_CDC_RX8_B3_CTL, TOMTOM_A_CDC_RX8_B3_CTL__POR }, + { TOMTOM_A_CDC_RX8_B4_CTL, TOMTOM_A_CDC_RX8_B4_CTL__POR }, + { TOMTOM_A_CDC_RX8_B5_CTL, TOMTOM_A_CDC_RX8_B5_CTL__POR }, + { TOMTOM_A_CDC_RX8_B6_CTL, TOMTOM_A_CDC_RX8_B6_CTL__POR }, + { TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL, + TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL__POR }, + { TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL__POR }, + { TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0, + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0__POR }, + { TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1, + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1__POR }, + { TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2, + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2__POR }, + { TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3, + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3__POR }, + { TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4, + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4__POR }, + { TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5, + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5__POR }, + { TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6, + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6__POR }, + { TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7, + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7__POR }, + { TOMTOM_A_CDC_BOOST_MODE_CTL, TOMTOM_A_CDC_BOOST_MODE_CTL__POR }, + { TOMTOM_A_CDC_BOOST_THRESHOLD, TOMTOM_A_CDC_BOOST_THRESHOLD__POR }, + { TOMTOM_A_CDC_BOOST_TAP_SEL, TOMTOM_A_CDC_BOOST_TAP_SEL__POR }, + { TOMTOM_A_CDC_BOOST_HOLD_TIME, TOMTOM_A_CDC_BOOST_HOLD_TIME__POR }, + { TOMTOM_A_CDC_BOOST_TRGR_EN, TOMTOM_A_CDC_BOOST_TRGR_EN__POR }, +}; + +static bool wcd9330_is_readable_register(struct device *dev, unsigned int reg) +{ + return tomtom_reg_readable[reg]; +} + +static bool tomtom_is_digital_gain_register(unsigned int reg) +{ + bool rtn = false; + + switch (reg) { + case TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL: + case TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL: + case TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL: + case TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL: + case TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL: + case TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL: + case TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL: + case TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL: + case TOMTOM_A_CDC_TX1_VOL_CTL_GAIN: + case TOMTOM_A_CDC_TX2_VOL_CTL_GAIN: + case TOMTOM_A_CDC_TX3_VOL_CTL_GAIN: + case TOMTOM_A_CDC_TX4_VOL_CTL_GAIN: + case TOMTOM_A_CDC_TX5_VOL_CTL_GAIN: + case TOMTOM_A_CDC_TX6_VOL_CTL_GAIN: + case TOMTOM_A_CDC_TX7_VOL_CTL_GAIN: + case TOMTOM_A_CDC_TX8_VOL_CTL_GAIN: + case TOMTOM_A_CDC_TX9_VOL_CTL_GAIN: + case TOMTOM_A_CDC_TX10_VOL_CTL_GAIN: + rtn = true; + break; + default: + break; + } + return rtn; +} + +static bool wcd9330_is_volatile_register(struct device *dev, unsigned int reg) +{ + + if ((reg >= TOMTOM_A_CDC_MBHC_EN_CTL) || (reg < 0x100)) + return true; + + /* IIR Coeff registers are not cacheable */ + if ((reg >= TOMTOM_A_CDC_IIR1_COEF_B1_CTL) && + (reg <= TOMTOM_A_CDC_IIR2_COEF_B2_CTL)) + return true; + + /* ANC filter registers are not cacheable */ + if ((reg >= TOMTOM_A_CDC_ANC1_IIR_B1_CTL) && + (reg <= TOMTOM_A_CDC_ANC1_LPF_B2_CTL)) + return true; + + if ((reg >= TOMTOM_A_CDC_ANC2_IIR_B1_CTL) && + (reg <= TOMTOM_A_CDC_ANC2_LPF_B2_CTL)) + return true; + + if (((reg >= TOMTOM_A_CDC_SPKR_CLIPDET_VAL0 && + reg <= TOMTOM_A_CDC_SPKR_CLIPDET_VAL7)) || + ((reg >= TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0) && + (reg <= TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7))) + return true; + + /* Digital gain register is not cacheable so we have to write + * the setting even it is the same + */ + if (tomtom_is_digital_gain_register(reg)) + return true; + + switch (reg) { + case TOMTOM_A_RX_HPH_L_STATUS: + case TOMTOM_A_RX_HPH_R_STATUS: + case TOMTOM_A_MBHC_INSERT_DET_STATUS: + case TOMTOM_A_RX_HPH_CNP_EN: + case TOMTOM_A_CDC_VBAT_GAIN_MON_VAL: + case TOMTOM_A_CDC_MAD_MAIN_CTL_1: + case TOMTOM_A_CDC_MAD_AUDIO_CTL_3: + case TOMTOM_A_CDC_MAD_AUDIO_CTL_4: + case TOMTOM_A_INTR_MODE: + case TOMTOM_A_INTR2_MASK0: + case TOMTOM_A_INTR2_STATUS0: + case TOMTOM_A_INTR2_CLEAR0: + case TOMTOM_SB_PGD_PORT_TX_BASE: + case TOMTOM_SB_PGD_PORT_RX_BASE: + case TOMTOM_A_CDC_ANC1_IIR_B1_CTL: + case TOMTOM_A_CDC_ANC1_GAIN_CTL: + case TOMTOM_A_SVASS_SPE_INBOX_TRG: + return true; + default: + return false; + } +} + +struct regmap_config wcd9330_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd9330_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd9330_defaults), + .max_register = WCD9330_MAX_REGISTER, + .volatile_reg = wcd9330_is_volatile_register, + .readable_reg = wcd9330_is_readable_register, +}; diff --git a/drivers/mfd/wcd9335-regmap.c b/drivers/mfd/wcd9335-regmap.c new file mode 100644 index 0000000000000000000000000000000000000000..1762374418c0ada58e57051be024643e41f299c5 --- /dev/null +++ b/drivers/mfd/wcd9335-regmap.c @@ -0,0 +1,1611 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include "wcd9xxx-regmap.h" + +static const struct reg_sequence wcd9335_1_x_defaults[] = { + { WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, 0x1f, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x00, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x00, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG, 0x00, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG, 0x00, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG, 0x00, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG, 0x00, 0x00 }, + { WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x14, 0x00 }, + { WCD9335_CPE_SS_SS_ERROR_INT_MASK, 0x3f, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL, 0x00, 0x00 }, + { WCD9335_BIAS_VBG_FINE_ADJ, 0x55, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_2, 0x6c, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_3, 0x2d, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_8, 0x6c, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_10, 0x6c, 0x00 }, + { WCD9335_SIDO_SIDO_DRIVER_2, 0x77, 0x00 }, + { WCD9335_SIDO_SIDO_DRIVER_3, 0x77, 0x00 }, + { WCD9335_SIDO_SIDO_TEST_2, 0x00, 0x00 }, + { WCD9335_MBHC_ZDET_ANA_CTL, 0x00, 0x00 }, + { WCD9335_MBHC_FSM_DEBUG, 0xc0, 0x00 }, + { WCD9335_TX_1_2_ATEST_REFCTL, 0x08, 0x00 }, + { WCD9335_TX_3_4_ATEST_REFCTL, 0x08, 0x00 }, + { WCD9335_TX_5_6_ATEST_REFCTL, 0x08, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_1, 0x67, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_4, 0x5f, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_9, 0x50, 0x00 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_1, 0x65, 0x00 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0x40, 0x00 }, + { WCD9335_RX_BIAS_HPH_PA, 0xaa, 0x00 }, + { WCD9335_RX_BIAS_HPH_LOWPOWER, 0x62, 0x00 }, + { WCD9335_HPH_PA_CTL2, 0x40, 0x00 }, + { WCD9335_HPH_L_EN, 0x00, 0x00 }, + { WCD9335_HPH_R_EN, 0x00, 0x00 }, + { WCD9335_HPH_R_ATEST, 0x50, 0x00 }, + { WCD9335_HPH_RDAC_LDO_CTL, 0x00, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER1_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER2_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER3_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER4_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER5_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER6_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER7_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER8_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC0_CLK_RST_CTL_0, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC1_CLK_RST_CTL_0, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC2_CLK_RST_CTL_0, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC3_CLK_RST_CTL_0, 0x00, 0x00 }, + { WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00, 0x00 }, + { WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, 0x00, 0x00 }, + { WCD9335_TEST_DEBUG_NPL_DLY_TEST_2, 0x00, 0x00 }, +}; + +static const struct reg_sequence wcd9335_2_0_defaults[] = { + { WCD9335_CODEC_RPM_CLK_GATE, 0x07, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, 0x3f, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x01, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x10, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG, 0x08, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG, 0x08, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG, 0x08, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG, 0x08, 0x00 }, + { WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x13, 0x00 }, + { WCD9335_CPE_SS_SS_ERROR_INT_MASK, 0xff, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL, 0x40, 0x00 }, + { WCD9335_BIAS_VBG_FINE_ADJ, 0xc5, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_2, 0x92, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_3, 0x35, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_8, 0x6e, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_10, 0x6e, 0x00 }, + { WCD9335_SIDO_SIDO_DRIVER_2, 0x55, 0x00 }, + { WCD9335_SIDO_SIDO_DRIVER_3, 0x55, 0x00 }, + { WCD9335_SIDO_SIDO_TEST_2, 0x0f, 0x00 }, + { WCD9335_MBHC_ZDET_ANA_CTL, 0x0f, 0x00 }, + { WCD9335_TX_1_2_ATEST_REFCTL, 0x0a, 0x00 }, + { WCD9335_TX_3_4_ATEST_REFCTL, 0x0a, 0x00 }, + { WCD9335_TX_5_6_ATEST_REFCTL, 0x0a, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_1, 0xeb, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_4, 0x7f, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_9, 0x64, 0x00 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_1, 0xed, 0x00 }, + { WCD9335_RX_BIAS_HPH_PA, 0x9a, 0x00 }, + { WCD9335_RX_BIAS_HPH_LOWPOWER, 0x82, 0x00 }, + { WCD9335_HPH_PA_CTL2, 0x50, 0x00 }, + { WCD9335_HPH_L_EN, 0x80, 0x00 }, + { WCD9335_HPH_R_EN, 0x80, 0x00 }, + { WCD9335_HPH_R_ATEST, 0x54, 0x00 }, + { WCD9335_HPH_RDAC_LDO_CTL, 0x33, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_COMPANDER1_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER2_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER3_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER4_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER5_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER6_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER7_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER8_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_SPLINE_SRC0_CLK_RST_CTL_0, 0x20, 0x00 }, + { WCD9335_SPLINE_SRC1_CLK_RST_CTL_0, 0x20, 0x00 }, + { WCD9335_SPLINE_SRC2_CLK_RST_CTL_0, 0x20, 0x00 }, + { WCD9335_SPLINE_SRC3_CLK_RST_CTL_0, 0x20, 0x00 }, + { WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x0c, 0x00 }, + { WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, 0x10, 0x00 }, + { WCD9335_TEST_DEBUG_NPL_DLY_TEST_2, 0x60, 0x00 }, + { WCD9335_DATA_HUB_NATIVE_FIFO_SYNC, 0x00, 0x00 }, + { WCD9335_DATA_HUB_NATIVE_FIFO_STATUS, 0x00, 0x00 }, + { WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD, 0x60, 0x00 }, + { WCD9335_CPE_SS_TX_PP_CFG, 0x3C, 0x00 }, + { WCD9335_CPE_SS_SVA_CFG, 0x00, 0x00 }, + { WCD9335_MBHC_FSM_STATUS, 0x00, 0x00 }, + { WCD9335_FLYBACK_CTRL_1, 0x45, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC7, 0x25, 0x00 }, + { WCD9335_SPLINE_SRC0_STATUS, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC1_STATUS, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC2_STATUS, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC3_STATUS, 0x00, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT, 0x00, 0x00 }, +}; + +static const struct reg_default wcd9335_defaults[] = { + /* Page #0 registers */ + { WCD9335_PAGE0_PAGE_REGISTER, 0x00 }, + { WCD9335_CODEC_RPM_CLK_BYPASS, 0x00 }, + { WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x00 }, + { WCD9335_CODEC_RPM_RST_CTL, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x07 }, + { WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_1, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_2, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_3, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN, 0x01 }, + { WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1, 0xff }, + { WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2, 0xff }, + { WCD9335_CODEC_RPM_INT_MASK, 0x3f }, + { WCD9335_CODEC_RPM_INT_STATUS, 0x00 }, + { WCD9335_CODEC_RPM_INT_CLEAR, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE1, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2, 0x07 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE3, 0x01 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_TEST0, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_TEST1, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT3, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT4, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT5, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT6, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT7, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT8, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT9, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT13, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO, 0x0d }, + { WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_1, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_2, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_3, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL, 0xCC }, + { WCD9335_CHIP_TIER_CTRL_I2C_ACTIVE, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC1_MON_CTL, 0x06 }, + { WCD9335_CHIP_TIER_CTRL_PROC1_MON_STATUS, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_MSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_LSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC2_MON_CTL, 0x06 }, + { WCD9335_CHIP_TIER_CTRL_PROC2_MON_STATUS, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_MSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_LSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC3_MON_CTL, 0x06 }, + { WCD9335_CHIP_TIER_CTRL_PROC3_MON_STATUS, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_MSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_LSB, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, 0x0c }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, 0x0c }, + { WCD9335_DATA_HUB_DATA_HUB_I2S_CLK, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX4_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX5_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX6_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX7_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX0_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX1_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX2_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX3_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX4_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX5_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX6_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX7_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX8_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX9_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX10_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX14_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX15_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG, 0x00 }, + { WCD9335_INTR_CFG, 0x00 }, + { WCD9335_INTR_CLR_COMMIT, 0x00 }, + { WCD9335_INTR_PIN1_MASK0, 0xff }, + { WCD9335_INTR_PIN1_MASK1, 0xff }, + { WCD9335_INTR_PIN1_MASK2, 0xff }, + { WCD9335_INTR_PIN1_MASK3, 0xff }, + { WCD9335_INTR_PIN1_STATUS0, 0x00 }, + { WCD9335_INTR_PIN1_STATUS1, 0x00 }, + { WCD9335_INTR_PIN1_STATUS2, 0x00 }, + { WCD9335_INTR_PIN1_STATUS3, 0x00 }, + { WCD9335_INTR_PIN1_CLEAR0, 0x00 }, + { WCD9335_INTR_PIN1_CLEAR1, 0x00 }, + { WCD9335_INTR_PIN1_CLEAR2, 0x00 }, + { WCD9335_INTR_PIN1_CLEAR3, 0x00 }, + { WCD9335_INTR_PIN2_MASK0, 0xff }, + { WCD9335_INTR_PIN2_MASK1, 0xff }, + { WCD9335_INTR_PIN2_MASK2, 0xff }, + { WCD9335_INTR_PIN2_MASK3, 0xff }, + { WCD9335_INTR_PIN2_STATUS0, 0x00 }, + { WCD9335_INTR_PIN2_STATUS1, 0x00 }, + { WCD9335_INTR_PIN2_STATUS2, 0x00 }, + { WCD9335_INTR_PIN2_STATUS3, 0x00 }, + { WCD9335_INTR_PIN2_CLEAR0, 0x00 }, + { WCD9335_INTR_PIN2_CLEAR1, 0x00 }, + { WCD9335_INTR_PIN2_CLEAR2, 0x00 }, + { WCD9335_INTR_PIN2_CLEAR3, 0x00 }, + { WCD9335_INTR_LEVEL0, 0x03 }, + { WCD9335_INTR_LEVEL1, 0xe0 }, + { WCD9335_INTR_LEVEL2, 0x10 }, + { WCD9335_INTR_LEVEL3, 0x80 }, + { WCD9335_INTR_BYPASS0, 0x00 }, + { WCD9335_INTR_BYPASS1, 0x00 }, + { WCD9335_INTR_BYPASS2, 0x00 }, + { WCD9335_INTR_BYPASS3, 0x00 }, + { WCD9335_INTR_SET0, 0x00 }, + { WCD9335_INTR_SET1, 0x00 }, + { WCD9335_INTR_SET2, 0x00 }, + { WCD9335_INTR_SET3, 0x00 }, + /* Page #1 registers */ + { WCD9335_PAGE1_PAGE_REGISTER, 0x00 }, + { WCD9335_CPE_FLL_USER_CTL_0, 0x71 }, + { WCD9335_CPE_FLL_USER_CTL_1, 0x34 }, + { WCD9335_CPE_FLL_USER_CTL_2, 0x0b }, + { WCD9335_CPE_FLL_USER_CTL_3, 0x02 }, + { WCD9335_CPE_FLL_USER_CTL_4, 0x04 }, + { WCD9335_CPE_FLL_USER_CTL_5, 0x02 }, + { WCD9335_CPE_FLL_USER_CTL_6, 0x64 }, + { WCD9335_CPE_FLL_USER_CTL_7, 0x00 }, + { WCD9335_CPE_FLL_USER_CTL_8, 0x94 }, + { WCD9335_CPE_FLL_USER_CTL_9, 0x70 }, + { WCD9335_CPE_FLL_L_VAL_CTL_0, 0x40 }, + { WCD9335_CPE_FLL_L_VAL_CTL_1, 0x00 }, + { WCD9335_CPE_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD9335_CPE_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD9335_CPE_FLL_CONFIG_CTL_0, 0x6b }, + { WCD9335_CPE_FLL_CONFIG_CTL_1, 0x05 }, + { WCD9335_CPE_FLL_CONFIG_CTL_2, 0x08 }, + { WCD9335_CPE_FLL_CONFIG_CTL_3, 0x00 }, + { WCD9335_CPE_FLL_CONFIG_CTL_4, 0x10 }, + { WCD9335_CPE_FLL_TEST_CTL_0, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_1, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_2, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_3, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_4, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_5, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_6, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_7, 0x33 }, + { WCD9335_CPE_FLL_FREQ_CTL_0, 0x00 }, + { WCD9335_CPE_FLL_FREQ_CTL_1, 0x00 }, + { WCD9335_CPE_FLL_FREQ_CTL_2, 0x00 }, + { WCD9335_CPE_FLL_FREQ_CTL_3, 0x00 }, + { WCD9335_CPE_FLL_SSC_CTL_0, 0x00 }, + { WCD9335_CPE_FLL_SSC_CTL_1, 0x00 }, + { WCD9335_CPE_FLL_SSC_CTL_2, 0x00 }, + { WCD9335_CPE_FLL_SSC_CTL_3, 0x00 }, + { WCD9335_CPE_FLL_FLL_MODE, 0x20 }, + { WCD9335_CPE_FLL_STATUS_0, 0x00 }, + { WCD9335_CPE_FLL_STATUS_1, 0x00 }, + { WCD9335_CPE_FLL_STATUS_2, 0x00 }, + { WCD9335_CPE_FLL_STATUS_3, 0x00 }, + { WCD9335_I2S_FLL_USER_CTL_0, 0x41 }, + { WCD9335_I2S_FLL_USER_CTL_1, 0x94 }, + { WCD9335_I2S_FLL_USER_CTL_2, 0x08 }, + { WCD9335_I2S_FLL_USER_CTL_3, 0x02 }, + { WCD9335_I2S_FLL_USER_CTL_4, 0x04 }, + { WCD9335_I2S_FLL_USER_CTL_5, 0x02 }, + { WCD9335_I2S_FLL_USER_CTL_6, 0x40 }, + { WCD9335_I2S_FLL_USER_CTL_7, 0x00 }, + { WCD9335_I2S_FLL_USER_CTL_8, 0x5f }, + { WCD9335_I2S_FLL_USER_CTL_9, 0x02 }, + { WCD9335_I2S_FLL_L_VAL_CTL_0, 0x40 }, + { WCD9335_I2S_FLL_L_VAL_CTL_1, 0x00 }, + { WCD9335_I2S_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD9335_I2S_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD9335_I2S_FLL_CONFIG_CTL_0, 0x6b }, + { WCD9335_I2S_FLL_CONFIG_CTL_1, 0x05 }, + { WCD9335_I2S_FLL_CONFIG_CTL_2, 0x08 }, + { WCD9335_I2S_FLL_CONFIG_CTL_3, 0x00 }, + { WCD9335_I2S_FLL_CONFIG_CTL_4, 0x30 }, + { WCD9335_I2S_FLL_TEST_CTL_0, 0x80 }, + { WCD9335_I2S_FLL_TEST_CTL_1, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_2, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_3, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_4, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_5, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_6, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_7, 0xff }, + { WCD9335_I2S_FLL_FREQ_CTL_0, 0x00 }, + { WCD9335_I2S_FLL_FREQ_CTL_1, 0x00 }, + { WCD9335_I2S_FLL_FREQ_CTL_2, 0x00 }, + { WCD9335_I2S_FLL_FREQ_CTL_3, 0x00 }, + { WCD9335_I2S_FLL_SSC_CTL_0, 0x00 }, + { WCD9335_I2S_FLL_SSC_CTL_1, 0x00 }, + { WCD9335_I2S_FLL_SSC_CTL_2, 0x00 }, + { WCD9335_I2S_FLL_SSC_CTL_3, 0x00 }, + { WCD9335_I2S_FLL_FLL_MODE, 0x00 }, + { WCD9335_I2S_FLL_STATUS_0, 0x00 }, + { WCD9335_I2S_FLL_STATUS_1, 0x00 }, + { WCD9335_I2S_FLL_STATUS_2, 0x00 }, + { WCD9335_I2S_FLL_STATUS_3, 0x00 }, + { WCD9335_SB_FLL_USER_CTL_0, 0x41 }, + { WCD9335_SB_FLL_USER_CTL_1, 0x94 }, + { WCD9335_SB_FLL_USER_CTL_2, 0x08 }, + { WCD9335_SB_FLL_USER_CTL_3, 0x02 }, + { WCD9335_SB_FLL_USER_CTL_4, 0x04 }, + { WCD9335_SB_FLL_USER_CTL_5, 0x02 }, + { WCD9335_SB_FLL_USER_CTL_6, 0x40 }, + { WCD9335_SB_FLL_USER_CTL_7, 0x00 }, + { WCD9335_SB_FLL_USER_CTL_8, 0x5e }, + { WCD9335_SB_FLL_USER_CTL_9, 0x01 }, + { WCD9335_SB_FLL_L_VAL_CTL_0, 0x40 }, + { WCD9335_SB_FLL_L_VAL_CTL_1, 0x00 }, + { WCD9335_SB_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD9335_SB_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD9335_SB_FLL_CONFIG_CTL_0, 0x6b }, + { WCD9335_SB_FLL_CONFIG_CTL_1, 0x05 }, + { WCD9335_SB_FLL_CONFIG_CTL_2, 0x08 }, + { WCD9335_SB_FLL_CONFIG_CTL_3, 0x00 }, + { WCD9335_SB_FLL_CONFIG_CTL_4, 0x10 }, + { WCD9335_SB_FLL_TEST_CTL_0, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_1, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_2, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_3, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_4, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_5, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_6, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_7, 0xff }, + { WCD9335_SB_FLL_FREQ_CTL_0, 0x00 }, + { WCD9335_SB_FLL_FREQ_CTL_1, 0x00 }, + { WCD9335_SB_FLL_FREQ_CTL_2, 0x00 }, + { WCD9335_SB_FLL_FREQ_CTL_3, 0x00 }, + { WCD9335_SB_FLL_SSC_CTL_0, 0x00 }, + { WCD9335_SB_FLL_SSC_CTL_1, 0x00 }, + { WCD9335_SB_FLL_SSC_CTL_2, 0x00 }, + { WCD9335_SB_FLL_SSC_CTL_3, 0x00 }, + { WCD9335_SB_FLL_FLL_MODE, 0x00 }, + { WCD9335_SB_FLL_STATUS_0, 0x00 }, + { WCD9335_SB_FLL_STATUS_1, 0x00 }, + { WCD9335_SB_FLL_STATUS_2, 0x00 }, + { WCD9335_SB_FLL_STATUS_3, 0x00 }, + /* Page #2 registers */ + { WCD9335_PAGE2_PAGE_REGISTER, 0x00 }, + { WCD9335_CPE_SS_MEM_PTR_0, 0x00 }, + { WCD9335_CPE_SS_MEM_PTR_1, 0x00 }, + { WCD9335_CPE_SS_MEM_PTR_2, 0x00 }, + { WCD9335_CPE_SS_MEM_CTRL, 0x08 }, + { WCD9335_CPE_SS_MEM_BANK_0, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_1, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_2, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_3, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_4, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_5, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_6, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_7, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_8, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_9, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_10, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_11, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_12, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_13, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_14, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_15, 0x00 }, + { WCD9335_CPE_SS_INBOX1_TRG, 0x00 }, + { WCD9335_CPE_SS_INBOX2_TRG, 0x00 }, + { WCD9335_CPE_SS_INBOX1_0, 0x00 }, + { WCD9335_CPE_SS_INBOX1_1, 0x00 }, + { WCD9335_CPE_SS_INBOX1_2, 0x00 }, + { WCD9335_CPE_SS_INBOX1_3, 0x00 }, + { WCD9335_CPE_SS_INBOX1_4, 0x00 }, + { WCD9335_CPE_SS_INBOX1_5, 0x00 }, + { WCD9335_CPE_SS_INBOX1_6, 0x00 }, + { WCD9335_CPE_SS_INBOX1_7, 0x00 }, + { WCD9335_CPE_SS_INBOX1_8, 0x00 }, + { WCD9335_CPE_SS_INBOX1_9, 0x00 }, + { WCD9335_CPE_SS_INBOX1_10, 0x00 }, + { WCD9335_CPE_SS_INBOX1_11, 0x00 }, + { WCD9335_CPE_SS_INBOX1_12, 0x00 }, + { WCD9335_CPE_SS_INBOX1_13, 0x00 }, + { WCD9335_CPE_SS_INBOX1_14, 0x00 }, + { WCD9335_CPE_SS_INBOX1_15, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_0, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_1, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_2, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_3, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_4, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_5, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_6, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_7, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_8, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_9, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_10, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_11, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_12, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_13, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_14, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_15, 0x00 }, + { WCD9335_CPE_SS_INBOX2_0, 0x00 }, + { WCD9335_CPE_SS_INBOX2_1, 0x00 }, + { WCD9335_CPE_SS_INBOX2_2, 0x00 }, + { WCD9335_CPE_SS_INBOX2_3, 0x00 }, + { WCD9335_CPE_SS_INBOX2_4, 0x00 }, + { WCD9335_CPE_SS_INBOX2_5, 0x00 }, + { WCD9335_CPE_SS_INBOX2_6, 0x00 }, + { WCD9335_CPE_SS_INBOX2_7, 0x00 }, + { WCD9335_CPE_SS_INBOX2_8, 0x00 }, + { WCD9335_CPE_SS_INBOX2_9, 0x00 }, + { WCD9335_CPE_SS_INBOX2_10, 0x00 }, + { WCD9335_CPE_SS_INBOX2_11, 0x00 }, + { WCD9335_CPE_SS_INBOX2_12, 0x00 }, + { WCD9335_CPE_SS_INBOX2_13, 0x00 }, + { WCD9335_CPE_SS_INBOX2_14, 0x00 }, + { WCD9335_CPE_SS_INBOX2_15, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_0, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_1, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_2, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_3, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_4, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_5, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_6, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_7, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_8, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_9, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_10, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_11, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_12, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_13, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_14, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_15, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_ACK, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_ACK, 0x00 }, + { WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3c }, + { WCD9335_CPE_SS_US_BUF_INT_PERIOD, 0x60 }, + { WCD9335_CPE_SS_CFG, 0x41 }, + { WCD9335_CPE_SS_US_EC_MUX_CFG, 0x00 }, + { WCD9335_CPE_SS_MAD_CTL, 0x00 }, + { WCD9335_CPE_SS_CPAR_CTL, 0x00 }, + { WCD9335_CPE_SS_DMIC0_CTL, 0x00 }, + { WCD9335_CPE_SS_DMIC1_CTL, 0x00 }, + { WCD9335_CPE_SS_DMIC2_CTL, 0x00 }, + { WCD9335_CPE_SS_DMIC_CFG, 0x80 }, + { WCD9335_CPE_SS_CPAR_CFG, 0x00 }, + { WCD9335_CPE_SS_WDOG_CFG, 0x01 }, + { WCD9335_CPE_SS_BACKUP_INT, 0x00 }, + { WCD9335_CPE_SS_STATUS, 0x00 }, + { WCD9335_CPE_SS_CPE_OCD_CFG, 0x00 }, + { WCD9335_CPE_SS_SS_ERROR_INT_STATUS, 0x00 }, + { WCD9335_CPE_SS_SS_ERROR_INT_CLEAR, 0x00 }, + { WCD9335_SOC_MAD_MAIN_CTL_1, 0x00 }, + { WCD9335_SOC_MAD_MAIN_CTL_2, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_1, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_2, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_3, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_4, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_5, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_6, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_7, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_8, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_1, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_2, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_3, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_4, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_5, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_6, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_7, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_1, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_2, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_3, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_4, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_5, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_6, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_7, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_8, 0x00 }, + { WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR, 0x00 }, + { WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL, 0x00 }, + { WCD9335_SOC_MAD_INP_SEL, 0x00 }, + /* Page #6 registers */ + { WCD9335_PAGE6_PAGE_REGISTER, 0x00 }, + { WCD9335_ANA_BIAS, 0x00 }, + { WCD9335_ANA_CLK_TOP, 0x00 }, + { WCD9335_ANA_RCO, 0x30 }, + { WCD9335_ANA_BUCK_VOUT_A, 0xb4 }, + { WCD9335_ANA_BUCK_VOUT_D, 0xb4 }, + { WCD9335_ANA_BUCK_CTL, 0x00 }, + { WCD9335_ANA_BUCK_STATUS, 0xe0 }, + { WCD9335_ANA_RX_SUPPLIES, 0x00 }, + { WCD9335_ANA_HPH, 0x00 }, + { WCD9335_ANA_EAR, 0x00 }, + { WCD9335_ANA_LO_1_2, 0x00 }, + { WCD9335_ANA_LO_3_4, 0x00 }, + { WCD9335_ANA_MAD_SETUP, 0x81 }, + { WCD9335_ANA_AMIC1, 0x20 }, + { WCD9335_ANA_AMIC2, 0x00 }, + { WCD9335_ANA_AMIC3, 0x20 }, + { WCD9335_ANA_AMIC4, 0x00 }, + { WCD9335_ANA_AMIC5, 0x20 }, + { WCD9335_ANA_AMIC6, 0x00 }, + { WCD9335_ANA_MBHC_MECH, 0x39 }, + { WCD9335_ANA_MBHC_ELECT, 0x08 }, + { WCD9335_ANA_MBHC_ZDET, 0x00 }, + { WCD9335_ANA_MBHC_RESULT_1, 0x00 }, + { WCD9335_ANA_MBHC_RESULT_2, 0x00 }, + { WCD9335_ANA_MBHC_RESULT_3, 0x00 }, + { WCD9335_ANA_MBHC_BTN0, 0x00 }, + { WCD9335_ANA_MBHC_BTN1, 0x10 }, + { WCD9335_ANA_MBHC_BTN2, 0x20 }, + { WCD9335_ANA_MBHC_BTN3, 0x30 }, + { WCD9335_ANA_MBHC_BTN4, 0x40 }, + { WCD9335_ANA_MBHC_BTN5, 0x50 }, + { WCD9335_ANA_MBHC_BTN6, 0x60 }, + { WCD9335_ANA_MBHC_BTN7, 0x70 }, + { WCD9335_ANA_MICB1, 0x10 }, + { WCD9335_ANA_MICB2, 0x10 }, + { WCD9335_ANA_MICB2_RAMP, 0x00 }, + { WCD9335_ANA_MICB3, 0x10 }, + { WCD9335_ANA_MICB4, 0x10 }, + { WCD9335_ANA_VBADC, 0x00 }, + { WCD9335_BIAS_CTL, 0x28 }, + { WCD9335_CLOCK_TEST_CTL, 0x00 }, + { WCD9335_RCO_CTRL_1, 0x44 }, + { WCD9335_RCO_CTRL_2, 0x44 }, + { WCD9335_RCO_CAL, 0x00 }, + { WCD9335_RCO_CAL_1, 0x00 }, + { WCD9335_RCO_CAL_2, 0x00 }, + { WCD9335_RCO_TEST_CTRL, 0x00 }, + { WCD9335_RCO_CAL_OUT_1, 0x00 }, + { WCD9335_RCO_CAL_OUT_2, 0x00 }, + { WCD9335_RCO_CAL_OUT_3, 0x00 }, + { WCD9335_RCO_CAL_OUT_4, 0x00 }, + { WCD9335_RCO_CAL_OUT_5, 0x00 }, + { WCD9335_SIDO_SIDO_MODE_1, 0x84 }, + { WCD9335_SIDO_SIDO_MODE_2, 0xfe }, + { WCD9335_SIDO_SIDO_MODE_3, 0xf6 }, + { WCD9335_SIDO_SIDO_MODE_4, 0x56 }, + { WCD9335_SIDO_SIDO_VCL_1, 0x00 }, + { WCD9335_SIDO_SIDO_VCL_2, 0x6c }, + { WCD9335_SIDO_SIDO_VCL_3, 0x44 }, + { WCD9335_SIDO_SIDO_CCL_1, 0x57 }, + { WCD9335_SIDO_SIDO_CCL_4, 0x61 }, + { WCD9335_SIDO_SIDO_CCL_5, 0x6d }, + { WCD9335_SIDO_SIDO_CCL_6, 0x60 }, + { WCD9335_SIDO_SIDO_CCL_7, 0x6f }, + { WCD9335_SIDO_SIDO_CCL_9, 0x6e }, + { WCD9335_SIDO_SIDO_FILTER_1, 0x92 }, + { WCD9335_SIDO_SIDO_FILTER_2, 0x54 }, + { WCD9335_SIDO_SIDO_DRIVER_1, 0x77 }, + { WCD9335_SIDO_SIDO_CAL_CODE_EXT_1, 0x9c }, + { WCD9335_SIDO_SIDO_CAL_CODE_EXT_2, 0x82 }, + { WCD9335_SIDO_SIDO_CAL_CODE_OUT_1, 0x00 }, + { WCD9335_SIDO_SIDO_CAL_CODE_OUT_2, 0x00 }, + { WCD9335_SIDO_SIDO_TEST_1, 0x00 }, + { WCD9335_MBHC_CTL_1, 0x32 }, + { WCD9335_MBHC_CTL_2, 0x01 }, + { WCD9335_MBHC_PLUG_DETECT_CTL, 0x69 }, + { WCD9335_MBHC_ZDET_RAMP_CTL, 0x00 }, + { WCD9335_MBHC_TEST_CTL, 0x00 }, + { WCD9335_VBADC_SUBBLOCK_EN, 0xfe }, + { WCD9335_VBADC_IBIAS_FE, 0x54 }, + { WCD9335_VBADC_BIAS_ADC, 0x51 }, + { WCD9335_VBADC_FE_CTRL, 0x1c }, + { WCD9335_VBADC_ADC_REF, 0x20 }, + { WCD9335_VBADC_ADC_IO, 0x80 }, + { WCD9335_VBADC_ADC_SAR, 0xff }, + { WCD9335_VBADC_DEBUG, 0x00 }, + { WCD9335_VBADC_ADC_DOUTMSB, 0x00 }, + { WCD9335_VBADC_ADC_DOUTLSB, 0x00 }, + { WCD9335_LDOH_MODE, 0x2b }, + { WCD9335_LDOH_BIAS, 0x68 }, + { WCD9335_LDOH_STB_LOADS, 0x00 }, + { WCD9335_LDOH_SLOWRAMP, 0x50 }, + { WCD9335_MICB1_TEST_CTL_1, 0x1a }, + { WCD9335_MICB1_TEST_CTL_2, 0x18 }, + { WCD9335_MICB1_TEST_CTL_3, 0xa4 }, + { WCD9335_MICB2_TEST_CTL_1, 0x1a }, + { WCD9335_MICB2_TEST_CTL_2, 0x18 }, + { WCD9335_MICB2_TEST_CTL_3, 0x24 }, + { WCD9335_MICB3_TEST_CTL_1, 0x1a }, + { WCD9335_MICB3_TEST_CTL_2, 0x18 }, + { WCD9335_MICB3_TEST_CTL_3, 0xa4 }, + { WCD9335_MICB4_TEST_CTL_1, 0x1a }, + { WCD9335_MICB4_TEST_CTL_2, 0x18 }, + { WCD9335_MICB4_TEST_CTL_3, 0xa4 }, + { WCD9335_TX_COM_ADC_VCM, 0x39 }, + { WCD9335_TX_COM_BIAS_ATEST, 0xc0 }, + { WCD9335_TX_COM_ADC_INT1_IB, 0x6f }, + { WCD9335_TX_COM_ADC_INT2_IB, 0x4f }, + { WCD9335_TX_COM_TXFE_DIV_CTL, 0x2e }, + { WCD9335_TX_COM_TXFE_DIV_START, 0x00 }, + { WCD9335_TX_COM_TXFE_DIV_STOP_9P6M, 0xc7 }, + { WCD9335_TX_COM_TXFE_DIV_STOP_12P288M, 0xff }, + { WCD9335_TX_1_2_TEST_EN, 0xcc }, + { WCD9335_TX_1_2_ADC_IB, 0x09 }, + { WCD9335_TX_1_2_TEST_CTL, 0x38 }, + { WCD9335_TX_1_2_TEST_BLK_EN, 0xff }, + { WCD9335_TX_1_2_TXFE_CLKDIV, 0x00 }, + { WCD9335_TX_1_2_SAR1_ERR, 0x00 }, + { WCD9335_TX_1_2_SAR2_ERR, 0x00 }, + { WCD9335_TX_3_4_TEST_EN, 0xcc }, + { WCD9335_TX_3_4_ADC_IB, 0x09 }, + { WCD9335_TX_3_4_TEST_CTL, 0x38 }, + { WCD9335_TX_3_4_TEST_BLK_EN, 0xff }, + { WCD9335_TX_3_4_TXFE_CLKDIV, 0x00 }, + { WCD9335_TX_3_4_SAR1_ERR, 0x00 }, + { WCD9335_TX_3_4_SAR2_ERR, 0x00 }, + { WCD9335_TX_5_6_TEST_EN, 0xcc }, + { WCD9335_TX_5_6_ADC_IB, 0x09 }, + { WCD9335_TX_5_6_TEST_CTL, 0x38 }, + { WCD9335_TX_5_6_TEST_BLK_EN, 0xff }, + { WCD9335_TX_5_6_TXFE_CLKDIV, 0x00 }, + { WCD9335_TX_5_6_SAR1_ERR, 0x00 }, + { WCD9335_TX_5_6_SAR2_ERR, 0x00 }, + { WCD9335_CLASSH_MODE_1, 0x40 }, + { WCD9335_CLASSH_MODE_2, 0x3a }, + { WCD9335_CLASSH_MODE_3, 0x00 }, + { WCD9335_CLASSH_CTRL_VCL_1, 0x70 }, + { WCD9335_CLASSH_CTRL_VCL_2, 0xa2 }, + { WCD9335_CLASSH_CTRL_CCL_1, 0x51 }, + { WCD9335_CLASSH_CTRL_CCL_2, 0x80 }, + { WCD9335_CLASSH_CTRL_CCL_3, 0x80 }, + { WCD9335_CLASSH_CTRL_CCL_4, 0x51 }, + { WCD9335_CLASSH_CTRL_CCL_5, 0x00 }, + { WCD9335_CLASSH_BUCK_TMUX_A_D, 0x00 }, + { WCD9335_CLASSH_BUCK_SW_DRV_CNTL, 0x77 }, + { WCD9335_CLASSH_SPARE, 0x00 }, + { WCD9335_FLYBACK_EN, 0x4e }, + { WCD9335_FLYBACK_VNEG_CTRL_2, 0x45 }, + { WCD9335_FLYBACK_VNEG_CTRL_3, 0x74 }, + { WCD9335_FLYBACK_VNEG_CTRL_5, 0x83 }, + { WCD9335_FLYBACK_VNEG_CTRL_6, 0x98 }, + { WCD9335_FLYBACK_VNEG_CTRL_7, 0xa9 }, + { WCD9335_FLYBACK_VNEG_CTRL_8, 0x68 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_2, 0x50 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_3, 0xa6 }, + { WCD9335_FLYBACK_TEST_CTL, 0x00 }, + { WCD9335_RX_AUX_SW_CTL, 0x00 }, + { WCD9335_RX_PA_AUX_IN_CONN, 0x00 }, + { WCD9335_RX_TIMER_DIV, 0x74 }, + { WCD9335_RX_OCP_CTL, 0x1f }, + { WCD9335_RX_OCP_COUNT, 0x77 }, + { WCD9335_RX_BIAS_EAR_DAC, 0xa0 }, + { WCD9335_RX_BIAS_EAR_AMP, 0xaa }, + { WCD9335_RX_BIAS_HPH_LDO, 0xa9 }, + { WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8a }, + { WCD9335_RX_BIAS_HPH_RDAC_LDO, 0x88 }, + { WCD9335_RX_BIAS_HPH_CNP1, 0x86 }, + { WCD9335_RX_BIAS_DIFFLO_PA, 0x80 }, + { WCD9335_RX_BIAS_DIFFLO_REF, 0x88 }, + { WCD9335_RX_BIAS_DIFFLO_LDO, 0x88 }, + { WCD9335_RX_BIAS_SELO_DAC_PA, 0xa8 }, + { WCD9335_RX_BIAS_BUCK_RST, 0x08 }, + { WCD9335_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 }, + { WCD9335_RX_BIAS_FLYB_ERRAMP, 0x40 }, + { WCD9335_RX_BIAS_FLYB_BUFF, 0xaa }, + { WCD9335_RX_BIAS_FLYB_MID_RST, 0x44 }, + { WCD9335_HPH_L_STATUS, 0x04 }, + { WCD9335_HPH_R_STATUS, 0x04 }, + { WCD9335_HPH_CNP_EN, 0x80 }, + { WCD9335_HPH_CNP_WG_CTL, 0xda }, + { WCD9335_HPH_CNP_WG_TIME, 0x15 }, + { WCD9335_HPH_OCP_CTL, 0x28 }, + { WCD9335_HPH_AUTO_CHOP, 0x12 }, + { WCD9335_HPH_CHOP_CTL, 0x83 }, + { WCD9335_HPH_PA_CTL1, 0x46 }, + { WCD9335_HPH_L_TEST, 0x00 }, + { WCD9335_HPH_L_ATEST, 0x50 }, + { WCD9335_HPH_R_TEST, 0x00 }, + { WCD9335_HPH_RDAC_CLK_CTL1, 0x99 }, + { WCD9335_HPH_RDAC_CLK_CTL2, 0x9b }, + { WCD9335_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD9335_HPH_REFBUFF_UHQA_CTL, 0xa8 }, + { WCD9335_HPH_REFBUFF_LP_CTL, 0x00 }, + { WCD9335_HPH_L_DAC_CTL, 0x00 }, + { WCD9335_HPH_R_DAC_CTL, 0x00 }, + { WCD9335_EAR_EN_REG, 0x60 }, + { WCD9335_EAR_CMBUFF, 0x0d }, + { WCD9335_EAR_ICTL, 0x40 }, + { WCD9335_EAR_EN_DBG_CTL, 0x00 }, + { WCD9335_EAR_CNP, 0xe0 }, + { WCD9335_EAR_DAC_CTL_ATEST, 0x00 }, + { WCD9335_EAR_STATUS_REG, 0x04 }, + { WCD9335_EAR_OUT_SHORT, 0x00 }, + { WCD9335_DIFF_LO_MISC, 0x03 }, + { WCD9335_DIFF_LO_LO2_COMPANDER, 0x00 }, + { WCD9335_DIFF_LO_LO1_COMPANDER, 0x00 }, + { WCD9335_DIFF_LO_COMMON, 0x40 }, + { WCD9335_DIFF_LO_BYPASS_EN, 0x00 }, + { WCD9335_DIFF_LO_CNP, 0x20 }, + { WCD9335_DIFF_LO_CORE_OUT_PROG, 0x00 }, + { WCD9335_DIFF_LO_LDO_OUT_PROG, 0x00 }, + { WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x9b }, + { WCD9335_DIFF_LO_COM_PA_FREQ, 0xb0 }, + { WCD9335_DIFF_LO_RESERVED_REG, 0x60 }, + { WCD9335_DIFF_LO_LO1_STATUS_1, 0x00 }, + { WCD9335_DIFF_LO_LO1_STATUS_2, 0x00 }, + { WCD9335_SE_LO_COM1, 0x80 }, + { WCD9335_SE_LO_COM2, 0x04 }, + { WCD9335_SE_LO_LO3_GAIN, 0x20 }, + { WCD9335_SE_LO_LO3_CTRL, 0x04 }, + { WCD9335_SE_LO_LO4_GAIN, 0x20 }, + { WCD9335_SE_LO_LO4_CTRL, 0x04 }, + { WCD9335_SE_LO_LO3_STATUS, 0x00 }, + { WCD9335_SE_LO_LO4_STATUS, 0x00 }, + /* Page #10 registers */ + { WCD9335_PAGE10_PAGE_REGISTER, 0x00 }, + { WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x00 }, + { WCD9335_CDC_ANC0_MODE_1_CTL, 0x00 }, + { WCD9335_CDC_ANC0_MODE_2_CTL, 0x00 }, + { WCD9335_CDC_ANC0_FF_SHIFT, 0x00 }, + { WCD9335_CDC_ANC0_FB_SHIFT, 0x00 }, + { WCD9335_CDC_ANC0_LPF_FF_A_CTL, 0x00 }, + { WCD9335_CDC_ANC0_LPF_FF_B_CTL, 0x00 }, + { WCD9335_CDC_ANC0_LPF_FB_CTL, 0x00 }, + { WCD9335_CDC_ANC0_SMLPF_CTL, 0x00 }, + { WCD9335_CDC_ANC0_DCFLT_SHIFT_CTL, 0x00 }, + { WCD9335_CDC_ANC0_IIR_ADAPT_CTL, 0x00 }, + { WCD9335_CDC_ANC0_IIR_COEFF_1_CTL, 0x00 }, + { WCD9335_CDC_ANC0_IIR_COEFF_2_CTL, 0x00 }, + { WCD9335_CDC_ANC0_FF_A_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC0_FF_B_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC0_FB_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x00 }, + { WCD9335_CDC_ANC1_MODE_1_CTL, 0x00 }, + { WCD9335_CDC_ANC1_MODE_2_CTL, 0x00 }, + { WCD9335_CDC_ANC1_FF_SHIFT, 0x00 }, + { WCD9335_CDC_ANC1_FB_SHIFT, 0x00 }, + { WCD9335_CDC_ANC1_LPF_FF_A_CTL, 0x00 }, + { WCD9335_CDC_ANC1_LPF_FF_B_CTL, 0x00 }, + { WCD9335_CDC_ANC1_LPF_FB_CTL, 0x00 }, + { WCD9335_CDC_ANC1_SMLPF_CTL, 0x00 }, + { WCD9335_CDC_ANC1_DCFLT_SHIFT_CTL, 0x00 }, + { WCD9335_CDC_ANC1_IIR_ADAPT_CTL, 0x00 }, + { WCD9335_CDC_ANC1_IIR_COEFF_1_CTL, 0x00 }, + { WCD9335_CDC_ANC1_IIR_COEFF_2_CTL, 0x00 }, + { WCD9335_CDC_ANC1_FF_A_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC1_FF_B_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC1_FB_GAIN_CTL, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX0_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX0_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX1_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX1_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX2_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX2_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX3_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX3_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX4_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX4_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX5_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX5_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX6_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX6_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX7_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX7_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX8_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX8_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x00 }, + /* Page #11 registers */ + { WCD9335_PAGE11_PAGE_REGISTER, 0x00 }, + { WCD9335_CDC_COMPANDER1_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER1_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER1_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER1_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER1_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER1_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER1_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER2_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER2_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER2_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER2_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER2_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER2_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER2_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER3_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER3_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER3_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER3_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER3_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER3_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER3_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER4_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER4_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER4_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER4_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER4_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER4_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER4_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER5_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER5_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER5_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER5_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER5_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER5_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER5_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER6_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER6_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER6_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER6_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER6_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER6_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER6_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER7_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER7_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER7_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER7_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER7_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER7_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER7_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER8_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER8_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER8_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER8_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER8_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER8_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER8_CTL6, 0x01 }, + { WCD9335_CDC_RX0_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX0_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX0_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX0_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX1_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX1_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX1_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC4, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX2_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX2_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX2_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC4, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX3_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX3_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX3_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX4_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX4_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX4_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX5_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX5_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX5_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX6_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX6_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX6_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX7_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX7_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX7_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX8_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX8_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX8_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_SEC1, 0x00 }, + /* Page #12 registers */ + { WCD9335_PAGE12_PAGE_REGISTER, 0x00 }, + { WCD9335_CDC_CLSH_CRC, 0x00 }, + { WCD9335_CDC_CLSH_DLY_CTRL, 0x03 }, + { WCD9335_CDC_CLSH_DECAY_CTRL, 0x02 }, + { WCD9335_CDC_CLSH_HPH_V_PA, 0x1c }, + { WCD9335_CDC_CLSH_EAR_V_PA, 0x39 }, + { WCD9335_CDC_CLSH_HPH_V_HD, 0x0c }, + { WCD9335_CDC_CLSH_EAR_V_HD, 0x0c }, + { WCD9335_CDC_CLSH_K1_MSB, 0x01 }, + { WCD9335_CDC_CLSH_K1_LSB, 0x00 }, + { WCD9335_CDC_CLSH_K2_MSB, 0x00 }, + { WCD9335_CDC_CLSH_K2_LSB, 0x80 }, + { WCD9335_CDC_CLSH_IDLE_CTRL, 0x00 }, + { WCD9335_CDC_CLSH_IDLE_HPH, 0x00 }, + { WCD9335_CDC_CLSH_IDLE_EAR, 0x00 }, + { WCD9335_CDC_CLSH_TEST0, 0x07 }, + { WCD9335_CDC_CLSH_TEST1, 0x00 }, + { WCD9335_CDC_CLSH_OVR_VREF, 0x00 }, + { WCD9335_CDC_BOOST0_BOOST_PATH_CTL, 0x00 }, + { WCD9335_CDC_BOOST0_BOOST_CTL, 0xb2 }, + { WCD9335_CDC_BOOST0_BOOST_CFG1, 0x00 }, + { WCD9335_CDC_BOOST0_BOOST_CFG2, 0x00 }, + { WCD9335_CDC_BOOST1_BOOST_PATH_CTL, 0x00 }, + { WCD9335_CDC_BOOST1_BOOST_CTL, 0xb2 }, + { WCD9335_CDC_BOOST1_BOOST_CFG1, 0x00 }, + { WCD9335_CDC_BOOST1_BOOST_CFG2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_DATA_0, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_DATA_1, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_DATA_2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_DATA_3, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_DATA_0, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_DATA_1, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_DATA_2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_DATA_3, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_ACCESS_CFG, 0x0f }, + { WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS, 0x03 }, + { WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_CFG, 0x0a }, + { WCD9335_CDC_VBAT_VBAT_ADC_CAL1, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_ADC_CAL2, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_ADC_CAL3, 0x04 }, + { WCD9335_CDC_VBAT_VBAT_PK_EST1, 0xe0 }, + { WCD9335_CDC_VBAT_VBAT_PK_EST2, 0x01 }, + { WCD9335_CDC_VBAT_VBAT_PK_EST3, 0x40 }, + { WCD9335_CDC_VBAT_VBAT_RF_PROC1, 0x2a }, + { WCD9335_CDC_VBAT_VBAT_RF_PROC2, 0x86 }, + { WCD9335_CDC_VBAT_VBAT_TAC1, 0x70 }, + { WCD9335_CDC_VBAT_VBAT_TAC2, 0x18 }, + { WCD9335_CDC_VBAT_VBAT_TAC3, 0x18 }, + { WCD9335_CDC_VBAT_VBAT_TAC4, 0x03 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD1, 0x01 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD2, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD3, 0x64 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD4, 0x01 }, + { WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD_MON, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL, 0x00 }, + { WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, 0x04 }, + { WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1, 0x00 }, + { WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, 0x04 }, + { WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1, 0x00 }, + /* Page #13 registers */ + { WCD9335_PAGE13_PAGE_REGISTER, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0x00 }, + { WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 0x00 }, + { WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 0x00 }, + { WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 0x00 }, + { WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 0x00 }, + { WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, 0x00 }, + { WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_CTL, 0x08 }, + { WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD0, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD1, 0x4b }, + { WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_STATUS, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_CTRL, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_CTL, 0x40 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_CTL, 0x40 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG0, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG1, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG2, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG3, 0x18 }, + { WCD9335_CDC_TOP_TOP_CFG4, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG5, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG6, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG7, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_WR_LSB, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_WR_MSB, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_LUT, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_RD_LSB, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_RD_MSB, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_WR_LSB, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_WR_MSB, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_LUT, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_RD_LSB, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_RD_MSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_WR_LSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_WR_MSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_LUT, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_RD_LSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_RD_MSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_WR_LSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_WR_MSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_LUT, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_RD_LSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_RD_MSB, 0x00 }, + /* Page #0x80 registers */ + { WCD9335_PAGE80_PAGE_REGISTER, 0x00 }, + { WCD9335_TLMM_BIST_MODE_PINCFG, 0x00 }, + { WCD9335_TLMM_RF_PA_ON_PINCFG, 0x00 }, + { WCD9335_TLMM_INTR1_PINCFG, 0x00 }, + { WCD9335_TLMM_INTR2_PINCFG, 0x00 }, + { WCD9335_TLMM_SWR_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_SWR_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_SLIMBUS_DATA2_PINCFG, 0x00 }, + { WCD9335_TLMM_I2C_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_I2C_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_RX_SD0_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_RX_SD1_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_RX_SCK_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_RX_WS_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_TX_SD0_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_TX_SD1_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_TX_SCK_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_TX_WS_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC1_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC1_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC2_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC2_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC3_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC3_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_JTDI_PINCFG, 0x00 }, + { WCD9335_TLMM_JTDO_PINCFG, 0x00 }, + { WCD9335_TLMM_JTMS_PINCFG, 0x00 }, + { WCD9335_TLMM_JTCK_PINCFG, 0x00 }, + { WCD9335_TLMM_JTRST_PINCFG, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_OE_0, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_OE_1, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_OE_2, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_OE_3, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_DATA_0, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_DATA_1, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_DATA_2, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_DATA_3, 0x00 }, + { WCD9335_TEST_DEBUG_PAD_DRVCTL, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_STATUS, 0x00 }, + { WCD9335_TEST_DEBUG_MEM_CTRL, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_BUS_SEL, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_JTAG, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_EN_1, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_EN_2, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_EN_3, 0x00 }, +}; + +/* + * wcd9335_regmap_register_patch: Update register defaults based on version + * @regmap: handle to wcd9xxx regmap + * @version: wcd9335 version + * + * Returns error code in case of failure or 0 for success + */ +int wcd9335_regmap_register_patch(struct regmap *regmap, int version) +{ + int rc; + + if (!regmap) { + pr_err("%s: regmap struct is NULL\n", __func__); + return -EINVAL; + } + + switch (version) { + case TASHA_VERSION_1_0: + case TASHA_VERSION_1_1: + regcache_cache_only(regmap, true); + rc = regmap_multi_reg_write(regmap, wcd9335_1_x_defaults, + ARRAY_SIZE(wcd9335_1_x_defaults)); + regcache_cache_only(regmap, false); + break; + case TASHA_VERSION_2_0: + regcache_cache_only(regmap, true); + rc = regmap_multi_reg_write(regmap, wcd9335_2_0_defaults, + ARRAY_SIZE(wcd9335_2_0_defaults)); + regcache_cache_only(regmap, false); + break; + default: + pr_err("%s: unknown version: %d\n", __func__, version); + rc = -EINVAL; + break; + } + + return rc; +} +EXPORT_SYMBOL(wcd9335_regmap_register_patch); + +static bool wcd9335_is_readable_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + /* + * Get the page number from MSB of codec register. If its 0x80, assign + * the corresponding page index PAGE_0x80. + */ + pg_num = reg >> 0x8; + if (pg_num == 0x80) + pg_num = PAGE_0X80; + else if (pg_num >= 0xE) + return false; + + reg_tbl = wcd9335_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl) + return reg_tbl[reg_offset]; + else + return false; +} + +static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg) +{ + /* + * registers from 0x000 to 0x0FF are volatile because + * this space contains registers related to interrupt + * status, mask etc + */ + if (reg < 0x100) + return true; + + /* IIR Coeff registers are not cacheable */ + if ((reg >= WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL) && + (reg <= WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)) + return true; + + if ((reg >= WCD9335_CDC_ANC0_IIR_COEFF_1_CTL) && + (reg <= WCD9335_CDC_ANC0_FB_GAIN_CTL)) + return true; + + if ((reg >= WCD9335_CDC_ANC1_IIR_COEFF_1_CTL) && + (reg <= WCD9335_CDC_ANC1_FB_GAIN_CTL)) + return true; + /* + * CPE inbox and outbox registers are volatile + * since they can be updated in the codec hardware + * to indicate CPE status + */ + if (reg >= WCD9335_CPE_SS_MEM_PTR_0 && + reg <= WCD9335_CPE_SS_OUTBOX2_ACK) + return true; + + if (reg >= WCD9335_RCO_CAL_OUT_1 && + reg <= WCD9335_RCO_CAL_OUT_5) + return true; + + switch (reg) { + case WCD9335_CPE_SS_INBOX1_TRG: + case WCD9335_CPE_SS_INBOX2_TRG: + case WCD9335_SWR_AHB_BRIDGE_WR_DATA_0: + case WCD9335_SWR_AHB_BRIDGE_WR_DATA_1: + case WCD9335_SWR_AHB_BRIDGE_WR_DATA_2: + case WCD9335_SWR_AHB_BRIDGE_WR_DATA_3: + case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0: + case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1: + case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2: + case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_0: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_1: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_2: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_3: + case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0: + case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1: + case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2: + case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3: + case WCD9335_ANA_BIAS: + case WCD9335_ANA_CLK_TOP: + case WCD9335_ANA_RCO: + case WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL: + case WCD9335_ANA_MBHC_RESULT_3: + case WCD9335_ANA_MBHC_RESULT_2: + case WCD9335_ANA_MBHC_RESULT_1: + case WCD9335_ANA_MBHC_MECH: + case WCD9335_ANA_MBHC_ELECT: + case WCD9335_ANA_MBHC_ZDET: + case WCD9335_ANA_MICB2: + case WCD9335_CPE_SS_SS_ERROR_INT_STATUS: + case WCD9335_CPE_SS_SS_ERROR_INT_MASK: + case WCD9335_CPE_SS_SS_ERROR_INT_CLEAR: + case WCD9335_CPE_SS_STATUS: + case WCD9335_CPE_SS_BACKUP_INT: + case WCD9335_CPE_SS_CFG: + case WCD9335_SOC_MAD_MAIN_CTL_1: + case WCD9335_SOC_MAD_AUDIO_CTL_3: + case WCD9335_SOC_MAD_AUDIO_CTL_4: + case WCD9335_FLYBACK_EN: + case WCD9335_ANA_RX_SUPPLIES: + case WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL: + case WCD9335_SIDO_SIDO_CCL_2: + case WCD9335_SIDO_SIDO_CCL_4: + case WCD9335_DATA_HUB_NATIVE_FIFO_STATUS: + case WCD9335_MBHC_FSM_STATUS: + case WCD9335_SPLINE_SRC0_STATUS: + case WCD9335_SPLINE_SRC1_STATUS: + case WCD9335_SPLINE_SRC2_STATUS: + case WCD9335_SPLINE_SRC3_STATUS: + case WCD9335_SIDO_SIDO_TEST_2: + case WCD9335_SIDO_SIDO_CCL_8: + case WCD9335_BIAS_VBG_FINE_ADJ: + case WCD9335_VBADC_ADC_DOUTMSB: + case WCD9335_VBADC_ADC_DOUTLSB: + case WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL: + case WCD9335_ANA_BUCK_CTL: + return true; + default: + return false; + } +} + +struct regmap_config wcd9335_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd9335_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd9335_defaults), + .max_register = WCD9335_MAX_REGISTER, + .volatile_reg = wcd9335_is_volatile_register, + .readable_reg = wcd9335_is_readable_register, + .can_multi_write = true, +}; diff --git a/drivers/mfd/wcd9335-tables.c b/drivers/mfd/wcd9335-tables.c new file mode 100644 index 0000000000000000000000000000000000000000..f5c32efe76ef66d6bade78ec1f42016318f213f9 --- /dev/null +++ b/drivers/mfd/wcd9335-tables.c @@ -0,0 +1,1325 @@ +/* + * Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#define WCD9335_REG(reg) ((reg) & 0xFF) + +const u8 wcd9335_page0_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE0_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_CLK_BYPASS)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_CLK_GATE)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_CLK_MCLK_CFG)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_RST_CTL)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_1)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_2)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_3)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_INT_MASK)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_INT_STATUS)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_INT_CLEAR)] = 0, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE1)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE3)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_TEST0)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_TEST1)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT3)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT4)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT5)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT6)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT7)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT8)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT9)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT13)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT14)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT15)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_1)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_2)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_3)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_ACTIVE)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_STATUS)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_MSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_LSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_STATUS)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_MSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_LSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_STATUS)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_MSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_LSB)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_I2S_CLK)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX4_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX5_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX6_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX7_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX0_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX1_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX2_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX3_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX4_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX5_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX6_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX7_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX8_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX9_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX10_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX14_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX15_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_NATIVE_FIFO_SYNC)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_NATIVE_FIFO_STATUS)] = 1, + [WCD9335_REG(WCD9335_INTR_CFG)] = 1, + [WCD9335_REG(WCD9335_INTR_CLR_COMMIT)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN1_MASK0)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_MASK1)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_MASK2)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_MASK3)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_STATUS0)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_STATUS1)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_STATUS2)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_STATUS3)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_CLEAR0)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN1_CLEAR1)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN1_CLEAR2)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN1_CLEAR3)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN2_MASK0)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_MASK1)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_MASK2)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_MASK3)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_STATUS0)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_STATUS1)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_STATUS2)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_STATUS3)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_CLEAR0)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN2_CLEAR1)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN2_CLEAR2)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN2_CLEAR3)] = 0, + [WCD9335_REG(WCD9335_INTR_LEVEL0)] = 1, + [WCD9335_REG(WCD9335_INTR_LEVEL1)] = 1, + [WCD9335_REG(WCD9335_INTR_LEVEL2)] = 1, + [WCD9335_REG(WCD9335_INTR_LEVEL3)] = 1, + [WCD9335_REG(WCD9335_INTR_BYPASS0)] = 1, + [WCD9335_REG(WCD9335_INTR_BYPASS1)] = 1, + [WCD9335_REG(WCD9335_INTR_BYPASS2)] = 1, + [WCD9335_REG(WCD9335_INTR_BYPASS3)] = 1, + [WCD9335_REG(WCD9335_INTR_SET0)] = 1, + [WCD9335_REG(WCD9335_INTR_SET1)] = 1, + [WCD9335_REG(WCD9335_INTR_SET2)] = 1, + [WCD9335_REG(WCD9335_INTR_SET3)] = 1, +}; + +const u8 wcd9335_page1_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE1_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_4)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_5)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_6)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_7)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_8)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_9)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_L_VAL_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_L_VAL_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_DSM_FRAC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_DSM_FRAC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_4)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_4)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_5)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_6)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_7)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FLL_MODE)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_STATUS_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_STATUS_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_STATUS_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_STATUS_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_4)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_5)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_6)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_7)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_8)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_9)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_L_VAL_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_L_VAL_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_DSM_FRAC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_DSM_FRAC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_4)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_4)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_5)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_6)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_7)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FLL_MODE)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_STATUS_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_STATUS_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_STATUS_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_STATUS_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_8)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_9)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_L_VAL_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_L_VAL_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_DSM_FRAC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_DSM_FRAC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FLL_MODE)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_STATUS_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_STATUS_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_STATUS_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_STATUS_3)] = 1, +}; + +const u8 wcd9335_page2_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE2_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_PTR_0)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_PTR_1)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_PTR_2)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_CTRL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_0)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_1)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_2)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_3)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_4)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_5)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_6)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_7)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_8)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_9)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_10)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_11)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_12)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_13)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_14)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_15)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_TRG)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_TRG)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_0)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_1)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_2)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_3)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_4)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_5)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_6)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_7)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_8)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_9)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_10)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_11)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_12)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_13)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_14)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_15)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_0)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_1)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_2)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_3)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_4)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_5)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_6)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_7)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_8)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_9)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_10)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_11)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_12)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_13)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_14)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_15)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_0)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_1)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_2)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_3)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_4)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_5)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_6)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_7)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_8)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_9)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_10)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_11)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_12)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_13)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_14)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_15)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_0)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_1)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_2)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_3)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_4)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_5)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_6)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_7)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_8)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_9)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_10)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_11)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_12)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_13)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_14)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_15)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_ACK)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_ACK)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_EC_BUF_INT_PERIOD)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_US_BUF_INT_PERIOD)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_US_EC_MUX_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MAD_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CPAR_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_TX_PP_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_DMIC0_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_DMIC1_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_DMIC2_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_DMIC_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_SVA_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CPAR_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_WDOG_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_BACKUP_INT)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_STATUS)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CPE_OCD_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_SS_ERROR_INT_MASK)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_SS_ERROR_INT_STATUS)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_SS_ERROR_INT_CLEAR)] = 0, + [WCD9335_REG(WCD9335_SOC_MAD_MAIN_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_MAIN_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_8)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_8)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_INP_SEL)] = 1, +}; + +const u8 wcd9335_page6_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE6_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_ANA_BIAS)] = 1, + [WCD9335_REG(WCD9335_ANA_CLK_TOP)] = 1, + [WCD9335_REG(WCD9335_ANA_RCO)] = 1, + [WCD9335_REG(WCD9335_ANA_BUCK_VOUT_A)] = 1, + [WCD9335_REG(WCD9335_ANA_BUCK_VOUT_D)] = 1, + [WCD9335_REG(WCD9335_ANA_BUCK_CTL)] = 1, + [WCD9335_REG(WCD9335_ANA_BUCK_STATUS)] = 1, + [WCD9335_REG(WCD9335_ANA_RX_SUPPLIES)] = 1, + [WCD9335_REG(WCD9335_ANA_HPH)] = 1, + [WCD9335_REG(WCD9335_ANA_EAR)] = 1, + [WCD9335_REG(WCD9335_ANA_LO_1_2)] = 1, + [WCD9335_REG(WCD9335_ANA_LO_3_4)] = 1, + [WCD9335_REG(WCD9335_ANA_MAD_SETUP)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC1)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC2)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC3)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC4)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC5)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC6)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_MECH)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_ELECT)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_ZDET)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_RESULT_1)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_RESULT_2)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_RESULT_3)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN0)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN1)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN2)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN3)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN4)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN5)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN6)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN7)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB1)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB2)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB2_RAMP)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB3)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB4)] = 1, + [WCD9335_REG(WCD9335_ANA_VBADC)] = 1, + [WCD9335_REG(WCD9335_BIAS_CTL)] = 1, + [WCD9335_REG(WCD9335_BIAS_VBG_FINE_ADJ)] = 1, + [WCD9335_REG(WCD9335_CLOCK_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_RCO_CTRL_1)] = 1, + [WCD9335_REG(WCD9335_RCO_CTRL_2)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_1)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_2)] = 1, + [WCD9335_REG(WCD9335_RCO_TEST_CTRL)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_1)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_2)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_3)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_4)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_5)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_MODE_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_MODE_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_MODE_3)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_MODE_4)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_VCL_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_VCL_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_VCL_3)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_3)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_4)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_5)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_6)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_7)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_8)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_9)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_10)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_FILTER_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_FILTER_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_DRIVER_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_DRIVER_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_DRIVER_3)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_EXT_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_EXT_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_OUT_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_OUT_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_TEST_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_TEST_2)] = 1, + [WCD9335_REG(WCD9335_MBHC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MBHC_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MBHC_PLUG_DETECT_CTL)] = 1, + [WCD9335_REG(WCD9335_MBHC_ZDET_ANA_CTL)] = 1, + [WCD9335_REG(WCD9335_MBHC_ZDET_RAMP_CTL)] = 1, + [WCD9335_REG(WCD9335_MBHC_FSM_DEBUG)] = 1, + [WCD9335_REG(WCD9335_MBHC_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_VBADC_SUBBLOCK_EN)] = 1, + [WCD9335_REG(WCD9335_VBADC_IBIAS_FE)] = 1, + [WCD9335_REG(WCD9335_VBADC_BIAS_ADC)] = 1, + [WCD9335_REG(WCD9335_VBADC_FE_CTRL)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_REF)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_IO)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_SAR)] = 1, + [WCD9335_REG(WCD9335_VBADC_DEBUG)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_DOUTMSB)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_DOUTLSB)] = 1, + [WCD9335_REG(WCD9335_LDOH_MODE)] = 1, + [WCD9335_REG(WCD9335_LDOH_BIAS)] = 1, + [WCD9335_REG(WCD9335_LDOH_STB_LOADS)] = 1, + [WCD9335_REG(WCD9335_LDOH_SLOWRAMP)] = 1, + [WCD9335_REG(WCD9335_MICB1_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MICB1_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MICB1_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_MICB2_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MICB2_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MICB2_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_MICB3_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MICB3_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MICB3_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_MICB4_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MICB4_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MICB4_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_TX_COM_ADC_VCM)] = 1, + [WCD9335_REG(WCD9335_TX_COM_BIAS_ATEST)] = 1, + [WCD9335_REG(WCD9335_TX_COM_ADC_INT1_IB)] = 1, + [WCD9335_REG(WCD9335_TX_COM_ADC_INT2_IB)] = 1, + [WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_CTL)] = 1, + [WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_START)] = 1, + [WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_STOP_9P6M)] = 1, + [WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_STOP_12P288M)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_TEST_EN)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_ADC_IB)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_ATEST_REFCTL)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_TEST_BLK_EN)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_TXFE_CLKDIV)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_SAR1_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_SAR2_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_TEST_EN)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_ADC_IB)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_ATEST_REFCTL)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_TEST_BLK_EN)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_TXFE_CLKDIV)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_SAR1_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_SAR2_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_TEST_EN)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_ADC_IB)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_ATEST_REFCTL)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_TEST_BLK_EN)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_TXFE_CLKDIV)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_SAR1_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_SAR2_ERR)] = 1, + [WCD9335_REG(WCD9335_CLASSH_MODE_1)] = 1, + [WCD9335_REG(WCD9335_CLASSH_MODE_2)] = 1, + [WCD9335_REG(WCD9335_CLASSH_MODE_3)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_VCL_1)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_VCL_2)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_1)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_2)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_3)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_4)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_5)] = 1, + [WCD9335_REG(WCD9335_CLASSH_BUCK_TMUX_A_D)] = 1, + [WCD9335_REG(WCD9335_CLASSH_BUCK_SW_DRV_CNTL)] = 1, + [WCD9335_REG(WCD9335_CLASSH_SPARE)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_EN)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_1)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_2)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_3)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_4)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_5)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_6)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_7)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_8)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_9)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_1)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_2)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_3)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_4)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_RX_AUX_SW_CTL)] = 1, + [WCD9335_REG(WCD9335_RX_PA_AUX_IN_CONN)] = 1, + [WCD9335_REG(WCD9335_RX_TIMER_DIV)] = 1, + [WCD9335_REG(WCD9335_RX_OCP_CTL)] = 1, + [WCD9335_REG(WCD9335_RX_OCP_COUNT)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_EAR_DAC)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_EAR_AMP)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_LDO)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_PA)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_RDAC_LDO)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_CNP1)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_LOWPOWER)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_DIFFLO_PA)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_DIFFLO_REF)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_DIFFLO_LDO)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_SELO_DAC_PA)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_BUCK_RST)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_BUCK_VREF_ERRAMP)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_FLYB_ERRAMP)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_FLYB_BUFF)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_FLYB_MID_RST)] = 1, + [WCD9335_REG(WCD9335_HPH_L_STATUS)] = 1, + [WCD9335_REG(WCD9335_HPH_R_STATUS)] = 1, + [WCD9335_REG(WCD9335_HPH_CNP_EN)] = 1, + [WCD9335_REG(WCD9335_HPH_CNP_WG_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_CNP_WG_TIME)] = 1, + [WCD9335_REG(WCD9335_HPH_OCP_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_AUTO_CHOP)] = 1, + [WCD9335_REG(WCD9335_HPH_CHOP_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_PA_CTL1)] = 1, + [WCD9335_REG(WCD9335_HPH_PA_CTL2)] = 1, + [WCD9335_REG(WCD9335_HPH_L_EN)] = 1, + [WCD9335_REG(WCD9335_HPH_L_TEST)] = 1, + [WCD9335_REG(WCD9335_HPH_L_ATEST)] = 1, + [WCD9335_REG(WCD9335_HPH_R_EN)] = 1, + [WCD9335_REG(WCD9335_HPH_R_TEST)] = 1, + [WCD9335_REG(WCD9335_HPH_R_ATEST)] = 1, + [WCD9335_REG(WCD9335_HPH_RDAC_CLK_CTL1)] = 1, + [WCD9335_REG(WCD9335_HPH_RDAC_CLK_CTL2)] = 1, + [WCD9335_REG(WCD9335_HPH_RDAC_LDO_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_RDAC_CHOP_CLK_LP_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_REFBUFF_UHQA_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_REFBUFF_LP_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_L_DAC_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_R_DAC_CTL)] = 1, + [WCD9335_REG(WCD9335_EAR_EN_REG)] = 1, + [WCD9335_REG(WCD9335_EAR_CMBUFF)] = 1, + [WCD9335_REG(WCD9335_EAR_ICTL)] = 1, + [WCD9335_REG(WCD9335_EAR_EN_DBG_CTL)] = 1, + [WCD9335_REG(WCD9335_EAR_CNP)] = 1, + [WCD9335_REG(WCD9335_EAR_DAC_CTL_ATEST)] = 1, + [WCD9335_REG(WCD9335_EAR_STATUS_REG)] = 1, + [WCD9335_REG(WCD9335_EAR_OUT_SHORT)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_MISC)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LO2_COMPANDER)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LO1_COMPANDER)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_COMMON)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_BYPASS_EN)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_CNP)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_CORE_OUT_PROG)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LDO_OUT_PROG)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_COM_PA_FREQ)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_RESERVED_REG)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LO1_STATUS_1)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LO1_STATUS_2)] = 1, + [WCD9335_REG(WCD9335_SE_LO_COM1)] = 1, + [WCD9335_REG(WCD9335_SE_LO_COM2)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO3_GAIN)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO3_CTRL)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO4_GAIN)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO4_CTRL)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO3_STATUS)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO4_STATUS)] = 1, +}; + +const u8 wcd9335_page10_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE10_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_CLK_RESET_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_MODE_1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_MODE_2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FF_SHIFT)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FB_SHIFT)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_LPF_FF_A_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_LPF_FF_B_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_LPF_FB_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_SMLPF_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_DCFLT_SHIFT_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_IIR_ADAPT_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_IIR_COEFF_1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_IIR_COEFF_2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FF_A_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FF_B_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FB_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_CLK_RESET_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_MODE_1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_MODE_2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FF_SHIFT)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FB_SHIFT)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_LPF_FF_A_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_LPF_FF_B_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_LPF_FB_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_SMLPF_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_DCFLT_SHIFT_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_IIR_ADAPT_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_IIR_COEFF_1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_IIR_COEFF_2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FF_A_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FF_B_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FB_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0)] = 1, +}; + +const u8 wcd9335_page11_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE11_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_SEC1)] = 1, +}; + +const u8 wcd9335_page12_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE12_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_CRC)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_DLY_CTRL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_DECAY_CTRL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_HPH_V_PA)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_EAR_V_PA)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_HPH_V_HD)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_EAR_V_HD)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_K1_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_K1_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_K2_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_K2_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_IDLE_CTRL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_IDLE_HPH)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_IDLE_EAR)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_TEST0)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_TEST1)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_OVR_VREF)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_CFG2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_0)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_1)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_3)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_0)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_1)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_3)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_ACCESS_CFG)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_ADC_CAL1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_ADC_CAL2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_ADC_CAL3)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PK_EST1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PK_EST2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PK_EST3)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_RF_PROC1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_RF_PROC2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC3)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC4)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD3)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD4)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_DEBUG1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD_MON)] = 0, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC0_CLK_RST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC0_STATUS)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC1_CLK_RST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC1_STATUS)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC2_CLK_RST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC2_STATUS)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC3_CLK_RST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC3_STATUS)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1)] = 1, +}; + +const u8 wcd9335_page13_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE13_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_ANC_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD0)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD1)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_STATUS)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_CTRL)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG4)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG5)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG6)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG7)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_WR_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_WR_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_LUT)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_RD_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_RD_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_WR_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_WR_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_LUT)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_RD_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_RD_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_WR_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_WR_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_LUT)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_RD_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_RD_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_WR_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_WR_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_LUT)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_RD_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_RD_MSB)] = 1, +}; + +const u8 wcd9335_page_0x80_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE80_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_TLMM_BIST_MODE_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_RF_PA_ON_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_INTR1_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_INTR2_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_SWR_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_SWR_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_SLIMBUS_DATA2_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2C_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2C_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_RX_SD0_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_RX_SD1_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_RX_SCK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_RX_WS_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_TX_SD0_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_TX_SD1_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_TX_SCK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_TX_WS_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC1_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC1_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC2_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC2_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC3_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC3_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTDI_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTDO_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTMS_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTCK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTRST_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_0)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_1)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_2)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_3)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_0)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_1)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_2)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_3)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PAD_DRVCTL)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_STATUS)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_NPL_DLY_TEST_1)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_NPL_DLY_TEST_2)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_MEM_CTRL)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_BUS_SEL)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_JTAG)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_EN_1)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_EN_2)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_EN_3)] = 1, +}; + +const u8 *wcd9335_reg[WCD9335_NUM_PAGES] = { + [PAGE_0] = wcd9335_page0_reg_readable, + [PAGE_1] = wcd9335_page1_reg_readable, + [PAGE_2] = wcd9335_page2_reg_readable, + [PAGE_6] = wcd9335_page6_reg_readable, + [PAGE_10] = wcd9335_page10_reg_readable, + [PAGE_11] = wcd9335_page11_reg_readable, + [PAGE_12] = wcd9335_page12_reg_readable, + [PAGE_13] = wcd9335_page13_reg_readable, + [PAGE_0X80] = wcd9335_page_0x80_reg_readable, +}; diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c new file mode 100644 index 0000000000000000000000000000000000000000..fbaf05e58affe9df58cb29537a43354d1ff877ec --- /dev/null +++ b/drivers/mfd/wcd934x-regmap.c @@ -0,0 +1,1945 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include "wcd9xxx-regmap.h" + + +static const struct reg_sequence wcd934x_1_1_defaults[] = { + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x01 }, + { WCD934X_BIAS_VBG_FINE_ADJ, 0x75 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0E }, + { WCD934X_EAR_DAC_CTL_ATEST, 0x08 }, + { WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x17 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x81 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x81 }, +}; + +static const struct reg_default wcd934x_defaults[] = { + { WCD934X_PAGE0_PAGE_REGISTER, 0x00 }, + { WCD934X_CODEC_RPM_CLK_BYPASS, 0x00 }, + { WCD934X_CODEC_RPM_CLK_GATE, 0x1f }, + { WCD934X_CODEC_RPM_CLK_MCLK_CFG, 0x00 }, + { WCD934X_CODEC_RPM_CLK_MCLK2_CFG, 0x02 }, + { WCD934X_CODEC_RPM_I2S_DSD_CLK_SEL, 0x00 }, + { WCD934X_CODEC_RPM_RST_CTL, 0x00 }, + { WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x04 }, + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE1, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2, 0x08 }, + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE3, 0x01 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x10 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_TEST0, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_TEST1, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT3, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT4, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT5, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT6, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT7, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT8, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT10, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT11, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT12, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT13, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO, 0x0d }, + { WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_1, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_2, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_3, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL, 0xcc }, + { WCD934X_CHIP_TIER_CTRL_SLNQ_WAIT_STATE_CTL, 0xcc }, + { WCD934X_CHIP_TIER_CTRL_I2C_ACTIVE, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA, 0x00 }, + { WCD934X_DATA_HUB_RX0_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX1_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX2_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX3_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX4_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX5_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX6_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX7_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX0_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX1_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX2_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX3_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX4_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX5_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX6_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX7_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX8_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX9_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX10_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX14_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX15_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_I2S_TX0_CFG, 0x00 }, + { WCD934X_DATA_HUB_I2S_TX1_0_CFG, 0x00 }, + { WCD934X_DATA_HUB_I2S_TX1_1_CFG, 0x00 }, + { WCD934X_DATA_HUB_I2S_0_CTL, 0x0c }, + { WCD934X_DATA_HUB_I2S_1_CTL, 0x0c }, + { WCD934X_DATA_HUB_I2S_2_CTL, 0x0c }, + { WCD934X_DATA_HUB_I2S_3_CTL, 0x0c }, + { WCD934X_DATA_HUB_I2S_CLKSRC_CTL, 0x00 }, + { WCD934X_DATA_HUB_I2S_COMMON_CTL, 0x00 }, + { WCD934X_DATA_HUB_I2S_0_TDM_CTL, 0x00 }, + { WCD934X_DATA_HUB_I2S_STATUS, 0x00 }, + { WCD934X_DMA_RDMA_CTL_0, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_0, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_0, 0xff }, + { WCD934X_DMA_RDMA_CTL_1, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_1, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_1, 0xff }, + { WCD934X_DMA_RDMA_CTL_2, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_2, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_2, 0xff }, + { WCD934X_DMA_RDMA_CTL_3, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_3, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_3, 0xff }, + { WCD934X_DMA_RDMA_CTL_4, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_4, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_4, 0xff }, + { WCD934X_DMA_RDMA4_PRT_CFG, 0x00 }, + { WCD934X_DMA_RDMA_SBTX0_7_CFG, 0x00 }, + { WCD934X_DMA_RDMA_SBTX8_11_CFG, 0x00 }, + { WCD934X_DMA_WDMA_CTL_0, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_0, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_0, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_0, 0x00 }, + { WCD934X_DMA_WDMA_CTL_1, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_1, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_1, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_1, 0x00 }, + { WCD934X_DMA_WDMA_CTL_2, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_2, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_2, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_2, 0x00 }, + { WCD934X_DMA_WDMA_CTL_3, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_3, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_3, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_3, 0x00 }, + { WCD934X_DMA_WDMA_CTL_4, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_4, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_4, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_4, 0x00 }, + { WCD934X_DMA_WDMA0_PRT_CFG, 0x00 }, + { WCD934X_DMA_WDMA3_PRT_CFG, 0x00 }, + { WCD934X_DMA_WDMA4_PRT0_3_CFG, 0x00 }, + { WCD934X_DMA_WDMA4_PRT4_7_CFG, 0x00 }, + { WCD934X_PAGE1_PAGE_REGISTER, 0x00 }, + { WCD934X_CPE_FLL_USER_CTL_0, 0x71 }, + { WCD934X_CPE_FLL_USER_CTL_1, 0x34 }, + { WCD934X_CPE_FLL_USER_CTL_2, 0x0b }, + { WCD934X_CPE_FLL_USER_CTL_3, 0x02 }, + { WCD934X_CPE_FLL_USER_CTL_4, 0x04 }, + { WCD934X_CPE_FLL_USER_CTL_5, 0x02 }, + { WCD934X_CPE_FLL_USER_CTL_6, 0x6e }, + { WCD934X_CPE_FLL_USER_CTL_7, 0x00 }, + { WCD934X_CPE_FLL_USER_CTL_8, 0x94 }, + { WCD934X_CPE_FLL_USER_CTL_9, 0x50 }, + { WCD934X_CPE_FLL_L_VAL_CTL_0, 0x53 }, + { WCD934X_CPE_FLL_L_VAL_CTL_1, 0x00 }, + { WCD934X_CPE_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD934X_CPE_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD934X_CPE_FLL_CONFIG_CTL_0, 0x6b }, + { WCD934X_CPE_FLL_CONFIG_CTL_1, 0x05 }, + { WCD934X_CPE_FLL_CONFIG_CTL_2, 0x08 }, + { WCD934X_CPE_FLL_CONFIG_CTL_3, 0x00 }, + { WCD934X_CPE_FLL_CONFIG_CTL_4, 0x10 }, + { WCD934X_CPE_FLL_TEST_CTL_0, 0x80 }, + { WCD934X_CPE_FLL_TEST_CTL_1, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_2, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_3, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_4, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_5, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_6, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_7, 0x33 }, + { WCD934X_CPE_FLL_FREQ_CTL_0, 0x00 }, + { WCD934X_CPE_FLL_FREQ_CTL_1, 0x00 }, + { WCD934X_CPE_FLL_FREQ_CTL_2, 0x00 }, + { WCD934X_CPE_FLL_FREQ_CTL_3, 0x00 }, + { WCD934X_CPE_FLL_SSC_CTL_0, 0x00 }, + { WCD934X_CPE_FLL_SSC_CTL_1, 0x00 }, + { WCD934X_CPE_FLL_SSC_CTL_2, 0x00 }, + { WCD934X_CPE_FLL_SSC_CTL_3, 0x00 }, + { WCD934X_CPE_FLL_FLL_MODE, 0x20 }, + { WCD934X_CPE_FLL_STATUS_0, 0x00 }, + { WCD934X_CPE_FLL_STATUS_1, 0x00 }, + { WCD934X_CPE_FLL_STATUS_2, 0x00 }, + { WCD934X_CPE_FLL_STATUS_3, 0x00 }, + { WCD934X_I2S_FLL_USER_CTL_0, 0x41 }, + { WCD934X_I2S_FLL_USER_CTL_1, 0x94 }, + { WCD934X_I2S_FLL_USER_CTL_2, 0x08 }, + { WCD934X_I2S_FLL_USER_CTL_3, 0x02 }, + { WCD934X_I2S_FLL_USER_CTL_4, 0x04 }, + { WCD934X_I2S_FLL_USER_CTL_5, 0x02 }, + { WCD934X_I2S_FLL_USER_CTL_6, 0x40 }, + { WCD934X_I2S_FLL_USER_CTL_7, 0x00 }, + { WCD934X_I2S_FLL_USER_CTL_8, 0x5f }, + { WCD934X_I2S_FLL_USER_CTL_9, 0x02 }, + { WCD934X_I2S_FLL_L_VAL_CTL_0, 0x40 }, + { WCD934X_I2S_FLL_L_VAL_CTL_1, 0x00 }, + { WCD934X_I2S_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD934X_I2S_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD934X_I2S_FLL_CONFIG_CTL_0, 0x6b }, + { WCD934X_I2S_FLL_CONFIG_CTL_1, 0x05 }, + { WCD934X_I2S_FLL_CONFIG_CTL_2, 0x08 }, + { WCD934X_I2S_FLL_CONFIG_CTL_3, 0x00 }, + { WCD934X_I2S_FLL_CONFIG_CTL_4, 0x30 }, + { WCD934X_I2S_FLL_TEST_CTL_0, 0x80 }, + { WCD934X_I2S_FLL_TEST_CTL_1, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_2, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_3, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_4, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_5, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_6, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_7, 0xff }, + { WCD934X_I2S_FLL_FREQ_CTL_0, 0x00 }, + { WCD934X_I2S_FLL_FREQ_CTL_1, 0x00 }, + { WCD934X_I2S_FLL_FREQ_CTL_2, 0x00 }, + { WCD934X_I2S_FLL_FREQ_CTL_3, 0x00 }, + { WCD934X_I2S_FLL_SSC_CTL_0, 0x00 }, + { WCD934X_I2S_FLL_SSC_CTL_1, 0x00 }, + { WCD934X_I2S_FLL_SSC_CTL_2, 0x00 }, + { WCD934X_I2S_FLL_SSC_CTL_3, 0x00 }, + { WCD934X_I2S_FLL_FLL_MODE, 0x00 }, + { WCD934X_I2S_FLL_STATUS_0, 0x00 }, + { WCD934X_I2S_FLL_STATUS_1, 0x00 }, + { WCD934X_I2S_FLL_STATUS_2, 0x00 }, + { WCD934X_I2S_FLL_STATUS_3, 0x00 }, + { WCD934X_SB_FLL_USER_CTL_0, 0x41 }, + { WCD934X_SB_FLL_USER_CTL_1, 0x94 }, + { WCD934X_SB_FLL_USER_CTL_2, 0x08 }, + { WCD934X_SB_FLL_USER_CTL_3, 0x02 }, + { WCD934X_SB_FLL_USER_CTL_4, 0x04 }, + { WCD934X_SB_FLL_USER_CTL_5, 0x02 }, + { WCD934X_SB_FLL_USER_CTL_6, 0x40 }, + { WCD934X_SB_FLL_USER_CTL_7, 0x00 }, + { WCD934X_SB_FLL_USER_CTL_8, 0x5e }, + { WCD934X_SB_FLL_USER_CTL_9, 0x01 }, + { WCD934X_SB_FLL_L_VAL_CTL_0, 0x40 }, + { WCD934X_SB_FLL_L_VAL_CTL_1, 0x00 }, + { WCD934X_SB_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD934X_SB_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD934X_SB_FLL_CONFIG_CTL_0, 0x6b }, + { WCD934X_SB_FLL_CONFIG_CTL_1, 0x05 }, + { WCD934X_SB_FLL_CONFIG_CTL_2, 0x08 }, + { WCD934X_SB_FLL_CONFIG_CTL_3, 0x00 }, + { WCD934X_SB_FLL_CONFIG_CTL_4, 0x10 }, + { WCD934X_SB_FLL_TEST_CTL_0, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_1, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_2, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_3, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_4, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_5, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_6, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_7, 0xff }, + { WCD934X_SB_FLL_FREQ_CTL_0, 0x00 }, + { WCD934X_SB_FLL_FREQ_CTL_1, 0x00 }, + { WCD934X_SB_FLL_FREQ_CTL_2, 0x00 }, + { WCD934X_SB_FLL_FREQ_CTL_3, 0x00 }, + { WCD934X_SB_FLL_SSC_CTL_0, 0x00 }, + { WCD934X_SB_FLL_SSC_CTL_1, 0x00 }, + { WCD934X_SB_FLL_SSC_CTL_2, 0x00 }, + { WCD934X_SB_FLL_SSC_CTL_3, 0x00 }, + { WCD934X_SB_FLL_FLL_MODE, 0x00 }, + { WCD934X_SB_FLL_STATUS_0, 0x00 }, + { WCD934X_SB_FLL_STATUS_1, 0x00 }, + { WCD934X_SB_FLL_STATUS_2, 0x00 }, + { WCD934X_SB_FLL_STATUS_3, 0x00 }, + { WCD934X_PAGE2_PAGE_REGISTER, 0x00 }, + { WCD934X_CPE_SS_CPE_CTL, 0x05 }, + { WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, 0x01 }, + { WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1, 0x00 }, + { WCD934X_CPE_SS_PWR_CPEFLL_CTL, 0x02 }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0f }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE, 0x00 }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, 0x07 }, + { WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, 0x00 }, + { WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL, 0x20 }, + { WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL1, 0x00 }, + { WCD934X_CPE_SS_US_BUF_INT_PERIOD, 0x60 }, + { WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x13 }, + { WCD934X_CPE_SS_SVA_CFG, 0x41 }, + { WCD934X_CPE_SS_US_CFG, 0x00 }, + { WCD934X_CPE_SS_MAD_CTL, 0x00 }, + { WCD934X_CPE_SS_CPAR_CTL, 0x00 }, + { WCD934X_CPE_SS_DMIC0_CTL, 0x00 }, + { WCD934X_CPE_SS_DMIC1_CTL, 0x00 }, + { WCD934X_CPE_SS_DMIC2_CTL, 0x00 }, + { WCD934X_CPE_SS_DMIC_CFG, 0x80 }, + { WCD934X_CPE_SS_CPAR_CFG, 0x00 }, + { WCD934X_CPE_SS_WDOG_CFG, 0x01 }, + { WCD934X_CPE_SS_BACKUP_INT, 0x00 }, + { WCD934X_CPE_SS_STATUS, 0x00 }, + { WCD934X_CPE_SS_CPE_OCD_CFG, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xff }, + { WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0x3f }, + { WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A, 0xff }, + { WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B, 0x3f }, + { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1A, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1B, 0x00 }, + { WCD934X_SOC_MAD_MAIN_CTL_1, 0x00 }, + { WCD934X_SOC_MAD_MAIN_CTL_2, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_1, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_2, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_3, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_4, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_5, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_6, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_7, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_8, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL, 0x40 }, + { WCD934X_SOC_MAD_ULTR_CTL_1, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_2, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_3, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_4, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_5, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_6, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_7, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_1, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_2, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_3, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_4, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_5, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_6, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_7, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_8, 0x00 }, + { WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR, 0x00 }, + { WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL, 0x00 }, + { WCD934X_SOC_MAD_INP_SEL, 0x00 }, + { WCD934X_PAGE4_PAGE_REGISTER, 0x00 }, + { WCD934X_INTR_CFG, 0x00 }, + { WCD934X_INTR_CLR_COMMIT, 0x00 }, + { WCD934X_INTR_PIN1_MASK0, 0xff }, + { WCD934X_INTR_PIN1_MASK1, 0xff }, + { WCD934X_INTR_PIN1_MASK2, 0xff }, + { WCD934X_INTR_PIN1_MASK3, 0xff }, + { WCD934X_INTR_PIN1_STATUS0, 0x00 }, + { WCD934X_INTR_PIN1_STATUS1, 0x00 }, + { WCD934X_INTR_PIN1_STATUS2, 0x00 }, + { WCD934X_INTR_PIN1_STATUS3, 0x00 }, + { WCD934X_INTR_PIN1_CLEAR0, 0x00 }, + { WCD934X_INTR_PIN1_CLEAR1, 0x00 }, + { WCD934X_INTR_PIN1_CLEAR2, 0x00 }, + { WCD934X_INTR_PIN1_CLEAR3, 0x00 }, + { WCD934X_INTR_PIN2_MASK3, 0xff }, + { WCD934X_INTR_PIN2_STATUS3, 0x00 }, + { WCD934X_INTR_PIN2_CLEAR3, 0x00 }, + { WCD934X_INTR_CPESS_SUMRY_MASK2, 0xff }, + { WCD934X_INTR_CPESS_SUMRY_MASK3, 0xff }, + { WCD934X_INTR_CPESS_SUMRY_STATUS2, 0x00 }, + { WCD934X_INTR_CPESS_SUMRY_STATUS3, 0x00 }, + { WCD934X_INTR_CPESS_SUMRY_CLEAR2, 0x00 }, + { WCD934X_INTR_CPESS_SUMRY_CLEAR3, 0x00 }, + { WCD934X_INTR_LEVEL0, 0x03 }, + { WCD934X_INTR_LEVEL1, 0xe0 }, + { WCD934X_INTR_LEVEL2, 0x94 }, + { WCD934X_INTR_LEVEL3, 0x80 }, + { WCD934X_INTR_BYPASS0, 0x00 }, + { WCD934X_INTR_BYPASS1, 0x00 }, + { WCD934X_INTR_BYPASS2, 0x00 }, + { WCD934X_INTR_BYPASS3, 0x00 }, + { WCD934X_INTR_SET0, 0x00 }, + { WCD934X_INTR_SET1, 0x00 }, + { WCD934X_INTR_SET2, 0x00 }, + { WCD934X_INTR_SET3, 0x00 }, + { WCD934X_INTR_CODEC_MISC_MASK, 0x7f }, + { WCD934X_INTR_CODEC_MISC_STATUS, 0x00 }, + { WCD934X_INTR_CODEC_MISC_CLEAR, 0x00 }, + { WCD934X_PAGE5_PAGE_REGISTER, 0x00 }, + { WCD934X_SLNQ_DIG_DEVICE, 0x49 }, + { WCD934X_SLNQ_DIG_REVISION, 0x01 }, + { WCD934X_SLNQ_DIG_H_COMMAND, 0x00 }, + { WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_MASTER_ADDRESS_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_MASTER_ADDRESS_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_SLAVE_ADDRESS_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_SLAVE_ADDRESS_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_MSB, 0x40 }, + { WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_MSB, 0x40 }, + { WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_MSB, 0x40 }, + { WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_COMM_CTL, 0x00 }, + { WCD934X_SLNQ_DIG_FRAME_CTRL, 0x01 }, + { WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH1_2, 0x77 }, + { WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH3_4, 0x77 }, + { WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH5, 0x70 }, + { WCD934X_SLNQ_DIG_SW_EVENT_RD, 0x00 }, + { WCD934X_SLNQ_DIG_SW_EVENT_CTRL, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_SELECT_1, 0x12 }, + { WCD934X_SLNQ_DIG_PDM_SELECT_2, 0x34 }, + { WCD934X_SLNQ_DIG_PDM_SELECT_3, 0x55 }, + { WCD934X_SLNQ_DIG_PDM_SAMPLING_FREQ, 0x01 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_CTL, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_SEL, 0x11 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_RAM_CNTRL, 0x01 }, + { WCD934X_SLNQ_DIG_SRAM_BANK, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_0, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_4, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_5, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_6, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_7, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_8, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_9, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_A, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_B, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_C, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_D, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_E, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_F, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_10, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_11, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_12, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_13, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_14, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_15, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_16, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_17, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_18, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_19, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1A, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1B, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1C, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1D, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1E, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1F, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_20, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_21, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_22, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_23, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_24, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_25, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_26, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_27, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_28, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_29, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2A, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2B, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2C, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2D, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2E, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2F, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_30, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_31, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_32, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_33, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_34, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_35, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_36, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_37, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_38, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_39, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3A, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3B, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3C, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3D, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3E, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3F, 0x00 }, + { WCD934X_SLNQ_DIG_TOP_CTRL1, 0x00 }, + { WCD934X_SLNQ_DIG_TOP_CTRL2, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_CTRL, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_MUTE_CTRL, 0x20 }, + { WCD934X_SLNQ_DIG_DEC_BYPASS_CTRL, 0x00 }, + { WCD934X_SLNQ_DIG_DEC_BYPASS_STATUS, 0x00 }, + { WCD934X_SLNQ_DIG_DEC_BYPASS_FS, 0x00 }, + { WCD934X_SLNQ_DIG_DEC_BYPASS_IN_SEL, 0x00 }, + { WCD934X_SLNQ_DIG_GPOUT_ENABLE, 0x00 }, + { WCD934X_SLNQ_DIG_GPOUT_VAL, 0x00 }, + { WCD934X_SLNQ_DIG_ANA_INTERRUPT_MASK, 0x00 }, + { WCD934X_SLNQ_DIG_ANA_INTERRUPT_STATUS, 0x00 }, + { WCD934X_SLNQ_DIG_ANA_INTERRUPT_CLR, 0x00 }, + { WCD934X_SLNQ_DIG_IP_TESTING, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CNTRL, 0x0f }, + { WCD934X_SLNQ_DIG_INTERRUPT_CNT, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CNT_MSB, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_CNT_LSB, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK0, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK1, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK2, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK3, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK4, 0x1f }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS0, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS1, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS2, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS3, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS4, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR0, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR1, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR2, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR3, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR4, 0x00 }, + { WCD934X_ANA_PAGE_REGISTER, 0x00 }, + { WCD934X_ANA_BIAS, 0x00 }, + { WCD934X_ANA_RCO, 0x00 }, + { WCD934X_ANA_PAGE6_SPARE2, 0x00 }, + { WCD934X_ANA_PAGE6_SPARE3, 0x00 }, + { WCD934X_ANA_BUCK_CTL, 0x00 }, + { WCD934X_ANA_BUCK_STATUS, 0x00 }, + { WCD934X_ANA_RX_SUPPLIES, 0x00 }, + { WCD934X_ANA_HPH, 0x0c }, + { WCD934X_ANA_EAR, 0x00 }, + { WCD934X_ANA_LO_1_2, 0x3c }, + { WCD934X_ANA_MAD_SETUP, 0x01 }, + { WCD934X_ANA_AMIC1, 0x20 }, + { WCD934X_ANA_AMIC2, 0x00 }, + { WCD934X_ANA_AMIC3, 0x20 }, + { WCD934X_ANA_AMIC4, 0x00 }, + { WCD934X_ANA_MBHC_MECH, 0x39 }, + { WCD934X_ANA_MBHC_ELECT, 0x08 }, + { WCD934X_ANA_MBHC_ZDET, 0x00 }, + { WCD934X_ANA_MBHC_RESULT_1, 0x00 }, + { WCD934X_ANA_MBHC_RESULT_2, 0x00 }, + { WCD934X_ANA_MBHC_RESULT_3, 0x00 }, + { WCD934X_ANA_MBHC_BTN0, 0x00 }, + { WCD934X_ANA_MBHC_BTN1, 0x10 }, + { WCD934X_ANA_MBHC_BTN2, 0x20 }, + { WCD934X_ANA_MBHC_BTN3, 0x30 }, + { WCD934X_ANA_MBHC_BTN4, 0x40 }, + { WCD934X_ANA_MBHC_BTN5, 0x50 }, + { WCD934X_ANA_MBHC_BTN6, 0x60 }, + { WCD934X_ANA_MBHC_BTN7, 0x70 }, + { WCD934X_ANA_MICB1, 0x10 }, + { WCD934X_ANA_MICB2, 0x10 }, + { WCD934X_ANA_MICB2_RAMP, 0x00 }, + { WCD934X_ANA_MICB3, 0x10 }, + { WCD934X_ANA_MICB4, 0x10 }, + { WCD934X_ANA_VBADC, 0x00 }, + { WCD934X_BIAS_CTL, 0x28 }, + { WCD934X_BIAS_VBG_FINE_ADJ, 0x65 }, + { WCD934X_RCO_CTRL_1, 0x44 }, + { WCD934X_RCO_CTRL_2, 0x48 }, + { WCD934X_RCO_CAL, 0x00 }, + { WCD934X_RCO_CAL_1, 0x00 }, + { WCD934X_RCO_CAL_2, 0x00 }, + { WCD934X_RCO_TEST_CTRL, 0x00 }, + { WCD934X_RCO_CAL_OUT_1, 0x00 }, + { WCD934X_RCO_CAL_OUT_2, 0x00 }, + { WCD934X_RCO_CAL_OUT_3, 0x00 }, + { WCD934X_RCO_CAL_OUT_4, 0x00 }, + { WCD934X_RCO_CAL_OUT_5, 0x00 }, + { WCD934X_SIDO_MODE_1, 0x84 }, + { WCD934X_SIDO_MODE_2, 0xfe }, + { WCD934X_SIDO_MODE_3, 0xf6 }, + { WCD934X_SIDO_MODE_4, 0x56 }, + { WCD934X_SIDO_VCL_1, 0x00 }, + { WCD934X_SIDO_VCL_2, 0x6c }, + { WCD934X_SIDO_VCL_3, 0x44 }, + { WCD934X_SIDO_CCL_1, 0x57 }, + { WCD934X_SIDO_CCL_2, 0x92 }, + { WCD934X_SIDO_CCL_3, 0x35 }, + { WCD934X_SIDO_CCL_4, 0x61 }, + { WCD934X_SIDO_CCL_5, 0x6d }, + { WCD934X_SIDO_CCL_6, 0x60 }, + { WCD934X_SIDO_CCL_7, 0x6f }, + { WCD934X_SIDO_CCL_8, 0x6f }, + { WCD934X_SIDO_CCL_9, 0x6e }, + { WCD934X_SIDO_CCL_10, 0x26 }, + { WCD934X_SIDO_FILTER_1, 0x92 }, + { WCD934X_SIDO_FILTER_2, 0x54 }, + { WCD934X_SIDO_DRIVER_1, 0x77 }, + { WCD934X_SIDO_DRIVER_2, 0x55 }, + { WCD934X_SIDO_DRIVER_3, 0x55 }, + { WCD934X_SIDO_CAL_CODE_EXT_1, 0x9c }, + { WCD934X_SIDO_CAL_CODE_EXT_2, 0x82 }, + { WCD934X_SIDO_CAL_CODE_OUT_1, 0x00 }, + { WCD934X_SIDO_CAL_CODE_OUT_2, 0x00 }, + { WCD934X_SIDO_TEST_1, 0x00 }, + { WCD934X_SIDO_TEST_2, 0x00 }, + { WCD934X_MBHC_CTL_CLK, 0x30 }, + { WCD934X_MBHC_CTL_ANA, 0x00 }, + { WCD934X_MBHC_CTL_SPARE_1, 0x00 }, + { WCD934X_MBHC_CTL_SPARE_2, 0x00 }, + { WCD934X_MBHC_CTL_BCS, 0x00 }, + { WCD934X_MBHC_STATUS_SPARE_1, 0x00 }, + { WCD934X_MBHC_TEST_CTL, 0x00 }, + { WCD934X_VBADC_SUBBLOCK_EN, 0xde }, + { WCD934X_VBADC_IBIAS_FE, 0x58 }, + { WCD934X_VBADC_BIAS_ADC, 0x51 }, + { WCD934X_VBADC_FE_CTRL, 0x1c }, + { WCD934X_VBADC_ADC_REF, 0x20 }, + { WCD934X_VBADC_ADC_IO, 0x80 }, + { WCD934X_VBADC_ADC_SAR, 0xff }, + { WCD934X_VBADC_DEBUG, 0x00 }, + { WCD934X_LDOH_MODE, 0x2b }, + { WCD934X_LDOH_BIAS, 0x68 }, + { WCD934X_LDOH_STB_LOADS, 0x00 }, + { WCD934X_LDOH_SLOWRAMP, 0x50 }, + { WCD934X_MICB1_TEST_CTL_1, 0x1a }, + { WCD934X_MICB1_TEST_CTL_2, 0x18 }, + { WCD934X_MICB1_TEST_CTL_3, 0xa4 }, + { WCD934X_MICB2_TEST_CTL_1, 0x1a }, + { WCD934X_MICB2_TEST_CTL_2, 0x18 }, + { WCD934X_MICB2_TEST_CTL_3, 0xa4 }, + { WCD934X_MICB3_TEST_CTL_1, 0x1a }, + { WCD934X_MICB3_TEST_CTL_2, 0x18 }, + { WCD934X_MICB3_TEST_CTL_3, 0xa4 }, + { WCD934X_MICB4_TEST_CTL_1, 0x1a }, + { WCD934X_MICB4_TEST_CTL_2, 0x18 }, + { WCD934X_MICB4_TEST_CTL_3, 0xa4 }, + { WCD934X_TX_COM_ADC_VCM, 0x39 }, + { WCD934X_TX_COM_BIAS_ATEST, 0xc0 }, + { WCD934X_TX_COM_ADC_INT1_IB, 0x6f }, + { WCD934X_TX_COM_ADC_INT2_IB, 0x4f }, + { WCD934X_TX_COM_TXFE_DIV_CTL, 0x2e }, + { WCD934X_TX_COM_TXFE_DIV_START, 0x00 }, + { WCD934X_TX_COM_TXFE_DIV_STOP_9P6M, 0xc7 }, + { WCD934X_TX_COM_TXFE_DIV_STOP_12P288M, 0xff }, + { WCD934X_TX_1_2_TEST_EN, 0xcc }, + { WCD934X_TX_1_2_ADC_IB, 0x09 }, + { WCD934X_TX_1_2_ATEST_REFCTL, 0x0a }, + { WCD934X_TX_1_2_TEST_CTL, 0x38 }, + { WCD934X_TX_1_2_TEST_BLK_EN, 0xff }, + { WCD934X_TX_1_2_TXFE_CLKDIV, 0x00 }, + { WCD934X_TX_1_2_SAR1_ERR, 0x00 }, + { WCD934X_TX_1_2_SAR2_ERR, 0x00 }, + { WCD934X_TX_3_4_TEST_EN, 0xcc }, + { WCD934X_TX_3_4_ADC_IB, 0x09 }, + { WCD934X_TX_3_4_ATEST_REFCTL, 0x0a }, + { WCD934X_TX_3_4_TEST_CTL, 0x38 }, + { WCD934X_TX_3_4_TEST_BLK_EN, 0xff }, + { WCD934X_TX_3_4_TXFE_CLKDIV, 0x00 }, + { WCD934X_TX_3_4_SAR1_ERR, 0x00 }, + { WCD934X_TX_3_4_SAR2_ERR, 0x00 }, + { WCD934X_CLASSH_MODE_1, 0x40 }, + { WCD934X_CLASSH_MODE_2, 0x3a }, + { WCD934X_CLASSH_MODE_3, 0x00 }, + { WCD934X_CLASSH_CTRL_VCL_1, 0x70 }, + { WCD934X_CLASSH_CTRL_VCL_2, 0x82 }, + { WCD934X_CLASSH_CTRL_CCL_1, 0x31 }, + { WCD934X_CLASSH_CTRL_CCL_2, 0x80 }, + { WCD934X_CLASSH_CTRL_CCL_3, 0x80 }, + { WCD934X_CLASSH_CTRL_CCL_4, 0x51 }, + { WCD934X_CLASSH_CTRL_CCL_5, 0x00 }, + { WCD934X_CLASSH_BUCK_TMUX_A_D, 0x00 }, + { WCD934X_CLASSH_BUCK_SW_DRV_CNTL, 0x77 }, + { WCD934X_CLASSH_SPARE, 0x00 }, + { WCD934X_FLYBACK_EN, 0x4e }, + { WCD934X_FLYBACK_VNEG_CTRL_1, 0x0b }, + { WCD934X_FLYBACK_VNEG_CTRL_2, 0x45 }, + { WCD934X_FLYBACK_VNEG_CTRL_3, 0x74 }, + { WCD934X_FLYBACK_VNEG_CTRL_4, 0x7f }, + { WCD934X_FLYBACK_VNEG_CTRL_5, 0x83 }, + { WCD934X_FLYBACK_VNEG_CTRL_6, 0x98 }, + { WCD934X_FLYBACK_VNEG_CTRL_7, 0xa9 }, + { WCD934X_FLYBACK_VNEG_CTRL_8, 0x68 }, + { WCD934X_FLYBACK_VNEG_CTRL_9, 0x64 }, + { WCD934X_FLYBACK_VNEGDAC_CTRL_1, 0xed }, + { WCD934X_FLYBACK_VNEGDAC_CTRL_2, 0xf0 }, + { WCD934X_FLYBACK_VNEGDAC_CTRL_3, 0xa6 }, + { WCD934X_FLYBACK_CTRL_1, 0x65 }, + { WCD934X_FLYBACK_TEST_CTL, 0x00 }, + { WCD934X_RX_AUX_SW_CTL, 0x00 }, + { WCD934X_RX_PA_AUX_IN_CONN, 0x00 }, + { WCD934X_RX_TIMER_DIV, 0x32 }, + { WCD934X_RX_OCP_CTL, 0x1f }, + { WCD934X_RX_OCP_COUNT, 0x77 }, + { WCD934X_RX_BIAS_EAR_DAC, 0xa0 }, + { WCD934X_RX_BIAS_EAR_AMP, 0xaa }, + { WCD934X_RX_BIAS_HPH_LDO, 0xa9 }, + { WCD934X_RX_BIAS_HPH_PA, 0xaa }, + { WCD934X_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8a }, + { WCD934X_RX_BIAS_HPH_RDAC_LDO, 0x88 }, + { WCD934X_RX_BIAS_HPH_CNP1, 0x82 }, + { WCD934X_RX_BIAS_HPH_LOWPOWER, 0x82 }, + { WCD934X_RX_BIAS_DIFFLO_PA, 0x80 }, + { WCD934X_RX_BIAS_DIFFLO_REF, 0x88 }, + { WCD934X_RX_BIAS_DIFFLO_LDO, 0x88 }, + { WCD934X_RX_BIAS_SELO_DAC_PA, 0xa8 }, + { WCD934X_RX_BIAS_BUCK_RST, 0x08 }, + { WCD934X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 }, + { WCD934X_RX_BIAS_FLYB_ERRAMP, 0x40 }, + { WCD934X_RX_BIAS_FLYB_BUFF, 0xaa }, + { WCD934X_RX_BIAS_FLYB_MID_RST, 0x14 }, + { WCD934X_HPH_L_STATUS, 0x04 }, + { WCD934X_HPH_R_STATUS, 0x04 }, + { WCD934X_HPH_CNP_EN, 0x80 }, + { WCD934X_HPH_CNP_WG_CTL, 0x9a }, + { WCD934X_HPH_CNP_WG_TIME, 0x14 }, + { WCD934X_HPH_OCP_CTL, 0x28 }, + { WCD934X_HPH_AUTO_CHOP, 0x16 }, + { WCD934X_HPH_CHOP_CTL, 0x83 }, + { WCD934X_HPH_PA_CTL1, 0x46 }, + { WCD934X_HPH_PA_CTL2, 0x50 }, + { WCD934X_HPH_L_EN, 0x80 }, + { WCD934X_HPH_L_TEST, 0xe0 }, + { WCD934X_HPH_L_ATEST, 0x50 }, + { WCD934X_HPH_R_EN, 0x80 }, + { WCD934X_HPH_R_TEST, 0xe0 }, + { WCD934X_HPH_R_ATEST, 0x54 }, + { WCD934X_HPH_RDAC_CLK_CTL1, 0x99 }, + { WCD934X_HPH_RDAC_CLK_CTL2, 0x9b }, + { WCD934X_HPH_RDAC_LDO_CTL, 0x33 }, + { WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD934X_HPH_REFBUFF_UHQA_CTL, 0xa8 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0a }, + { WCD934X_HPH_L_DAC_CTL, 0x00 }, + { WCD934X_HPH_R_DAC_CTL, 0x00 }, + { WCD934X_EAR_EN_REG, 0x60 }, + { WCD934X_EAR_CMBUFF, 0x05 }, + { WCD934X_EAR_ICTL, 0x40 }, + { WCD934X_EAR_EN_DBG_CTL, 0x00 }, + { WCD934X_EAR_CNP, 0xe0 }, + { WCD934X_EAR_DAC_CTL_ATEST, 0x00 }, + { WCD934X_EAR_STATUS_REG, 0x04 }, + { WCD934X_EAR_EAR_MISC, 0x28 }, + { WCD934X_DIFF_LO_MISC, 0x03 }, + { WCD934X_DIFF_LO_LO2_COMPANDER, 0x00 }, + { WCD934X_DIFF_LO_LO1_COMPANDER, 0x00 }, + { WCD934X_DIFF_LO_COMMON, 0x40 }, + { WCD934X_DIFF_LO_BYPASS_EN, 0x00 }, + { WCD934X_DIFF_LO_CNP, 0x20 }, + { WCD934X_DIFF_LO_CORE_OUT_PROG, 0xa0 }, + { WCD934X_DIFF_LO_LDO_OUT_PROG, 0x00 }, + { WCD934X_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x8b }, + { WCD934X_DIFF_LO_COM_PA_FREQ, 0xb0 }, + { WCD934X_DIFF_LO_RESERVED_REG, 0x60 }, + { WCD934X_DIFF_LO_LO1_STATUS_1, 0x00 }, + { WCD934X_DIFF_LO_LO1_STATUS_2, 0x00 }, + { WCD934X_ANA_NEW_PAGE_REGISTER, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD934X_SLNQ_ANA_EN, 0x02 }, + { WCD934X_SLNQ_ANA_STATUS, 0x00 }, + { WCD934X_SLNQ_ANA_LDO_CONFIG, 0xea }, + { WCD934X_SLNQ_ANA_LDO_OCP_CONFIG, 0x95 }, + { WCD934X_SLNQ_ANA_TX_LDO_CONFIG, 0xb6 }, + { WCD934X_SLNQ_ANA_TX_DRV_CONFIG, 0x26 }, + { WCD934X_SLNQ_ANA_RX_CONFIG_1, 0x64 }, + { WCD934X_SLNQ_ANA_RX_CONFIG_2, 0x40 }, + { WCD934X_SLNQ_ANA_PLL_ENABLES, 0x00 }, + { WCD934X_SLNQ_ANA_PLL_PRESET, 0x08 }, + { WCD934X_SLNQ_ANA_PLL_STATUS, 0x00 }, + { WCD934X_CLK_SYS_PLL_ENABLES, 0x00 }, + { WCD934X_CLK_SYS_PLL_PRESET, 0x00 }, + { WCD934X_CLK_SYS_PLL_STATUS, 0x00 }, + { WCD934X_CLK_SYS_MCLK_PRG, 0x00 }, + { WCD934X_CLK_SYS_MCLK2_PRG1, 0x00 }, + { WCD934X_CLK_SYS_MCLK2_PRG2, 0x00 }, + { WCD934X_CLK_SYS_XO_PRG, 0x00 }, + { WCD934X_CLK_SYS_XO_CAP_XTP, 0x00 }, + { WCD934X_CLK_SYS_XO_CAP_XTM, 0x00 }, + { WCD934X_BOOST_BST_EN_DLY, 0x40 }, + { WCD934X_BOOST_CTRL_ILIM, 0x9c }, + { WCD934X_BOOST_VOUT_SETTING, 0xca }, + { WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x05 }, + { WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x0d }, + { WCD934X_SIDO_NEW_VOUT_D_FREQ1, 0x07 }, + { WCD934X_SIDO_NEW_VOUT_D_FREQ2, 0x00 }, + { WCD934X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00 }, + { WCD934X_MBHC_NEW_CTL_1, 0x02 }, + { WCD934X_MBHC_NEW_CTL_2, 0x05 }, + { WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0xe9 }, + { WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x0f }, + { WCD934X_MBHC_NEW_ZDET_RAMP_CTL, 0x00 }, + { WCD934X_MBHC_NEW_FSM_STATUS, 0x00 }, + { WCD934X_MBHC_NEW_ADC_RESULT, 0x00 }, + { WCD934X_TX_NEW_AMIC_4_5_SEL, 0x00 }, + { WCD934X_VBADC_NEW_ADC_MODE, 0x10 }, + { WCD934X_VBADC_NEW_ADC_DOUTMSB, 0x00 }, + { WCD934X_VBADC_NEW_ADC_DOUTLSB, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0xa0 }, + { WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 }, + { WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xfe }, + { WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x02 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e }, + { WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, + { WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x62 }, + { WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01 }, + { WCD934X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11 }, + { WCD934X_SLNQ_INT_ANA_INT_LDO_TEST, 0x0d }, + { WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_1, 0x85 }, + { WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_2, 0xb4 }, + { WCD934X_SLNQ_INT_ANA_INT_TX_LDO_TEST, 0x16 }, + { WCD934X_SLNQ_INT_ANA_INT_TX_DRV_TEST, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RX_TEST, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RX_TEST_STATUS, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_1, 0x50 }, + { WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_2, 0x04 }, + { WCD934X_SLNQ_INT_ANA_INT_CLK_CTRL, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RESERVED_1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RESERVED_2, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG0, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG0, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG0, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_L_VAL, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_M_VAL, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_N_VAL, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG0, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_PFD_CP_DSM_PROG, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_VCO_PROG, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_LDO_LOCK_CFG, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_DIG_LOCK_DET_CFG, 0x00 }, + { WCD934X_CLK_SYS_INT_POST_DIV_REG0, 0x00 }, + { WCD934X_CLK_SYS_INT_POST_DIV_REG1, 0x00 }, + { WCD934X_CLK_SYS_INT_REF_DIV_REG0, 0x00 }, + { WCD934X_CLK_SYS_INT_REF_DIV_REG1, 0x00 }, + { WCD934X_CLK_SYS_INT_FILTER_REG0, 0x00 }, + { WCD934X_CLK_SYS_INT_FILTER_REG1, 0x00 }, + { WCD934X_CLK_SYS_INT_PLL_L_VAL, 0x00 }, + { WCD934X_CLK_SYS_INT_PLL_M_VAL, 0x00 }, + { WCD934X_CLK_SYS_INT_PLL_N_VAL, 0x00 }, + { WCD934X_CLK_SYS_INT_TEST_REG0, 0x00 }, + { WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG, 0x00 }, + { WCD934X_CLK_SYS_INT_VCO_PROG, 0x00 }, + { WCD934X_CLK_SYS_INT_TEST_REG1, 0x00 }, + { WCD934X_CLK_SYS_INT_LDO_LOCK_CFG, 0x00 }, + { WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG, 0x00 }, + { WCD934X_CLK_SYS_INT_CLK_TEST1, 0x00 }, + { WCD934X_CLK_SYS_INT_CLK_TEST2, 0x00 }, + { WCD934X_CLK_SYS_INT_CLK_TEST3, 0x00 }, + { WCD934X_CLK_SYS_INT_XO_TEST1, 0x98 }, + { WCD934X_CLK_SYS_INT_XO_TEST2, 0x00 }, + { WCD934X_BOOST_INT_VCOMP_HYST, 0x02 }, + { WCD934X_BOOST_INT_VLOOP_FILTER, 0xef }, + { WCD934X_BOOST_INT_CTRL_IDELTA, 0xa8 }, + { WCD934X_BOOST_INT_CTRL_ILIM_STARTUP, 0x17 }, + { WCD934X_BOOST_INT_CTRL_MIN_ONTIME, 0x5f }, + { WCD934X_BOOST_INT_CTRL_MAX_ONTIME, 0x88 }, + { WCD934X_BOOST_INT_CTRL_TIMING, 0x0a }, + { WCD934X_BOOST_INT_TMUX_A_D, 0x00 }, + { WCD934X_BOOST_INT_SW_DRV_CNTL, 0xf8 }, + { WCD934X_BOOST_INT_SPARE1, 0x00 }, + { WCD934X_BOOST_INT_SPARE2, 0x00 }, + { WCD934X_SIDO_NEW_INT_RAMP_STATUS, 0x00 }, + { WCD934X_SIDO_NEW_INT_SPARE_1, 0x00 }, + { WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A, 0x64 }, + { WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D, 0x40 }, + { WCD934X_SIDO_NEW_INT_RAMP_INC_WAIT, 0x24 }, + { WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL, 0x09 }, + { WCD934X_SIDO_NEW_INT_RAMP_IBLEED_CTL, 0x7d }, + { WCD934X_SIDO_NEW_INT_DEBUG_CPROVR_TEST, 0x00 }, + { WCD934X_SIDO_NEW_INT_RAMP_CTL_A, 0x14 }, + { WCD934X_SIDO_NEW_INT_RAMP_CTL_D, 0x14 }, + { WCD934X_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD, 0x33 }, + { WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1, 0x3f }, + { WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2, 0x74 }, + { WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3, 0x33 }, + { WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1, 0x1d }, + { WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2, 0x0a }, + { WCD934X_MBHC_NEW_INT_SLNQ_HPF, 0x50 }, + { WCD934X_MBHC_NEW_INT_SLNQ_REF, 0x24 }, + { WCD934X_MBHC_NEW_INT_SLNQ_COMP, 0x50 }, + { WCD934X_MBHC_NEW_INT_SPARE_2, 0x00 }, + { WCD934X_PAGE10_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_ANC0_CLK_RESET_CTL, 0x00 }, + { WCD934X_CDC_ANC0_MODE_1_CTL, 0x00 }, + { WCD934X_CDC_ANC0_MODE_2_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FF_SHIFT, 0x00 }, + { WCD934X_CDC_ANC0_FB_SHIFT, 0x00 }, + { WCD934X_CDC_ANC0_LPF_FF_A_CTL, 0x00 }, + { WCD934X_CDC_ANC0_LPF_FF_B_CTL, 0x00 }, + { WCD934X_CDC_ANC0_LPF_FB_CTL, 0x00 }, + { WCD934X_CDC_ANC0_SMLPF_CTL, 0x00 }, + { WCD934X_CDC_ANC0_DCFLT_SHIFT_CTL, 0x00 }, + { WCD934X_CDC_ANC0_IIR_ADAPT_CTL, 0x00 }, + { WCD934X_CDC_ANC0_IIR_COEFF_1_CTL, 0x00 }, + { WCD934X_CDC_ANC0_IIR_COEFF_2_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FF_A_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FF_B_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FB_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC0_RC_COMMON_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FIFO_COMMON_CTL, 0x88 }, + { WCD934X_CDC_ANC0_RC0_STATUS_FMIN_CNTR, 0x00 }, + { WCD934X_CDC_ANC0_RC1_STATUS_FMIN_CNTR, 0x00 }, + { WCD934X_CDC_ANC0_RC0_STATUS_FMAX_CNTR, 0x00 }, + { WCD934X_CDC_ANC0_RC1_STATUS_FMAX_CNTR, 0x00 }, + { WCD934X_CDC_ANC0_STATUS_FIFO, 0x00 }, + { WCD934X_CDC_ANC1_CLK_RESET_CTL, 0x00 }, + { WCD934X_CDC_ANC1_MODE_1_CTL, 0x00 }, + { WCD934X_CDC_ANC1_MODE_2_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FF_SHIFT, 0x00 }, + { WCD934X_CDC_ANC1_FB_SHIFT, 0x00 }, + { WCD934X_CDC_ANC1_LPF_FF_A_CTL, 0x00 }, + { WCD934X_CDC_ANC1_LPF_FF_B_CTL, 0x00 }, + { WCD934X_CDC_ANC1_LPF_FB_CTL, 0x00 }, + { WCD934X_CDC_ANC1_SMLPF_CTL, 0x00 }, + { WCD934X_CDC_ANC1_DCFLT_SHIFT_CTL, 0x00 }, + { WCD934X_CDC_ANC1_IIR_ADAPT_CTL, 0x00 }, + { WCD934X_CDC_ANC1_IIR_COEFF_1_CTL, 0x00 }, + { WCD934X_CDC_ANC1_IIR_COEFF_2_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FF_A_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FF_B_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FB_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC1_RC_COMMON_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FIFO_COMMON_CTL, 0x88 }, + { WCD934X_CDC_ANC1_RC0_STATUS_FMIN_CNTR, 0x00 }, + { WCD934X_CDC_ANC1_RC1_STATUS_FMIN_CNTR, 0x00 }, + { WCD934X_CDC_ANC1_RC0_STATUS_FMAX_CNTR, 0x00 }, + { WCD934X_CDC_ANC1_RC1_STATUS_FMAX_CNTR, 0x00 }, + { WCD934X_CDC_ANC1_STATUS_FIFO, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX0_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX0_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX0_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX0_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX0_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX0_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC7, 0x25 }, + { WCD934X_CDC_TX1_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX1_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX1_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX1_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX1_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX1_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX1_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX2_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX2_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX2_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX2_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX2_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX2_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX3_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX3_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX3_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX3_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX3_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX3_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX4_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX4_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX4_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX4_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX4_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX4_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX5_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX5_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX5_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX5_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX5_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX5_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX6_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX6_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX6_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX6_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX6_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX6_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX7_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX7_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX7_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX7_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX7_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX7_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX8_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX8_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX8_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX8_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX8_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX8_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD934X_PAGE11_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_COMPANDER1_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER1_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER1_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER1_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER1_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER1_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER1_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER1_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER2_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER2_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER2_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER2_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER2_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER2_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER2_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER2_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER3_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER3_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER3_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER3_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER3_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER3_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER3_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER3_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER4_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER4_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER4_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER4_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER4_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER4_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER4_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER4_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER7_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER7_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER7_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER7_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER7_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER7_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER7_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER7_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER8_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER8_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER8_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER8_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER8_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER8_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER8_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER8_CTL7, 0x08 }, + { WCD934X_CDC_RX0_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX0_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX0_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX0_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX0_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX0_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX0_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX0_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX1_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX1_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX1_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX1_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX1_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX1_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC4, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX1_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX2_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX2_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX2_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX2_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX2_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX2_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC4, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX2_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX3_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX3_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX3_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX3_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX3_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX3_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX3_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX4_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX4_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX4_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX4_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX4_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX4_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX4_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX7_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX7_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX7_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX7_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC0, 0x04 }, + { WCD934X_CDC_RX7_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX7_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX7_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX8_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX8_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX8_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX8_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC0, 0x04 }, + { WCD934X_CDC_RX8_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX8_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX8_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_PAGE12_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_CLSH_CRC, 0x00 }, + { WCD934X_CDC_CLSH_DLY_CTRL, 0x03 }, + { WCD934X_CDC_CLSH_DECAY_CTRL, 0x02 }, + { WCD934X_CDC_CLSH_HPH_V_PA, 0x1c }, + { WCD934X_CDC_CLSH_EAR_V_PA, 0x39 }, + { WCD934X_CDC_CLSH_HPH_V_HD, 0x0c }, + { WCD934X_CDC_CLSH_EAR_V_HD, 0x0c }, + { WCD934X_CDC_CLSH_K1_MSB, 0x01 }, + { WCD934X_CDC_CLSH_K1_LSB, 0x00 }, + { WCD934X_CDC_CLSH_K2_MSB, 0x00 }, + { WCD934X_CDC_CLSH_K2_LSB, 0x80 }, + { WCD934X_CDC_CLSH_IDLE_CTRL, 0x00 }, + { WCD934X_CDC_CLSH_IDLE_HPH, 0x00 }, + { WCD934X_CDC_CLSH_IDLE_EAR, 0x00 }, + { WCD934X_CDC_CLSH_TEST0, 0x07 }, + { WCD934X_CDC_CLSH_TEST1, 0x00 }, + { WCD934X_CDC_CLSH_OVR_VREF, 0x00 }, + { WCD934X_CDC_BOOST0_BOOST_PATH_CTL, 0x00 }, + { WCD934X_CDC_BOOST0_BOOST_CTL, 0xb2 }, + { WCD934X_CDC_BOOST0_BOOST_CFG1, 0x00 }, + { WCD934X_CDC_BOOST0_BOOST_CFG2, 0x00 }, + { WCD934X_CDC_BOOST1_BOOST_PATH_CTL, 0x00 }, + { WCD934X_CDC_BOOST1_BOOST_CTL, 0xb2 }, + { WCD934X_CDC_BOOST1_BOOST_CFG1, 0x00 }, + { WCD934X_CDC_BOOST1_BOOST_CFG2, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_PATH_CTL, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_CFG, 0x1a }, + { WCD934X_CDC_VBAT_VBAT_ADC_CAL1, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_ADC_CAL2, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_ADC_CAL3, 0x04 }, + { WCD934X_CDC_VBAT_VBAT_PK_EST1, 0xe0 }, + { WCD934X_CDC_VBAT_VBAT_PK_EST2, 0x01 }, + { WCD934X_CDC_VBAT_VBAT_PK_EST3, 0x40 }, + { WCD934X_CDC_VBAT_VBAT_RF_PROC1, 0x2a }, + { WCD934X_CDC_VBAT_VBAT_RF_PROC2, 0x86 }, + { WCD934X_CDC_VBAT_VBAT_TAC1, 0x70 }, + { WCD934X_CDC_VBAT_VBAT_TAC2, 0x18 }, + { WCD934X_CDC_VBAT_VBAT_TAC3, 0x18 }, + { WCD934X_CDC_VBAT_VBAT_TAC4, 0x03 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD1, 0x01 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD2, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD3, 0x64 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD4, 0x01 }, + { WCD934X_CDC_VBAT_VBAT_DEBUG1, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD_MON, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_MON_VAL, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_BAN, 0x0c }, + { WCD934X_MIXING_ASRC0_CLK_RST_CTL, 0x00 }, + { WCD934X_MIXING_ASRC0_CTL0, 0x00 }, + { WCD934X_MIXING_ASRC0_CTL1, 0x00 }, + { WCD934X_MIXING_ASRC0_FIFO_CTL, 0xa8 }, + { WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC0_STATUS_FIFO, 0x00 }, + { WCD934X_MIXING_ASRC1_CLK_RST_CTL, 0x00 }, + { WCD934X_MIXING_ASRC1_CTL0, 0x00 }, + { WCD934X_MIXING_ASRC1_CTL1, 0x00 }, + { WCD934X_MIXING_ASRC1_FIFO_CTL, 0xa8 }, + { WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC1_STATUS_FIFO, 0x00 }, + { WCD934X_MIXING_ASRC2_CLK_RST_CTL, 0x00 }, + { WCD934X_MIXING_ASRC2_CTL0, 0x00 }, + { WCD934X_MIXING_ASRC2_CTL1, 0x00 }, + { WCD934X_MIXING_ASRC2_FIFO_CTL, 0xa8 }, + { WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC2_STATUS_FIFO, 0x00 }, + { WCD934X_MIXING_ASRC3_CLK_RST_CTL, 0x00 }, + { WCD934X_MIXING_ASRC3_CTL0, 0x00 }, + { WCD934X_MIXING_ASRC3_CTL1, 0x00 }, + { WCD934X_MIXING_ASRC3_FIFO_CTL, 0xa8 }, + { WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC3_STATUS_FIFO, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_DATA_0, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_DATA_1, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_DATA_2, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_DATA_3, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_ADDR_1, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_ADDR_2, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_ADDR_3, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_ADDR_1, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_ADDR_2, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_ADDR_3, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_DATA_0, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_DATA_1, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_DATA_2, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_DATA_3, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_ACCESS_CFG, 0x0f }, + { WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS, 0x03 }, + { WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, 0x04 }, + { WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1, 0x00 }, + { WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, 0x04 }, + { WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1, 0x00 }, + { WCD934X_SIDETONE_ASRC0_CLK_RST_CTL, 0x00 }, + { WCD934X_SIDETONE_ASRC0_CTL0, 0x00 }, + { WCD934X_SIDETONE_ASRC0_CTL1, 0x00 }, + { WCD934X_SIDETONE_ASRC0_FIFO_CTL, 0xa8 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FIFO, 0x00 }, + { WCD934X_SIDETONE_ASRC1_CLK_RST_CTL, 0x00 }, + { WCD934X_SIDETONE_ASRC1_CTL0, 0x00 }, + { WCD934X_SIDETONE_ASRC1_CTL1, 0x00 }, + { WCD934X_SIDETONE_ASRC1_FIFO_CTL, 0xa8 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FIFO, 0x00 }, + { WCD934X_EC_REF_HQ0_EC_REF_HQ_PATH_CTL, 0x00 }, + { WCD934X_EC_REF_HQ0_EC_REF_HQ_CFG0, 0x01 }, + { WCD934X_EC_REF_HQ1_EC_REF_HQ_PATH_CTL, 0x00 }, + { WCD934X_EC_REF_HQ1_EC_REF_HQ_CFG0, 0x01 }, + { WCD934X_EC_ASRC0_CLK_RST_CTL, 0x00 }, + { WCD934X_EC_ASRC0_CTL0, 0x00 }, + { WCD934X_EC_ASRC0_CTL1, 0x00 }, + { WCD934X_EC_ASRC0_FIFO_CTL, 0xa8 }, + { WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_EC_ASRC0_STATUS_FIFO, 0x00 }, + { WCD934X_EC_ASRC1_CLK_RST_CTL, 0x00 }, + { WCD934X_EC_ASRC1_CTL0, 0x00 }, + { WCD934X_EC_ASRC1_CTL1, 0x00 }, + { WCD934X_EC_ASRC1_FIFO_CTL, 0xa8 }, + { WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_EC_ASRC1_STATUS_FIFO, 0x00 }, + { WCD934X_PAGE13_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_EC_REF_HQ_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0x00 }, + { WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 0x00 }, + { WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 0x00 }, + { WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 0x00 }, + { WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 0x00 }, + { WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, 0x00 }, + { WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x0c }, + { WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, 0x00 }, + { WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, 0x00 }, + { WCD934X_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL, 0x0f }, + { WCD934X_CDC_CLK_RST_CTRL_GFM_CONTROL, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_CTL, 0x08 }, + { WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD0, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD1, 0x4b }, + { WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_STATUS, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_CTRL, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, 0x40 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, 0x40 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL, 0x00 }, + { WCD934X_CDC_TOP_TOP_CFG0, 0x00 }, + { WCD934X_CDC_TOP_TOP_CFG1, 0x00 }, + { WCD934X_CDC_TOP_TOP_CFG7, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_WR_LSB, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_WR_MSB, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_LUT, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_RD_LSB, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_RD_MSB, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_WR_LSB, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_WR_MSB, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_LUT, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_RD_LSB, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_RD_MSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_WR_LSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_WR_MSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_LUT, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_RD_LSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_RD_MSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_WR_LSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_WR_MSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_LUT, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_RD_LSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_RD_MSB, 0x00 }, + { WCD934X_CDC_DSD0_PATH_CTL, 0x00 }, + { WCD934X_CDC_DSD0_CFG0, 0x00 }, + { WCD934X_CDC_DSD0_CFG1, 0x00 }, + { WCD934X_CDC_DSD0_CFG2, 0x42 }, + { WCD934X_CDC_DSD0_CFG3, 0x00 }, + { WCD934X_CDC_DSD0_CFG4, 0x02 }, + { WCD934X_CDC_DSD0_CFG5, 0x00 }, + { WCD934X_CDC_DSD1_PATH_CTL, 0x00 }, + { WCD934X_CDC_DSD1_CFG0, 0x00 }, + { WCD934X_CDC_DSD1_CFG1, 0x00 }, + { WCD934X_CDC_DSD1_CFG2, 0x42 }, + { WCD934X_CDC_DSD1_CFG3, 0x00 }, + { WCD934X_CDC_DSD1_CFG4, 0x02 }, + { WCD934X_CDC_DSD1_CFG5, 0x00 }, + { WCD934X_CDC_RX_IDLE_DET_PATH_CTL, 0x00 }, + { WCD934X_CDC_RX_IDLE_DET_CFG0, 0x07 }, + { WCD934X_CDC_RX_IDLE_DET_CFG1, 0x3c }, + { WCD934X_CDC_RX_IDLE_DET_CFG2, 0x00 }, + { WCD934X_CDC_RX_IDLE_DET_CFG3, 0x00 }, + { WCD934X_PAGE14_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_CLK_RST_CTL, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_CTL, 0x09 }, + { WCD934X_CDC_RATE_EST0_RE_PULSE_SUPR_CTL, 0x06 }, + { WCD934X_CDC_RATE_EST0_RE_TIMER, 0x01 }, + { WCD934X_CDC_RATE_EST0_RE_BW_SW, 0x20 }, + { WCD934X_CDC_RATE_EST0_RE_THRESH, 0xa0 }, + { WCD934X_CDC_RATE_EST0_RE_STATUS, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_CTRL, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_TIMER2, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW1, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW2, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW3, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW4, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW5, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW1, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW2, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW3, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW4, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW5, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_RMAX_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RMIN_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_PH_DET, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_CLR, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_MB_SW_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_MAST_DIAG_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_7_0, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_15_8, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_23_16, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_31_24, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_39_32, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_40_43, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_CLK_RST_CTL, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_CTL, 0x09 }, + { WCD934X_CDC_RATE_EST1_RE_PULSE_SUPR_CTL, 0x06 }, + { WCD934X_CDC_RATE_EST1_RE_TIMER, 0x01 }, + { WCD934X_CDC_RATE_EST1_RE_BW_SW, 0x20 }, + { WCD934X_CDC_RATE_EST1_RE_THRESH, 0xa0 }, + { WCD934X_CDC_RATE_EST1_RE_STATUS, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_CTRL, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_TIMER2, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW1, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW2, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW3, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW4, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW5, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW1, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW2, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW3, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW4, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW5, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_RMAX_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RMIN_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_PH_DET, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_CLR, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_MB_SW_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_MAST_DIAG_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_7_0, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_15_8, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_23_16, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_31_24, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_39_32, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_40_43, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_CLK_RST_CTL, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_CTL, 0x09 }, + { WCD934X_CDC_RATE_EST2_RE_PULSE_SUPR_CTL, 0x06 }, + { WCD934X_CDC_RATE_EST2_RE_TIMER, 0x01 }, + { WCD934X_CDC_RATE_EST2_RE_BW_SW, 0x20 }, + { WCD934X_CDC_RATE_EST2_RE_THRESH, 0xa0 }, + { WCD934X_CDC_RATE_EST2_RE_STATUS, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_CTRL, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_TIMER2, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW1, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW2, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW3, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW4, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW5, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW1, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW2, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW3, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW4, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW5, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_RMAX_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RMIN_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_PH_DET, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_CLR, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_MB_SW_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_MAST_DIAG_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_7_0, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_15_8, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_23_16, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_31_24, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_39_32, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_40_43, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_CLK_RST_CTL, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_CTL, 0x09 }, + { WCD934X_CDC_RATE_EST3_RE_PULSE_SUPR_CTL, 0x06 }, + { WCD934X_CDC_RATE_EST3_RE_TIMER, 0x01 }, + { WCD934X_CDC_RATE_EST3_RE_BW_SW, 0x20 }, + { WCD934X_CDC_RATE_EST3_RE_THRESH, 0xa0 }, + { WCD934X_CDC_RATE_EST3_RE_STATUS, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_CTRL, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_TIMER2, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW1, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW2, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW3, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW4, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW5, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW1, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW2, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW3, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW4, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW5, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_RMAX_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RMIN_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_PH_DET, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_CLR, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_MB_SW_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_MAST_DIAG_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_7_0, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_15_8, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_23_16, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_31_24, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_39_32, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_40_43, 0x00 }, + { WCD934X_PAGE15_PAGE_REGISTER, 0x00 }, + { WCD934X_SPLINE_SRC0_CLK_RST_CTL_0, 0x20 }, + { WCD934X_SPLINE_SRC0_STATUS, 0x00 }, + { WCD934X_SPLINE_SRC1_CLK_RST_CTL_0, 0x20 }, + { WCD934X_SPLINE_SRC1_STATUS, 0x00 }, + { WCD934X_SPLINE_SRC2_CLK_RST_CTL_0, 0x20 }, + { WCD934X_SPLINE_SRC2_STATUS, 0x00 }, + { WCD934X_SPLINE_SRC3_CLK_RST_CTL_0, 0x20 }, + { WCD934X_SPLINE_SRC3_STATUS, 0x00 }, + { WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x11 }, + { WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x20 }, + { WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG2, 0x00 }, + { WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x08 }, + { WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x11 }, + { WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x20 }, + { WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG2, 0x00 }, + { WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x08 }, + { WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG0, 0x00 }, + { WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG1, 0x00 }, + { WCD934X_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0, 0x00 }, + { WCD934X_CDC_DEBUG_ANC0_RC0_FIFO_CTL, 0x4c }, + { WCD934X_CDC_DEBUG_ANC0_RC1_FIFO_CTL, 0x4c }, + { WCD934X_CDC_DEBUG_ANC1_RC0_FIFO_CTL, 0x4c }, + { WCD934X_CDC_DEBUG_ANC1_RC1_FIFO_CTL, 0x4c }, + { WCD934X_CDC_DEBUG_ANC_RC_RST_DBG_CNTR, 0x00 }, + { WCD934X_PAGE80_PAGE_REGISTER, 0x00 }, + { WCD934X_CODEC_CPR_WR_DATA_0, 0x00 }, + { WCD934X_CODEC_CPR_WR_DATA_1, 0x00 }, + { WCD934X_CODEC_CPR_WR_DATA_2, 0x00 }, + { WCD934X_CODEC_CPR_WR_DATA_3, 0x00 }, + { WCD934X_CODEC_CPR_WR_ADDR_0, 0x00 }, + { WCD934X_CODEC_CPR_WR_ADDR_1, 0x00 }, + { WCD934X_CODEC_CPR_WR_ADDR_2, 0x00 }, + { WCD934X_CODEC_CPR_WR_ADDR_3, 0x00 }, + { WCD934X_CODEC_CPR_RD_ADDR_0, 0x00 }, + { WCD934X_CODEC_CPR_RD_ADDR_1, 0x00 }, + { WCD934X_CODEC_CPR_RD_ADDR_2, 0x00 }, + { WCD934X_CODEC_CPR_RD_ADDR_3, 0x00 }, + { WCD934X_CODEC_CPR_RD_DATA_0, 0x00 }, + { WCD934X_CODEC_CPR_RD_DATA_1, 0x00 }, + { WCD934X_CODEC_CPR_RD_DATA_2, 0x00 }, + { WCD934X_CODEC_CPR_RD_DATA_3, 0x00 }, + { WCD934X_CODEC_CPR_ACCESS_CFG, 0x0f }, + { WCD934X_CODEC_CPR_ACCESS_STATUS, 0x03 }, + { WCD934X_CODEC_CPR_NOM_CX_VDD, 0xb4 }, + { WCD934X_CODEC_CPR_SVS_CX_VDD, 0x5c }, + { WCD934X_CODEC_CPR_SVS2_CX_VDD, 0x40 }, + { WCD934X_CODEC_CPR_NOM_MX_VDD, 0xb4 }, + { WCD934X_CODEC_CPR_SVS_MX_VDD, 0xb4 }, + { WCD934X_CODEC_CPR_SVS2_MX_VDD, 0xa0 }, + { WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD, 0x28 }, + { WCD934X_CODEC_CPR_MAX_SVS2_STEP, 0x08 }, + { WCD934X_CODEC_CPR_CTL, 0x00 }, + { WCD934X_CODEC_CPR_SW_MODECHNG_STATUS, 0x00 }, + { WCD934X_CODEC_CPR_SW_MODECHNG_START, 0x00 }, + { WCD934X_CODEC_CPR_CPR_STATUS, 0x00 }, + { WCD934X_PAGE128_PAGE_REGISTER, 0x00 }, + { WCD934X_TLMM_BIST_MODE_PINCFG, 0x00 }, + { WCD934X_TLMM_RF_PA_ON_PINCFG, 0x00 }, + { WCD934X_TLMM_INTR1_PINCFG, 0x00 }, + { WCD934X_TLMM_INTR2_PINCFG, 0x00 }, + { WCD934X_TLMM_SWR_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_SWR_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_2_SCK_PINCFG, 0x00 }, + { WCD934X_TLMM_SLIMBUS_DATA1_PINCFG, 0x00 }, + { WCD934X_TLMM_SLIMBUS_DATA2_PINCFG, 0x00 }, + { WCD934X_TLMM_SLIMBUS_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2C_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2C_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_0_RX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_0_TX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_0_SCK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_0_WS_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_1_RX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_1_TX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_1_SCK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_1_WS_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC1_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC1_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC2_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC2_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC3_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC3_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_JTCK_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO1_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO2_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO3_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO4_PINCFG, 0x00 }, + { WCD934X_TLMM_SPI_S_CSN_PINCFG, 0x00 }, + { WCD934X_TLMM_SPI_S_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_SPI_S_DOUT_PINCFG, 0x00 }, + { WCD934X_TLMM_SPI_S_DIN_PINCFG, 0x00 }, + { WCD934X_TLMM_BA_N_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO0_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_2_RX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_2_WS_PINCFG, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_0, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_1, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_2, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_3, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_4, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_0, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_1, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_2, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_3, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_4, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_DRVCTL_0, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_DRVCTL_1, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_STATUS, 0x00 }, + { WCD934X_TEST_DEBUG_NPL_DLY_TEST_1, 0x10 }, + { WCD934X_TEST_DEBUG_NPL_DLY_TEST_2, 0x60 }, + { WCD934X_TEST_DEBUG_MEM_CTRL, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_BUS_SEL, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_JTAG, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_1, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_2, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_3, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_4, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_5, 0x00 }, + { WCD934X_TEST_DEBUG_ANA_DTEST_DIR, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_0, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_1, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_2, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_3, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_4, 0x00 }, + { WCD934X_TEST_DEBUG_SYSMEM_CTRL, 0x00 }, + { WCD934X_TEST_DEBUG_SOC_SW_PWR_SEQ_DELAY, 0x00 }, + { WCD934X_TEST_DEBUG_LVAL_NOM_LOW, 0x96 }, + { WCD934X_TEST_DEBUG_LVAL_NOM_HIGH, 0x00 }, + { WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW, 0x53 }, + { WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH, 0x00 }, + { WCD934X_TEST_DEBUG_SPI_SLAVE_CHAR, 0x00 }, + { WCD934X_TEST_DEBUG_CODEC_DIAGS, 0x00 }, +}; + +/* + * wcd934x_regmap_register_patch: Update register defaults based on version + * @regmap: handle to wcd9xxx regmap + * @version: wcd934x version + * + * Returns error code in case of failure or 0 for success + */ +int wcd934x_regmap_register_patch(struct regmap *regmap, int revision) +{ + int rc = 0; + + if (!regmap) { + pr_err("%s: regmap struct is NULL\n", __func__); + return -EINVAL; + } + + switch (revision) { + case TAVIL_VERSION_1_1: + case TAVIL_VERSION_WCD9340_1_1: + case TAVIL_VERSION_WCD9341_1_1: + regcache_cache_only(regmap, true); + rc = regmap_multi_reg_write(regmap, wcd934x_1_1_defaults, + ARRAY_SIZE(wcd934x_1_1_defaults)); + regcache_cache_only(regmap, false); + break; + } + + return rc; +} +EXPORT_SYMBOL(wcd934x_regmap_register_patch); + +static bool wcd934x_is_readable_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + /* + * Get the page number from MSB of codec register. If its 0x80, assign + * the corresponding page index PAGE_0x80. + */ + pg_num = reg >> 0x8; + if (pg_num == 0x80) + pg_num = WCD934X_PAGE_0X80; + else if (pg_num == 0x50) + pg_num = WCD934X_PAGE_0x50; + else if (pg_num > 0xF) + return false; + + reg_tbl = wcd934x_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl && reg_tbl[reg_offset]) + return true; + else + return false; +} + +static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + pg_num = reg >> 0x8; + if (pg_num == 0x80) + pg_num = WCD934X_PAGE_0X80; + else if (pg_num == 0x50) + pg_num = WCD934X_PAGE_0x50; + else if (pg_num > 0xF) + return false; + + reg_tbl = wcd934x_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl && reg_tbl[reg_offset] == WCD934X_READ) + return true; + + /* IIR Coeff registers are not cacheable */ + if ((reg >= WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL) && + (reg <= WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)) + return true; + + if ((reg >= WCD934X_CDC_ANC0_IIR_COEFF_1_CTL) && + (reg <= WCD934X_CDC_ANC0_FB_GAIN_CTL)) + return true; + + if ((reg >= WCD934X_CDC_ANC1_IIR_COEFF_1_CTL) && + (reg <= WCD934X_CDC_ANC1_FB_GAIN_CTL)) + return true; + + if ((reg >= WCD934X_CODEC_CPR_WR_DATA_0) && + (reg <= WCD934X_CODEC_CPR_RD_DATA_3)) + return true; + + /* + * Need to mark volatile for registers that are writable but + * only few bits are read-only + */ + switch (reg) { + case WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL: + case WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0: + case WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1: + case WCD934X_CPE_SS_CPAR_CTL: + case WCD934X_CPE_SS_STATUS: + case WCD934X_CODEC_RPM_RST_CTL: + case WCD934X_SIDO_NEW_VOUT_A_STARTUP: + case WCD934X_SIDO_NEW_VOUT_D_STARTUP: + case WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL: + case WCD934X_ANA_MBHC_MECH: + case WCD934X_ANA_MBHC_ELECT: + case WCD934X_ANA_MBHC_ZDET: + case WCD934X_ANA_MICB2: + return true; + } + + return false; +} + +struct regmap_config wcd934x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd934x_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd934x_defaults), + .max_register = WCD934X_MAX_REGISTER, + .volatile_reg = wcd934x_is_volatile_register, + .readable_reg = wcd934x_is_readable_register, + .can_multi_write = true, +}; diff --git a/drivers/mfd/wcd934x-tables.c b/drivers/mfd/wcd934x-tables.c new file mode 100644 index 0000000000000000000000000000000000000000..db963d08b66e4a2af62b197e7255f11f3af607b8 --- /dev/null +++ b/drivers/mfd/wcd934x-tables.c @@ -0,0 +1,2155 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#define WCD934X_REG(reg) ((reg) & 0xFF) + +const u8 wcd934x_page0_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE0_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_CLK_BYPASS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_CLK_GATE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_CLK_MCLK_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_CLK_MCLK2_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_I2S_DSD_CLK_SEL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_TEST0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_TEST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT4)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT5)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT7)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT10)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT11)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT12)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT13)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_SLNQ_WAIT_STATE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_ACTIVE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX0_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX1_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX2_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX3_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX4_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX5_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX6_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX7_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX0_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX1_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX2_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX3_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX4_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX5_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX6_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX7_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX8_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX9_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX10_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX11_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX13_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX14_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX15_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_TX0_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_TX1_0_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_TX1_1_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_0_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_3_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_CLKSRC_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_0_TDM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA4_PRT_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_SBTX0_7_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_SBTX8_11_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA0_PRT_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA3_PRT_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA4_PRT0_3_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA4_PRT4_7_CFG)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page1_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE1_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_L_VAL_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_L_VAL_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_DSM_FRAC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_DSM_FRAC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FLL_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_STATUS_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_FLL_STATUS_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_FLL_STATUS_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_FLL_STATUS_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_L_VAL_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_L_VAL_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_DSM_FRAC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_DSM_FRAC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FLL_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_STATUS_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_I2S_FLL_STATUS_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_I2S_FLL_STATUS_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_I2S_FLL_STATUS_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_L_VAL_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_L_VAL_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_DSM_FRAC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_DSM_FRAC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FLL_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_STATUS_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SB_FLL_STATUS_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SB_FLL_STATUS_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SB_FLL_STATUS_3)] = WCD934X_READ, +}; + +const u8 wcd934x_page2_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE2_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPEFLL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_US_BUF_INT_PERIOD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SVA_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_US_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_MAD_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPAR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_DMIC0_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_DMIC1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_DMIC2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_DMIC_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPAR_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_WDOG_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_BACKUP_INT)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_STATUS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPE_OCD_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1A)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1B)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_MAIN_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_MAIN_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_INP_SEL)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page4_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE4_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CLR_COMMIT)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_MASK0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_MASK1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_MASK2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_MASK3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_STATUS0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN1_STATUS1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN1_STATUS2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN1_STATUS3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN1_CLEAR0)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_CLEAR1)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_CLEAR2)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_CLEAR3)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN2_MASK3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN2_STATUS3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN2_CLEAR3)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_MASK2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_MASK3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_STATUS2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_STATUS3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_CLEAR2)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_CLEAR3)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_LEVEL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_LEVEL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_LEVEL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_LEVEL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_BYPASS0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_BYPASS1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_BYPASS2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_BYPASS3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_SET0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_SET1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_SET2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_SET3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CODEC_MISC_MASK)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CODEC_MISC_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_CODEC_MISC_CLEAR)] = WCD934X_WRITE, +}; + +const u8 wcd934x_page5_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE5_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEVICE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_REVISION)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_H_COMMAND)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_MASTER_ADDRESS_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_MASTER_ADDRESS_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SLAVE_ADDRESS_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SLAVE_ADDRESS_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_COMM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_FRAME_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH1_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH3_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SW_EVENT_RD)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_SW_EVENT_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SELECT_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SELECT_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SELECT_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SAMPLING_FREQ)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_SEL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_RAM_CNTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BANK)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_B)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_C)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_E)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_F)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_10)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_11)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_12)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_13)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_14)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_15)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_16)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_17)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_18)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_19)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1B)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1C)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1E)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1F)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_20)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_21)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_22)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_23)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_24)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_25)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_26)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_27)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_28)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_29)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2B)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2C)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2E)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2F)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_30)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_31)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_32)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_33)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_34)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_35)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_36)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_37)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_38)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_39)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3B)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3C)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3E)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3F)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TOP_CTRL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TOP_CTRL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_MUTE_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_STATUS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_FS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_IN_SEL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_GPOUT_ENABLE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_GPOUT_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_ANA_INTERRUPT_MASK)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_ANA_INTERRUPT_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_ANA_INTERRUPT_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_IP_TESTING)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNT_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNT_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS4)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR0)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR1)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR2)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR3)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR4)] = WCD934X_WRITE, +}; + +const u8 wcd934x_page6_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_ANA_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_BIAS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_RCO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_PAGE6_SPARE2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_PAGE6_SPARE3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_BUCK_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_BUCK_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_ANA_RX_SUPPLIES)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_HPH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_EAR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_LO_1_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MAD_SETUP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_AMIC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_AMIC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_AMIC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_AMIC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_MECH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_ELECT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_ZDET)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_RESULT_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_ANA_MBHC_RESULT_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_ANA_MBHC_RESULT_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB2_RAMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_VBADC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BIAS_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BIAS_VBG_FINE_ADJ)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CTRL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CTRL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CAL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CAL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_TEST_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_4)] = WCD934X_READ, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_5)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_MODE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_MODE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_MODE_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_MODE_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_VCL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_VCL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_VCL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_10)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_FILTER_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_FILTER_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_DRIVER_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_DRIVER_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_DRIVER_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CAL_CODE_EXT_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CAL_CODE_EXT_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CAL_CODE_OUT_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_CAL_CODE_OUT_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_TEST_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_TEST_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_CLK)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_ANA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_SPARE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_SPARE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_BCS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_STATUS_SPARE_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MBHC_TEST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_SUBBLOCK_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_IBIAS_FE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_BIAS_ADC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_FE_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_ADC_REF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_ADC_IO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_ADC_SAR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_DEBUG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_LDOH_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_LDOH_BIAS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_LDOH_STB_LOADS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_LDOH_SLOWRAMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB1_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB1_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB1_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB2_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB2_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB2_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB3_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB3_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB3_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB4_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB4_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB4_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_ADC_VCM)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_BIAS_ATEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_ADC_INT1_IB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_ADC_INT2_IB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_START)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_STOP_9P6M)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_STOP_12P288M)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_TEST_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_ADC_IB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_ATEST_REFCTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_TEST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_TEST_BLK_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_TXFE_CLKDIV)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_SAR1_ERR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TX_1_2_SAR2_ERR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TX_3_4_TEST_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_ADC_IB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_ATEST_REFCTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_TEST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_TEST_BLK_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_TXFE_CLKDIV)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_SAR1_ERR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TX_3_4_SAR2_ERR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CLASSH_MODE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_MODE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_MODE_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_VCL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_VCL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_BUCK_TMUX_A_D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_BUCK_SW_DRV_CNTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_SPARE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEGDAC_CTRL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEGDAC_CTRL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEGDAC_CTRL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_CTRL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_TEST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_AUX_SW_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_PA_AUX_IN_CONN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_TIMER_DIV)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_OCP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_OCP_COUNT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_EAR_DAC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_EAR_AMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_LDO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_RDACBUFF_CNP2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_RDAC_LDO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_CNP1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_LOWPOWER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_DIFFLO_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_DIFFLO_REF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_DIFFLO_LDO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_SELO_DAC_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_BUCK_RST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_BUCK_VREF_ERRAMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_FLYB_ERRAMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_FLYB_BUFF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_FLYB_MID_RST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_HPH_R_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_HPH_CNP_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_CNP_WG_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_CNP_WG_TIME)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_OCP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_AUTO_CHOP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_CHOP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_PA_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_PA_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_TEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_ATEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_R_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_R_TEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_R_ATEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_RDAC_CLK_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_RDAC_CLK_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_RDAC_LDO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_REFBUFF_UHQA_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_REFBUFF_LP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_DAC_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_R_DAC_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_EN_REG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_CMBUFF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_ICTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_EN_DBG_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_CNP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_DAC_CTL_ATEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_STATUS_REG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EAR_EAR_MISC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_MISC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_LO2_COMPANDER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_LO1_COMPANDER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_COMMON)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_BYPASS_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_CNP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_CORE_OUT_PROG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_LDO_OUT_PROG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_COM_SWCAP_REFBUF_FREQ)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_COM_PA_FREQ)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_RESERVED_REG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_LO1_STATUS_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_DIFF_LO_LO1_STATUS_2)] = WCD934X_READ, +}; + +const u8 wcd934x_page7_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_ANA_NEW_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_ANA_HPH2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_ANA_HPH3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_ANA_LDO_CONFIG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_LDO_OCP_CONFIG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_TX_LDO_CONFIG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_TX_DRV_CONFIG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_RX_CONFIG_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_RX_CONFIG_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_PLL_ENABLES)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_PLL_PRESET)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_PLL_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CLK_SYS_PLL_ENABLES)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_PLL_PRESET)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_PLL_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CLK_SYS_MCLK_PRG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_MCLK2_PRG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_MCLK2_PRG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_XO_PRG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_XO_CAP_XTP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_XO_CAP_XTM)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_BST_EN_DLY)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_CTRL_ILIM)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_VOUT_SETTING)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_VOUT_A_STARTUP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_VOUT_D_STARTUP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_VOUT_D_FREQ1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_VOUT_D_FREQ2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_ELECT_REM_CLAMP_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_PLUG_DETECT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_ZDET_ANA_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_ZDET_RAMP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_FSM_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MBHC_NEW_ADC_RESULT)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TX_NEW_AMIC_4_5_SEL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_NEW_ADC_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_NEW_ADC_DOUTMSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_VBADC_NEW_ADC_DOUTLSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_HD2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_VREF_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_MISC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_MISC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_MISC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_RDAC_MISC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_RDAC_MISC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_RDAC_MISC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_ULP)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_NEW_INT_HPH_RDAC_LDO_LP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_LDO_TEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_TX_LDO_TEST)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_TX_DRV_TEST)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_TEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_TEST_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_CLK_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RESERVED_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RESERVED_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_L_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_M_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_N_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_PFD_CP_DSM_PROG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_VCO_PROG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_LDO_LOCK_CFG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_DIG_LOCK_DET_CFG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_POST_DIV_REG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_POST_DIV_REG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_REF_DIV_REG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_REF_DIV_REG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_FILTER_REG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_FILTER_REG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_PLL_L_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_PLL_M_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_PLL_N_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_TEST_REG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_VCO_PROG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_TEST_REG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_LDO_LOCK_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_CLK_TEST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_CLK_TEST2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_CLK_TEST3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_XO_TEST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_XO_TEST2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_VCOMP_HYST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_VLOOP_FILTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_IDELTA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_ILIM_STARTUP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_MIN_ONTIME)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_MAX_ONTIME)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_TIMING)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_TMUX_A_D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_SW_DRV_CNTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_SPARE1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_SPARE2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_SPARE_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_INC_WAIT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_IBLEED_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DEBUG_CPROVR_TEST)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_CTL_A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_CTL_D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_INT_SLNQ_HPF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_INT_SLNQ_REF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_INT_SLNQ_COMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_INT_SPARE_2)] = WCD934X_READ_WRITE, + +}; + +const u8 wcd934x_page10_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE10_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_CLK_RESET_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_MODE_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_MODE_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FF_SHIFT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FB_SHIFT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_LPF_FF_A_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_LPF_FF_B_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_LPF_FB_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_SMLPF_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_DCFLT_SHIFT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_IIR_ADAPT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_IIR_COEFF_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_IIR_COEFF_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FF_A_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FF_B_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FB_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_RC_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FIFO_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_RC0_STATUS_FMIN_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC0_RC1_STATUS_FMIN_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC0_RC0_STATUS_FMAX_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC0_RC1_STATUS_FMAX_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC0_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_CLK_RESET_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_MODE_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_MODE_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FF_SHIFT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FB_SHIFT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_LPF_FF_A_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_LPF_FF_B_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_LPF_FB_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_SMLPF_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_DCFLT_SHIFT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_IIR_ADAPT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_IIR_COEFF_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_IIR_COEFF_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FF_A_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FF_B_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FB_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_RC_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FIFO_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_RC0_STATUS_FMIN_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_RC1_STATUS_FMIN_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_RC0_STATUS_FMAX_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_RC1_STATUS_FMAX_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0)] = + WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page11_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE11_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page12_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE12_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_CRC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_DLY_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_DECAY_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_HPH_V_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_EAR_V_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_HPH_V_HD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_EAR_V_HD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_K1_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_K1_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_K2_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_K2_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_IDLE_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_IDLE_HPH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_IDLE_EAR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_TEST0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_TEST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_OVR_VREF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_ADC_CAL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_ADC_CAL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_ADC_CAL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PK_EST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PK_EST2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PK_EST3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_RF_PROC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_RF_PROC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_DEBUG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD_MON)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_MON_VAL)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_BAN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC1_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC1_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC2_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC2_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC2_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC3_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC3_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC3_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_ACCESS_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_LSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_MSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_LSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_MSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_REF_HQ0_EC_REF_HQ_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_REF_HQ0_EC_REF_HQ_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_REF_HQ1_EC_REF_HQ_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_REF_HQ1_EC_REF_HQ_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC1_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC1_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FIFO)] = WCD934X_READ, +}; + +const u8 wcd934x_page13_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE13_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_ANC_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_EC_REF_HQ_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_GFM_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_CTRL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_TOP_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_TOP_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_TOP_CFG7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_WR_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_WR_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_LUT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_RD_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_RD_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_WR_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_WR_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_LUT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_RD_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_RD_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_WR_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_WR_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_LUT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_RD_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_RD_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_WR_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_WR_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_LUT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_RD_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_RD_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_DSD0_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG3)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page14_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE14_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_CLK_RST_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_PULSE_SUPR_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_TIMER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_BW_SW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_THRESH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_TIMER2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RMAX_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RMIN_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_PH_DET)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_MB_SW_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_MAST_DIAG_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_7_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_15_8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_23_16)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_31_24)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_39_32)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_40_43)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_CLK_RST_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_PULSE_SUPR_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_TIMER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_BW_SW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_THRESH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_TIMER2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RMAX_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RMIN_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_PH_DET)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_MB_SW_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_MAST_DIAG_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_7_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_15_8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_23_16)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_31_24)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_39_32)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_40_43)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_CLK_RST_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_PULSE_SUPR_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_TIMER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_BW_SW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_THRESH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_TIMER2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RMAX_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RMIN_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_PH_DET)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_MB_SW_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_MAST_DIAG_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_7_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_15_8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_23_16)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_31_24)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_39_32)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_40_43)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_CLK_RST_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_PULSE_SUPR_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_TIMER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_BW_SW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_THRESH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_TIMER2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RMAX_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RMIN_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_PH_DET)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_MB_SW_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_MAST_DIAG_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_7_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_15_8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_23_16)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_31_24)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_39_32)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_40_43)] = WCD934X_READ, +}; + +const u8 wcd934x_page15_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE15_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC0_CLK_RST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC0_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SPLINE_SRC1_CLK_RST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC1_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SPLINE_SRC2_CLK_RST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC2_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SPLINE_SRC3_CLK_RST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC3_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC0_RC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC0_RC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC1_RC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC1_RC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC_RC_RST_DBG_CNTR)] = + WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page_0x50_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE80_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_MAX_SVS2_STEP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_START)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CPR_STATUS)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page_0x80_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE80_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_MAX_SVS2_STEP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_START)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CPR_STATUS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_PAGE128_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_BIST_MODE_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_RF_PA_ON_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_INTR1_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_INTR2_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SWR_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SWR_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_2_SCK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SLIMBUS_DATA1_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SLIMBUS_DATA2_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SLIMBUS_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2C_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2C_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_0_RX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_0_TX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_0_SCK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_0_WS_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_1_RX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_1_TX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_1_SCK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_1_WS_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC1_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC1_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC2_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC2_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC3_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC3_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_JTCK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO1_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO2_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO3_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO4_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SPI_S_CSN_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SPI_S_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SPI_S_DOUT_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SPI_S_DIN_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_BA_N_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO0_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_2_RX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_2_WS_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_DRVCTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_DRVCTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TEST_DEBUG_NPL_DLY_TEST_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_NPL_DLY_TEST_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_MEM_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_BUS_SEL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_JTAG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_ANA_DTEST_DIR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_SYSMEM_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_SOC_SW_PWR_SEQ_DELAY)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_NOM_LOW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_NOM_HIGH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_SPI_SLAVE_CHAR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_CODEC_DIAGS)] = WCD934X_READ, +}; + +const u8 * const wcd934x_reg[WCD934X_NUM_PAGES] = { + [WCD934X_PAGE_0] = wcd934x_page0_reg_access, + [WCD934X_PAGE_1] = wcd934x_page1_reg_access, + [WCD934X_PAGE_2] = wcd934x_page2_reg_access, + [WCD934X_PAGE_4] = wcd934x_page4_reg_access, + [WCD934X_PAGE_5] = wcd934x_page5_reg_access, + [WCD934X_PAGE_6] = wcd934x_page6_reg_access, + [WCD934X_PAGE_7] = wcd934x_page7_reg_access, + [WCD934X_PAGE_10] = wcd934x_page10_reg_access, + [WCD934X_PAGE_11] = wcd934x_page11_reg_access, + [WCD934X_PAGE_12] = wcd934x_page12_reg_access, + [WCD934X_PAGE_13] = wcd934x_page13_reg_access, + [WCD934X_PAGE_14] = wcd934x_page14_reg_access, + [WCD934X_PAGE_15] = wcd934x_page15_reg_access, + [WCD934X_PAGE_0x50] = wcd934x_page_0x50_reg_access, + [WCD934X_PAGE_0X80] = wcd934x_page_0x80_reg_access, +}; diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c new file mode 100644 index 0000000000000000000000000000000000000000..f0126dd560333f7f411d7810fd21dc2f73bbb632 --- /dev/null +++ b/drivers/mfd/wcd9xxx-core.c @@ -0,0 +1,1690 @@ +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd9xxx-regmap.h" + +#define WCD9XXX_REGISTER_START_OFFSET 0x800 +#define WCD9XXX_SLIM_RW_MAX_TRIES 3 +#define SLIMBUS_PRESENT_TIMEOUT 100 + +#define MAX_WCD9XXX_DEVICE 4 +#define WCD9XXX_I2C_GSBI_SLAVE_ID "3-000d" +#define WCD9XXX_I2C_TOP_SLAVE_ADDR 0x0d +#define WCD9XXX_ANALOG_I2C_SLAVE_ADDR 0x77 +#define WCD9XXX_DIGITAL1_I2C_SLAVE_ADDR 0x66 +#define WCD9XXX_DIGITAL2_I2C_SLAVE_ADDR 0x55 +#define WCD9XXX_I2C_TOP_LEVEL 0 +#define WCD9XXX_I2C_ANALOG 1 +#define WCD9XXX_I2C_DIGITAL_1 2 +#define WCD9XXX_I2C_DIGITAL_2 3 + +/* + * Number of return values needs to be checked for each + * registration of Slimbus of I2C bus for each codec + */ +#define NUM_WCD9XXX_REG_RET 4 + +#define SLIM_USR_MC_REPEAT_CHANGE_VALUE 0x0 +#define SLIM_REPEAT_WRITE_MAX_SLICE 16 +#define REG_BYTES 2 +#define VAL_BYTES 1 +#define WCD9XXX_PAGE_NUM(reg) (((reg) >> 8) & 0xff) +#define WCD9XXX_PAGE_SIZE 256 + +struct wcd9xxx_i2c { + struct i2c_client *client; + struct i2c_msg xfer_msg[2]; + struct mutex xfer_lock; + int mod_id; +}; + +static struct regmap_config wcd9xxx_base_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .can_multi_write = true, +}; + +static struct regmap_config wcd9xxx_i2c_base_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .can_multi_write = false, + .use_single_rw = true, +}; + +static u8 wcd9xxx_pgd_la; +static u8 wcd9xxx_inf_la; + +static const int wcd9xxx_cdc_types[] = { + [WCD9XXX] = WCD9XXX, + [WCD9330] = WCD9330, + [WCD9335] = WCD9335, + [WCD934X] = WCD934X, +}; + +static const struct of_device_id wcd9xxx_of_match[] = { + { .compatible = "qcom,tasha-i2c-pgd", + .data = (void *)&wcd9xxx_cdc_types[WCD9335]}, + { .compatible = "qcom,wcd9xxx-i2c", + .data = (void *)&wcd9xxx_cdc_types[WCD9330]}, + { } +}; +MODULE_DEVICE_TABLE(of, wcd9xxx_of_match); + +static int wcd9xxx_slim_device_up(struct slim_device *sldev); +static int wcd9xxx_slim_device_down(struct slim_device *sldev); + +struct wcd9xxx_i2c wcd9xxx_modules[MAX_WCD9XXX_DEVICE]; + +static int wcd9xxx_slim_multi_reg_write(struct wcd9xxx *wcd9xxx, + const void *data, size_t count) +{ + unsigned int reg; + struct device *dev; + u8 val[WCD9XXX_PAGE_SIZE]; + int ret = 0; + int i = 0; + int n = 0; + unsigned int page_num; + size_t num_regs = (count / (REG_BYTES + VAL_BYTES)); + struct wcd9xxx_reg_val *bulk_reg; + u8 *buf; + + dev = wcd9xxx->dev; + if (!data) { + dev_err(dev, "%s: data is NULL\n", __func__); + return -EINVAL; + } + if (num_regs == 0) + return -EINVAL; + + bulk_reg = kzalloc(num_regs * (sizeof(struct wcd9xxx_reg_val)), + GFP_KERNEL); + if (!bulk_reg) + return -ENOMEM; + + buf = (u8 *)data; + reg = *(u16 *)buf; + page_num = WCD9XXX_PAGE_NUM(reg); + for (i = 0, n = 0; n < num_regs; i++, n++) { + reg = *(u16 *)buf; + if (page_num != WCD9XXX_PAGE_NUM(reg)) { + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + i, false); + page_num = WCD9XXX_PAGE_NUM(reg); + i = 0; + } + buf += REG_BYTES; + val[i] = *buf; + buf += VAL_BYTES; + bulk_reg[i].reg = reg; + bulk_reg[i].buf = &val[i]; + bulk_reg[i].bytes = 1; + } + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + i, false); + if (ret) + dev_err(dev, "%s: error writing bulk regs\n", + __func__); + + kfree(bulk_reg); + return ret; +} + +/* + * wcd9xxx_interface_reg_read: Read slim interface registers + * + * @wcd9xxx: Pointer to wcd9xxx structure + * @reg: register adderss + * + * Returns register value in success and negative error code in case of failure + */ +int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg) +{ + u8 val; + int ret; + + mutex_lock(&wcd9xxx->io_lock); + ret = wcd9xxx->read_dev(wcd9xxx, reg, 1, (void *)&val, + true); + if (ret < 0) + dev_err(wcd9xxx->dev, "%s: Codec read 0x%x failed\n", + __func__, reg); + else + dev_dbg(wcd9xxx->dev, "%s: Read 0x%02x from 0x%x\n", + __func__, val, reg); + + mutex_unlock(&wcd9xxx->io_lock); + + if (ret < 0) + return ret; + else + return val; +} +EXPORT_SYMBOL(wcd9xxx_interface_reg_read); + +/* + * wcd9xxx_interface_reg_write: Write slim interface registers + * + * @wcd9xxx: Pointer to wcd9xxx structure + * @reg: register adderss + * @val: value of the register to be written + * + * Returns 0 for success and negative error code in case of failure + */ +int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg, + u8 val) +{ + int ret; + + mutex_lock(&wcd9xxx->io_lock); + ret = wcd9xxx->write_dev(wcd9xxx, reg, 1, (void *)&val, true); + dev_dbg(wcd9xxx->dev, "%s: Write %02x to 0x%x ret(%d)\n", + __func__, val, reg, ret); + mutex_unlock(&wcd9xxx->io_lock); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_interface_reg_write); + +static int wcd9xxx_slim_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *dest, bool interface) +{ + int ret; + struct slim_ele_access msg; + int slim_read_tries = WCD9XXX_SLIM_RW_MAX_TRIES; + + msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg; + msg.num_bytes = bytes; + msg.comp = NULL; + + if (!wcd9xxx->dev_up) { + dev_dbg_ratelimited( + wcd9xxx->dev, "%s: No read allowed. dev_up = %d\n", + __func__, wcd9xxx->dev_up); + return 0; + } + + while (1) { + mutex_lock(&wcd9xxx->xfer_lock); + ret = slim_request_val_element(interface ? + wcd9xxx->slim_slave : wcd9xxx->slim, + &msg, dest, bytes); + mutex_unlock(&wcd9xxx->xfer_lock); + if (likely(ret == 0) || (--slim_read_tries == 0)) + break; + usleep_range(5000, 5100); + } + + if (ret) + dev_err(wcd9xxx->dev, "%s: Error, Codec read failed (%d)\n", + __func__, ret); + + return ret; +} + +/* + * Interface specifies whether the write is to the interface or general + * registers. + */ +static int wcd9xxx_slim_write_device(struct wcd9xxx *wcd9xxx, + unsigned short reg, int bytes, void *src, bool interface) +{ + int ret; + struct slim_ele_access msg; + int slim_write_tries = WCD9XXX_SLIM_RW_MAX_TRIES; + + msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg; + msg.num_bytes = bytes; + msg.comp = NULL; + + if (!wcd9xxx->dev_up) { + dev_dbg_ratelimited( + wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", + __func__, wcd9xxx->dev_up); + return 0; + } + + while (1) { + mutex_lock(&wcd9xxx->xfer_lock); + ret = slim_change_val_element(interface ? + wcd9xxx->slim_slave : wcd9xxx->slim, + &msg, src, bytes); + mutex_unlock(&wcd9xxx->xfer_lock); + if (likely(ret == 0) || (--slim_write_tries == 0)) + break; + usleep_range(5000, 5100); + } + + if (ret) + pr_err("%s: Error, Codec write failed (%d)\n", __func__, ret); + + return ret; +} + +static int wcd9xxx_slim_get_allowed_slice(struct wcd9xxx *wcd9xxx, + int bytes) +{ + int allowed_sz = bytes; + + if (likely(bytes == SLIM_REPEAT_WRITE_MAX_SLICE)) + allowed_sz = 16; + else if (bytes >= 12) + allowed_sz = 12; + else if (bytes >= 8) + allowed_sz = 8; + else if (bytes >= 6) + allowed_sz = 6; + else if (bytes >= 4) + allowed_sz = 4; + else + allowed_sz = bytes; + + return allowed_sz; +} + +/* + * wcd9xxx_slim_write_repeat: Write the same register with multiple values + * @wcd9xxx: handle to wcd core + * @reg: register to be written + * @bytes: number of bytes to be written to reg + * @src: buffer with data content to be written to reg + * This API will write reg with bytes from src in a single slimbus + * transaction. All values from 1 to 16 are supported by this API. + */ +int wcd9xxx_slim_write_repeat(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *src) +{ + int ret = 0, bytes_to_write = bytes, bytes_allowed; + struct slim_ele_access slim_msg; + + mutex_lock(&wcd9xxx->io_lock); + if (wcd9xxx->type == WCD9335 || wcd9xxx->type == WCD934X) { + ret = wcd9xxx_page_write(wcd9xxx, ®); + if (ret) + goto done; + } + + slim_msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg; + slim_msg.comp = NULL; + + if (unlikely(bytes > SLIM_REPEAT_WRITE_MAX_SLICE)) { + dev_err(wcd9xxx->dev, "%s: size %d not supported\n", + __func__, bytes); + ret = -EINVAL; + goto done; + } + + if (!wcd9xxx->dev_up) { + dev_dbg_ratelimited( + wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", + __func__, wcd9xxx->dev_up); + ret = 0; + goto done; + } + + while (bytes_to_write > 0) { + bytes_allowed = wcd9xxx_slim_get_allowed_slice(wcd9xxx, + bytes_to_write); + + slim_msg.num_bytes = bytes_allowed; + mutex_lock(&wcd9xxx->xfer_lock); + ret = slim_user_msg(wcd9xxx->slim, wcd9xxx->slim->laddr, + SLIM_MSG_MT_DEST_REFERRED_USER, + SLIM_USR_MC_REPEAT_CHANGE_VALUE, + &slim_msg, src, bytes_allowed); + mutex_unlock(&wcd9xxx->xfer_lock); + + if (ret) { + dev_err(wcd9xxx->dev, "%s: failed, ret = %d\n", + __func__, ret); + break; + } + + bytes_to_write = bytes_to_write - bytes_allowed; + src = ((u8 *)src) + bytes_allowed; + } + +done: + mutex_unlock(&wcd9xxx->io_lock); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_slim_write_repeat); + +/* + * wcd9xxx_slim_reserve_bw: API to reserve the slimbus bandwidth + * @wcd9xxx: Handle to the wcd9xxx core + * @bw_ops: value of the bandwidth that is requested + * @commit: Flag to indicate if bandwidth change is to be committed + * right away + */ +int wcd9xxx_slim_reserve_bw(struct wcd9xxx *wcd9xxx, + u32 bw_ops, bool commit) +{ + if (!wcd9xxx || !wcd9xxx->slim) { + pr_err("%s: Invalid handle to %s\n", + __func__, + (!wcd9xxx) ? "wcd9xxx" : "slim_device"); + return -EINVAL; + } + + return slim_reservemsg_bw(wcd9xxx->slim, bw_ops, commit); +} +EXPORT_SYMBOL(wcd9xxx_slim_reserve_bw); + +/* + * wcd9xxx_slim_bulk_write: API to write multiple registers with one descriptor + * @wcd9xxx: Handle to the wcd9xxx core + * @wcd9xxx_reg_val: structure holding register and values to be written + * @size: Indicates number of messages to be written with one descriptor + * @is_interface: Indicates whether the register is for slim interface or for + * general registers. + * @return: returns 0 if success or error information to the caller in case + * of failure. + */ +int wcd9xxx_slim_bulk_write(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_reg_val *bulk_reg, + unsigned int size, bool is_interface) +{ + int ret, i; + struct slim_val_inf *msgs; + unsigned short reg; + + if (!bulk_reg || !size || !wcd9xxx) { + pr_err("%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + if (!wcd9xxx->dev_up) { + dev_dbg_ratelimited( + wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", + __func__, wcd9xxx->dev_up); + return 0; + } + + msgs = kzalloc(size * (sizeof(struct slim_val_inf)), GFP_KERNEL); + if (!msgs) { + ret = -ENOMEM; + goto mem_fail; + } + + mutex_lock(&wcd9xxx->io_lock); + reg = bulk_reg->reg; + for (i = 0; i < size; i++) { + msgs[i].start_offset = WCD9XXX_REGISTER_START_OFFSET + + (bulk_reg->reg & 0xFF); + msgs[i].num_bytes = bulk_reg->bytes; + msgs[i].wbuf = bulk_reg->buf; + bulk_reg++; + } + ret = wcd9xxx_page_write(wcd9xxx, ®); + if (ret) { + pr_err("%s: Page write error for reg: 0x%x\n", + __func__, reg); + goto err; + } + + ret = slim_bulk_msg_write(is_interface ? + wcd9xxx->slim_slave : wcd9xxx->slim, + SLIM_MSG_MT_CORE, + SLIM_MSG_MC_CHANGE_VALUE, msgs, size, + NULL, NULL); + if (ret) + pr_err("%s: Error, Codec bulk write failed (%d)\n", + __func__, ret); + /* 100 usec sleep is needed as per HW requirement */ + usleep_range(100, 110); +err: + mutex_unlock(&wcd9xxx->io_lock); + kfree(msgs); +mem_fail: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_slim_bulk_write); + +static int wcd9xxx_num_irq_regs(const struct wcd9xxx *wcd9xxx) +{ + return (wcd9xxx->codec_type->num_irqs / 8) + + ((wcd9xxx->codec_type->num_irqs % 8) ? 1 : 0); +} + +static int wcd9xxx_regmap_init_cache(struct wcd9xxx *wcd9xxx) +{ + struct regmap_config *regmap_config; + int rc; + + regmap_config = wcd9xxx_get_regmap_config(wcd9xxx->type); + if (!regmap_config) { + dev_err(wcd9xxx->dev, "regmap config is not defined\n"); + return -EINVAL; + } + + rc = regmap_reinit_cache(wcd9xxx->regmap, regmap_config); + if (rc != 0) { + dev_err(wcd9xxx->dev, "%s:Failed to reinit register cache: %d\n", + __func__, rc); + } + + return rc; +} + +static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx) +{ + int ret = 0, i; + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + regmap_patch_fptr regmap_apply_patch = NULL; + + mutex_init(&wcd9xxx->io_lock); + mutex_init(&wcd9xxx->xfer_lock); + + ret = wcd9xxx_bringup(wcd9xxx->dev); + if (ret) { + ret = -EPROBE_DEFER; + goto err_bring_up; + } + + wcd9xxx->codec_type = devm_kzalloc(wcd9xxx->dev, + sizeof(struct wcd9xxx_codec_type), GFP_KERNEL); + if (!wcd9xxx->codec_type) { + ret = -ENOMEM; + goto err_bring_up; + } + ret = wcd9xxx_get_codec_info(wcd9xxx->dev); + if (ret) { + ret = -EPROBE_DEFER; + goto fail_cdc_fill; + } + wcd9xxx->version = wcd9xxx->codec_type->version; + if (!wcd9xxx->codec_type->dev || !wcd9xxx->codec_type->size) + goto fail_cdc_fill; + + core_res->parent = wcd9xxx; + core_res->dev = wcd9xxx->dev; + core_res->intr_table = wcd9xxx->codec_type->intr_tbl; + core_res->intr_table_size = wcd9xxx->codec_type->intr_tbl_size; + + for (i = 0; i < WCD9XXX_INTR_REG_MAX; i++) + wcd9xxx->core_res.intr_reg[i] = + wcd9xxx->codec_type->intr_reg[i]; + + wcd9xxx_core_res_init(&wcd9xxx->core_res, + wcd9xxx->codec_type->num_irqs, + wcd9xxx_num_irq_regs(wcd9xxx), + wcd9xxx->regmap); + + if (wcd9xxx_core_irq_init(&wcd9xxx->core_res)) + goto err; + + ret = wcd9xxx_regmap_init_cache(wcd9xxx); + if (ret) + goto err_irq; + + regmap_apply_patch = wcd9xxx_get_regmap_reg_patch( + wcd9xxx->type); + if (regmap_apply_patch) { + ret = regmap_apply_patch(wcd9xxx->regmap, + wcd9xxx->version); + if (ret) + dev_err(wcd9xxx->dev, + "Failed to register patch: %d\n", ret); + } + + ret = mfd_add_devices(wcd9xxx->dev, -1, wcd9xxx->codec_type->dev, + wcd9xxx->codec_type->size, NULL, 0, NULL); + if (ret != 0) { + dev_err(wcd9xxx->dev, "Failed to add children: %d\n", ret); + goto err_irq; + } + + ret = device_init_wakeup(wcd9xxx->dev, true); + if (ret) { + dev_err(wcd9xxx->dev, "Device wakeup init failed: %d\n", ret); + goto err_irq; + } + + return ret; +err_irq: + wcd9xxx_irq_exit(&wcd9xxx->core_res); +fail_cdc_fill: + devm_kfree(wcd9xxx->dev, wcd9xxx->codec_type); + wcd9xxx->codec_type = NULL; +err: + wcd9xxx_bringdown(wcd9xxx->dev); + wcd9xxx_core_res_deinit(&wcd9xxx->core_res); +err_bring_up: + mutex_destroy(&wcd9xxx->io_lock); + mutex_destroy(&wcd9xxx->xfer_lock); + return ret; +} + +static void wcd9xxx_device_exit(struct wcd9xxx *wcd9xxx) +{ + device_init_wakeup(wcd9xxx->dev, false); + wcd9xxx_irq_exit(&wcd9xxx->core_res); + wcd9xxx_bringdown(wcd9xxx->dev); + wcd9xxx_reset_low(wcd9xxx->dev); + wcd9xxx_core_res_deinit(&wcd9xxx->core_res); + mutex_destroy(&wcd9xxx->io_lock); + mutex_destroy(&wcd9xxx->xfer_lock); + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + slim_remove_device(wcd9xxx->slim_slave); +} + + +#ifdef CONFIG_DEBUG_FS +struct wcd9xxx *debugCodec; + +static struct dentry *debugfs_wcd9xxx_dent; +static struct dentry *debugfs_peek; +static struct dentry *debugfs_poke; +static struct dentry *debugfs_power_state; +static struct dentry *debugfs_reg_dump; + +static unsigned char read_data; + +static int codec_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int get_parameters(char *buf, long int *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token != NULL) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtoul(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + return 0; +} + +static ssize_t wcd9xxx_slimslave_reg_show(char __user *ubuf, size_t count, + loff_t *ppos) +{ + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[25]; /* each line is 12 bytes but 25 for margin of error */ + + for (i = (int) *ppos / 12; i <= SLIM_MAX_REG_ADDR; i++) { + reg_val = wcd9xxx_interface_reg_read(debugCodec, i); + len = snprintf(tmp_buf, sizeof(tmp_buf), + "0x%.3x: 0x%.2x\n", i, reg_val); + + if ((total + len) >= count - 1) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + pr_err("%s: fail to copy reg dump\n", __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } + +copy_err: + return total; +} + +static ssize_t codec_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char lbuf[8]; + char *access_str = file->private_data; + ssize_t ret_cnt; + + if (*ppos < 0 || !count) + return -EINVAL; + + if (!strcmp(access_str, "slimslave_peek")) { + snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data); + ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf, + strnlen(lbuf, 7)); + } else if (!strcmp(access_str, "slimslave_reg_dump")) { + ret_cnt = wcd9xxx_slimslave_reg_show(ubuf, count, ppos); + } else { + pr_err("%s: %s not permitted to read\n", __func__, access_str); + ret_cnt = -EPERM; + } + + return ret_cnt; +} + +static void wcd9xxx_set_reset_pin_state(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_pdata *pdata, + bool active) +{ + if (wcd9xxx->wcd_rst_np) { + if (active) + msm_cdc_pinctrl_select_active_state( + wcd9xxx->wcd_rst_np); + else + msm_cdc_pinctrl_select_sleep_state( + wcd9xxx->wcd_rst_np); + + return; + } else if (gpio_is_valid(wcd9xxx->reset_gpio)) { + gpio_direction_output(wcd9xxx->reset_gpio, + (active == true ? 1 : 0)); + } +} + +static int codec_debug_process_cdc_power(char *lbuf) +{ + long int param; + int rc; + struct wcd9xxx_pdata *pdata; + + if (wcd9xxx_get_intf_type() != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + pr_err("%s: CODEC is not in SLIMBUS mode\n", __func__); + rc = -EPERM; + goto error_intf; + } + + rc = get_parameters(lbuf, ¶m, 1); + + if (likely(!rc)) { + pdata = debugCodec->slim->dev.platform_data; + if (param == 0) { + wcd9xxx_slim_device_down(debugCodec->slim); + msm_cdc_disable_static_supplies(debugCodec->dev, + debugCodec->supplies, + pdata->regulator, + pdata->num_supplies); + wcd9xxx_set_reset_pin_state(debugCodec, pdata, false); + } else if (param == 1) { + msm_cdc_enable_static_supplies(debugCodec->dev, + debugCodec->supplies, + pdata->regulator, + pdata->num_supplies); + usleep_range(1000, 2000); + wcd9xxx_set_reset_pin_state(debugCodec, pdata, false); + usleep_range(1000, 2000); + wcd9xxx_set_reset_pin_state(debugCodec, pdata, true); + usleep_range(1000, 2000); + wcd9xxx_slim_device_up(debugCodec->slim); + } else { + pr_err("%s: invalid command %ld\n", __func__, param); + } + } + +error_intf: + return rc; +} + +static ssize_t codec_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *access_str = filp->private_data; + char lbuf[32]; + int rc; + long int param[5]; + + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + + if (!strcmp(access_str, "slimslave_poke")) { + /* write */ + rc = get_parameters(lbuf, param, 2); + if ((param[0] <= 0x3FF) && (param[1] <= 0xFF) && + (rc == 0)) + wcd9xxx_interface_reg_write(debugCodec, param[0], + param[1]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "slimslave_peek")) { + /* read */ + rc = get_parameters(lbuf, param, 1); + if ((param[0] <= 0x3FF) && (rc == 0)) + read_data = wcd9xxx_interface_reg_read(debugCodec, + param[0]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "power_state")) { + rc = codec_debug_process_cdc_power(lbuf); + } + + if (rc == 0) + rc = cnt; + else + pr_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations codec_debug_ops = { + .open = codec_debug_open, + .write = codec_debug_write, + .read = codec_debug_read +}; +#endif + +static struct wcd9xxx_i2c *wcd9xxx_i2c_get_device_info(struct wcd9xxx *wcd9xxx, + u16 reg) +{ + u16 mask = 0x0f00; + int value = 0; + struct wcd9xxx_i2c *wcd9xxx_i2c = NULL; + + if (wcd9xxx->type == WCD9335) { + wcd9xxx_i2c = &wcd9xxx_modules[0]; + } else { + value = ((reg & mask) >> 8) & 0x000f; + switch (value) { + case 0: + wcd9xxx_i2c = &wcd9xxx_modules[0]; + break; + case 1: + wcd9xxx_i2c = &wcd9xxx_modules[1]; + break; + case 2: + wcd9xxx_i2c = &wcd9xxx_modules[2]; + break; + case 3: + wcd9xxx_i2c = &wcd9xxx_modules[3]; + break; + + default: + break; + } + } + return wcd9xxx_i2c; +} + +static int wcd9xxx_i2c_write_device(struct wcd9xxx *wcd9xxx, u16 reg, u8 *value, + u32 bytes) +{ + + struct i2c_msg *msg; + int ret = 0; + u8 reg_addr = 0; + u8 data[bytes + 1]; + struct wcd9xxx_i2c *wcd9xxx_i2c; + + wcd9xxx_i2c = wcd9xxx_i2c_get_device_info(wcd9xxx, reg); + if (wcd9xxx_i2c == NULL || wcd9xxx_i2c->client == NULL) { + pr_err("failed to get device info\n"); + return -ENODEV; + } + reg_addr = (u8)reg; + msg = &wcd9xxx_i2c->xfer_msg[0]; + msg->addr = wcd9xxx_i2c->client->addr; + msg->len = bytes + 1; + msg->flags = 0; + data[0] = reg; + data[1] = *value; + msg->buf = data; + ret = i2c_transfer(wcd9xxx_i2c->client->adapter, + wcd9xxx_i2c->xfer_msg, 1); + /* Try again if the write fails */ + if (ret != 1) { + ret = i2c_transfer(wcd9xxx_i2c->client->adapter, + wcd9xxx_i2c->xfer_msg, 1); + if (ret != 1) { + pr_err("failed to write the device\n"); + return ret; + } + } + pr_debug("write success register = %x val = %x\n", reg, data[1]); + return 0; +} + + +static int wcd9xxx_i2c_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, unsigned char *dest) +{ + struct i2c_msg *msg; + int ret = 0; + u8 reg_addr = 0; + struct wcd9xxx_i2c *wcd9xxx_i2c; + u8 i = 0; + + wcd9xxx_i2c = wcd9xxx_i2c_get_device_info(wcd9xxx, reg); + if (wcd9xxx_i2c == NULL || wcd9xxx_i2c->client == NULL) { + pr_err("failed to get device info\n"); + return -ENODEV; + } + for (i = 0; i < bytes; i++) { + reg_addr = (u8)reg++; + msg = &wcd9xxx_i2c->xfer_msg[0]; + msg->addr = wcd9xxx_i2c->client->addr; + msg->len = 1; + msg->flags = 0; + msg->buf = ®_addr; + + msg = &wcd9xxx_i2c->xfer_msg[1]; + msg->addr = wcd9xxx_i2c->client->addr; + msg->len = 1; + msg->flags = I2C_M_RD; + msg->buf = dest++; + ret = i2c_transfer(wcd9xxx_i2c->client->adapter, + wcd9xxx_i2c->xfer_msg, 2); + + /* Try again if read fails first time */ + if (ret != 2) { + ret = i2c_transfer(wcd9xxx_i2c->client->adapter, + wcd9xxx_i2c->xfer_msg, 2); + if (ret != 2) { + pr_err("failed to read wcd9xxx register\n"); + return ret; + } + } + } + return 0; +} + +int wcd9xxx_i2c_read(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *dest, bool interface_reg) +{ + return wcd9xxx_i2c_read_device(wcd9xxx, reg, bytes, dest); +} + +int wcd9xxx_i2c_write(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *src, bool interface_reg) +{ + return wcd9xxx_i2c_write_device(wcd9xxx, reg, src, bytes); +} + +static int wcd9xxx_i2c_get_client_index(struct i2c_client *client, + int *wcd9xx_index) +{ + int ret = 0; + + switch (client->addr) { + case WCD9XXX_I2C_TOP_SLAVE_ADDR: + *wcd9xx_index = WCD9XXX_I2C_TOP_LEVEL; + break; + case WCD9XXX_ANALOG_I2C_SLAVE_ADDR: + *wcd9xx_index = WCD9XXX_I2C_ANALOG; + break; + case WCD9XXX_DIGITAL1_I2C_SLAVE_ADDR: + *wcd9xx_index = WCD9XXX_I2C_DIGITAL_1; + break; + case WCD9XXX_DIGITAL2_I2C_SLAVE_ADDR: + *wcd9xx_index = WCD9XXX_I2C_DIGITAL_2; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int wcd9xxx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wcd9xxx *wcd9xxx = NULL; + struct wcd9xxx_pdata *pdata = NULL; + int val = 0; + int ret = 0; + int wcd9xx_index = 0; + struct device *dev; + int intf_type; + const struct of_device_id *of_id; + + intf_type = wcd9xxx_get_intf_type(); + + pr_debug("%s: interface status %d\n", __func__, intf_type); + if (intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + dev_dbg(&client->dev, "%s:Codec is detected in slimbus mode\n", + __func__); + return -ENODEV; + } else if (intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = wcd9xxx_i2c_get_client_index(client, &wcd9xx_index); + if (ret != 0) + dev_err(&client->dev, "%s: I2C set codec I2C\n" + "client failed\n", __func__); + else { + dev_err(&client->dev, "%s:probe for other slaves\n" + "devices of codec I2C slave Addr = %x\n", + __func__, client->addr); + wcd9xxx_modules[wcd9xx_index].client = client; + } + return ret; + } else if (intf_type == WCD9XXX_INTERFACE_TYPE_PROBING) { + dev = &client->dev; + if (client->dev.of_node) { + dev_dbg(&client->dev, "%s:Platform data\n" + "from device tree\n", __func__); + pdata = wcd9xxx_populate_dt_data(&client->dev); + if (!pdata) { + dev_err(&client->dev, + "%s: Fail to obtain pdata from device tree\n", + __func__); + ret = -EINVAL; + goto fail; + } + client->dev.platform_data = pdata; + } else { + dev_dbg(&client->dev, "%s:Platform data from\n" + "board file\n", __func__); + pdata = client->dev.platform_data; + } + wcd9xxx = devm_kzalloc(&client->dev, sizeof(struct wcd9xxx), + GFP_KERNEL); + if (!wcd9xxx) { + ret = -ENOMEM; + goto fail; + } + + if (!pdata) { + dev_dbg(&client->dev, "no platform data?\n"); + ret = -EINVAL; + goto fail; + } + wcd9xxx->type = WCD9XXX; + if (client->dev.of_node) { + of_id = of_match_device(wcd9xxx_of_match, &client->dev); + if (of_id) { + wcd9xxx->type = *((int *)of_id->data); + dev_info(&client->dev, "%s: codec type is %d\n", + __func__, wcd9xxx->type); + } + } else { + dev_info(&client->dev, "%s: dev.of_node is NULL, default to WCD9XXX\n", + __func__); + wcd9xxx->type = WCD9XXX; + } + wcd9xxx->regmap = wcd9xxx_regmap_init(&client->dev, + &wcd9xxx_i2c_base_regmap_config); + if (IS_ERR(wcd9xxx->regmap)) { + ret = PTR_ERR(wcd9xxx->regmap); + dev_err(&client->dev, "%s: Failed to allocate register map: %d\n", + __func__, ret); + goto err_codec; + } + wcd9xxx->reset_gpio = pdata->reset_gpio; + wcd9xxx->wcd_rst_np = pdata->wcd_rst_np; + + if (!wcd9xxx->wcd_rst_np) { + pdata->use_pinctrl = false; + dev_err(&client->dev, "%s: pinctrl not used for rst_n\n", + __func__); + goto err_codec; + } + + if (i2c_check_functionality(client->adapter, + I2C_FUNC_I2C) == 0) { + dev_dbg(&client->dev, "can't talk I2C?\n"); + ret = -EIO; + goto fail; + } + dev_set_drvdata(&client->dev, wcd9xxx); + wcd9xxx->dev = &client->dev; + wcd9xxx->dev_up = true; + if (client->dev.of_node) + wcd9xxx->mclk_rate = pdata->mclk_rate; + + wcd9xxx->num_of_supplies = pdata->num_supplies; + ret = msm_cdc_init_supplies(wcd9xxx->dev, &wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + if (!wcd9xxx->supplies) { + dev_err(wcd9xxx->dev, "%s: Cannot init wcd supplies\n", + __func__); + goto err_codec; + } + ret = msm_cdc_enable_static_supplies(wcd9xxx->dev, + wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + if (ret) { + dev_err(wcd9xxx->dev, "%s: wcd static supply enable failed!\n", + __func__); + goto err_codec; + } + /* For WCD9335, 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 + */ + if (wcd9xxx->type == WCD9335) + usleep_range(600, 650); + else + usleep_range(5, 10); + + ret = wcd9xxx_reset(wcd9xxx->dev); + if (ret) { + pr_err("%s: Resetting Codec failed\n", __func__); + goto err_supplies; + } + + ret = wcd9xxx_i2c_get_client_index(client, &wcd9xx_index); + if (ret != 0) { + pr_err("%s:Set codec I2C client failed\n", __func__); + goto err_supplies; + } + + wcd9xxx_modules[wcd9xx_index].client = client; + wcd9xxx->read_dev = wcd9xxx_i2c_read; + wcd9xxx->write_dev = wcd9xxx_i2c_write; + if (!wcd9xxx->dev->of_node) + wcd9xxx_assign_irq(&wcd9xxx->core_res, + pdata->irq, pdata->irq_base); + + ret = wcd9xxx_device_init(wcd9xxx); + if (ret) { + pr_err("%s: error, initializing device failed (%d)\n", + __func__, ret); + goto err_device_init; + } + + ret = wcd9xxx_i2c_read(wcd9xxx, WCD9XXX_A_CHIP_STATUS, 1, + &val, 0); + if (ret < 0) + pr_err("%s: failed to read the wcd9xxx status (%d)\n", + __func__, ret); + if (val != wcd9xxx->codec_type->i2c_chip_status) + pr_err("%s: unknown chip status 0x%x\n", __func__, val); + + wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_I2C); + + return ret; + } + + pr_err("%s: I2C probe in wrong state\n", __func__); + + +err_device_init: + wcd9xxx_reset_low(wcd9xxx->dev); +err_supplies: + msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + pdata->regulator = NULL; + pdata->num_supplies = 0; +err_codec: + devm_kfree(&client->dev, wcd9xxx); + dev_set_drvdata(&client->dev, NULL); +fail: + return ret; +} + +static int wcd9xxx_i2c_remove(struct i2c_client *client) +{ + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_pdata *pdata = client->dev.platform_data; + + wcd9xxx = dev_get_drvdata(&client->dev); + msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + wcd9xxx_device_exit(wcd9xxx); + dev_set_drvdata(&client->dev, NULL); + return 0; +} + +static int wcd9xxx_dt_parse_slim_interface_dev_info(struct device *dev, + struct slim_device *slim_ifd) +{ + int ret = 0; + struct property *prop; + + ret = of_property_read_string(dev->of_node, "qcom,cdc-slim-ifd", + &slim_ifd->name); + if (ret) { + dev_err(dev, "Looking up %s property in node %s failed", + "qcom,cdc-slim-ifd-dev", dev->of_node->full_name); + return -ENODEV; + } + prop = of_find_property(dev->of_node, + "qcom,cdc-slim-ifd-elemental-addr", NULL); + if (!prop) { + dev_err(dev, "Looking up %s property in node %s failed", + "qcom,cdc-slim-ifd-elemental-addr", + dev->of_node->full_name); + return -ENODEV; + } else if (prop->length != 6) { + dev_err(dev, "invalid codec slim ifd addr. addr length = %d\n", + prop->length); + return -ENODEV; + } + memcpy(slim_ifd->e_addr, prop->value, 6); + + return 0; +} + +static int wcd9xxx_slim_get_laddr(struct slim_device *sb, + const u8 *e_addr, u8 e_len, u8 *laddr) +{ + int ret; + const unsigned long timeout = jiffies + + msecs_to_jiffies(SLIMBUS_PRESENT_TIMEOUT); + + do { + ret = slim_get_logical_addr(sb, e_addr, e_len, laddr); + if (!ret) + break; + /* Give SLIMBUS time to report present and be ready. */ + usleep_range(1000, 1100); + pr_debug_ratelimited("%s: retyring get logical addr\n", + __func__); + } while time_before(jiffies, timeout); + + return ret; +} + +static int wcd9xxx_slim_probe(struct slim_device *slim) +{ + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_pdata *pdata; + int ret = 0; + int intf_type; + + intf_type = wcd9xxx_get_intf_type(); + + if (!slim) { + ret = -EINVAL; + goto err; + } + + if (intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + dev_dbg(&slim->dev, "%s:Codec is detected in I2C mode\n", + __func__); + return -ENODEV; + } + if (slim->dev.of_node) { + dev_info(&slim->dev, "Platform data from device tree\n"); + pdata = wcd9xxx_populate_dt_data(&slim->dev); + if (!pdata) { + dev_err(&slim->dev, + "%s: Fail to obtain pdata from device tree\n", + __func__); + ret = -EINVAL; + goto err; + } + + ret = wcd9xxx_dt_parse_slim_interface_dev_info(&slim->dev, + &pdata->slimbus_slave_device); + if (ret) { + dev_err(&slim->dev, "Error, parsing slim interface\n"); + devm_kfree(&slim->dev, pdata); + ret = -EINVAL; + goto err; + } + slim->dev.platform_data = pdata; + + } else { + dev_info(&slim->dev, "Platform data from board file\n"); + pdata = slim->dev.platform_data; + } + + if (!pdata) { + dev_err(&slim->dev, "Error, no platform data\n"); + ret = -EINVAL; + goto err; + } + + wcd9xxx = devm_kzalloc(&slim->dev, sizeof(struct wcd9xxx), + GFP_KERNEL); + if (!wcd9xxx) { + ret = -ENOMEM; + goto err; + } + if (!slim->ctrl) { + dev_err(&slim->dev, "%s: Error, no SLIMBUS control data\n", + __func__); + ret = -EINVAL; + goto err_codec; + } + wcd9xxx->type = slim_get_device_id(slim)->driver_data; + dev_info(&slim->dev, "%s: probing for wcd type: %d, name: %s\n", + __func__, wcd9xxx->type, slim_get_device_id(slim)->name); + + /* wcd9xxx members init */ + wcd9xxx->multi_reg_write = wcd9xxx_slim_multi_reg_write; + wcd9xxx->slim = slim; + slim_set_clientdata(slim, wcd9xxx); + wcd9xxx->reset_gpio = pdata->reset_gpio; + wcd9xxx->dev = &slim->dev; + wcd9xxx->mclk_rate = pdata->mclk_rate; + wcd9xxx->dev_up = true; + wcd9xxx->wcd_rst_np = pdata->wcd_rst_np; + + wcd9xxx->regmap = wcd9xxx_regmap_init(&slim->dev, + &wcd9xxx_base_regmap_config); + if (IS_ERR(wcd9xxx->regmap)) { + ret = PTR_ERR(wcd9xxx->regmap); + dev_err(&slim->dev, "%s: Failed to allocate register map: %d\n", + __func__, ret); + goto err_codec; + } + + if (!wcd9xxx->wcd_rst_np) { + pdata->use_pinctrl = false; + dev_err(&slim->dev, "%s: pinctrl not used for rst_n\n", + __func__); + goto err_codec; + } + + wcd9xxx->num_of_supplies = pdata->num_supplies; + ret = msm_cdc_init_supplies(&slim->dev, &wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + if (!wcd9xxx->supplies) { + dev_err(wcd9xxx->dev, "%s: Cannot init wcd supplies\n", + __func__); + goto err_codec; + } + ret = msm_cdc_enable_static_supplies(wcd9xxx->dev, + wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + if (ret) { + dev_err(wcd9xxx->dev, "%s: wcd static supply enable failed!\n", + __func__); + goto err_codec; + } + + /* + * For WCD9335, 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 + */ + if (wcd9xxx->type == WCD9335 || wcd9xxx->type == WCD934X) + usleep_range(600, 650); + else + usleep_range(5, 10); + + ret = wcd9xxx_reset(&slim->dev); + if (ret) { + dev_err(&slim->dev, "%s: Resetting Codec failed\n", __func__); + goto err_supplies; + } + + ret = wcd9xxx_slim_get_laddr(wcd9xxx->slim, wcd9xxx->slim->e_addr, + ARRAY_SIZE(wcd9xxx->slim->e_addr), + &wcd9xxx->slim->laddr); + if (ret) { + dev_err(&slim->dev, "%s: failed to get slimbus %s logical address: %d\n", + __func__, wcd9xxx->slim->name, ret); + goto err_reset; + } + wcd9xxx->read_dev = wcd9xxx_slim_read_device; + wcd9xxx->write_dev = wcd9xxx_slim_write_device; + wcd9xxx_pgd_la = wcd9xxx->slim->laddr; + wcd9xxx->slim_slave = &pdata->slimbus_slave_device; + if (!wcd9xxx->dev->of_node) + wcd9xxx_assign_irq(&wcd9xxx->core_res, + pdata->irq, pdata->irq_base); + + ret = slim_add_device(slim->ctrl, wcd9xxx->slim_slave); + if (ret) { + dev_err(&slim->dev, "%s: error, adding SLIMBUS device failed\n", + __func__); + goto err_reset; + } + + ret = wcd9xxx_slim_get_laddr(wcd9xxx->slim_slave, + wcd9xxx->slim_slave->e_addr, + ARRAY_SIZE(wcd9xxx->slim_slave->e_addr), + &wcd9xxx->slim_slave->laddr); + if (ret) { + dev_err(&slim->dev, "%s: failed to get slimbus %s logical address: %d\n", + __func__, wcd9xxx->slim->name, ret); + goto err_slim_add; + } + wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr; + wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_SLIMBUS); + + ret = wcd9xxx_device_init(wcd9xxx); + if (ret) { + dev_err(&slim->dev, "%s: error, initializing device failed (%d)\n", + __func__, ret); + goto err_slim_add; + } +#ifdef CONFIG_DEBUG_FS + debugCodec = wcd9xxx; + + debugfs_wcd9xxx_dent = debugfs_create_dir + ("wcd9xxx_core", 0); + if (!IS_ERR(debugfs_wcd9xxx_dent)) { + debugfs_peek = debugfs_create_file("slimslave_peek", + S_IFREG | 0444, debugfs_wcd9xxx_dent, + (void *) "slimslave_peek", &codec_debug_ops); + + debugfs_poke = debugfs_create_file("slimslave_poke", + S_IFREG | 0444, debugfs_wcd9xxx_dent, + (void *) "slimslave_poke", &codec_debug_ops); + + debugfs_power_state = debugfs_create_file("power_state", + S_IFREG | 0444, debugfs_wcd9xxx_dent, + (void *) "power_state", &codec_debug_ops); + + debugfs_reg_dump = debugfs_create_file("slimslave_reg_dump", + S_IFREG | 0444, debugfs_wcd9xxx_dent, + (void *) "slimslave_reg_dump", &codec_debug_ops); + } +#endif + + return ret; + +err_slim_add: + slim_remove_device(wcd9xxx->slim_slave); +err_reset: + wcd9xxx_reset_low(wcd9xxx->dev); +err_supplies: + msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); +err_codec: + slim_set_clientdata(slim, NULL); +err: + return ret; +} +static int wcd9xxx_slim_remove(struct slim_device *pdev) +{ + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_pdata *pdata = pdev->dev.platform_data; + +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(debugfs_wcd9xxx_dent); +#endif + wcd9xxx = slim_get_devicedata(pdev); + wcd9xxx_deinit_slimslave(wcd9xxx); + slim_remove_device(wcd9xxx->slim_slave); + msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + wcd9xxx_device_exit(wcd9xxx); + slim_set_clientdata(pdev, NULL); + return 0; +} + +static int wcd9xxx_device_up(struct wcd9xxx *wcd9xxx) +{ + int ret = 0; + struct wcd9xxx_core_resource *wcd9xxx_res = &wcd9xxx->core_res; + + dev_info(wcd9xxx->dev, "%s: codec bring up\n", __func__); + wcd9xxx_bringup(wcd9xxx->dev); + ret = wcd9xxx_irq_init(wcd9xxx_res); + if (ret) { + pr_err("%s: wcd9xx_irq_init failed : %d\n", __func__, ret); + } else { + if (wcd9xxx->post_reset) + ret = wcd9xxx->post_reset(wcd9xxx); + } + return ret; +} + +static int wcd9xxx_slim_device_reset(struct slim_device *sldev) +{ + int ret; + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + + dev_info(wcd9xxx->dev, "%s: device reset, dev_up = %d\n", + __func__, wcd9xxx->dev_up); + if (wcd9xxx->dev_up) + return 0; + + ret = wcd9xxx_reset(wcd9xxx->dev); + if (ret) + dev_err(wcd9xxx->dev, "%s: Resetting Codec failed\n", __func__); + + return ret; +} + +static int wcd9xxx_slim_device_up(struct slim_device *sldev) +{ + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + dev_info(wcd9xxx->dev, "%s: slim device up, dev_up = %d\n", + __func__, wcd9xxx->dev_up); + if (wcd9xxx->dev_up) + return 0; + + wcd9xxx->dev_up = true; + return wcd9xxx_device_up(wcd9xxx); +} + +static int wcd9xxx_slim_device_down(struct slim_device *sldev) +{ + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + + dev_info(wcd9xxx->dev, "%s: device down, dev_up = %d\n", + __func__, wcd9xxx->dev_up); + if (!wcd9xxx->dev_up) + return 0; + + wcd9xxx->dev_up = false; + wcd9xxx_irq_exit(&wcd9xxx->core_res); + if (wcd9xxx->dev_down) + wcd9xxx->dev_down(wcd9xxx); + wcd9xxx_reset_low(wcd9xxx->dev); + return 0; +} + +static int wcd9xxx_slim_resume(struct slim_device *sldev) +{ + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + return wcd9xxx_core_res_resume(&wcd9xxx->core_res); +} + +static int wcd9xxx_i2c_resume(struct device *dev) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + + if (wcd9xxx) + return wcd9xxx_core_res_resume(&wcd9xxx->core_res); + else + return 0; +} + +static int wcd9xxx_slim_suspend(struct slim_device *sldev, pm_message_t pmesg) +{ + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + return wcd9xxx_core_res_suspend(&wcd9xxx->core_res, pmesg); +} + +static int wcd9xxx_i2c_suspend(struct device *dev) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + pm_message_t pmesg = {0}; + + if (wcd9xxx) + return wcd9xxx_core_res_suspend(&wcd9xxx->core_res, pmesg); + else + return 0; +} + +static const struct slim_device_id wcd_slim_device_id[] = { + {"sitar-slim", 0}, + {"sitar1p1-slim", 0}, + {"tabla-slim", 0}, + {"tabla2x-slim", 0}, + {"taiko-slim-pgd", 0}, + {"tapan-slim-pgd", 0}, + {"tomtom-slim-pgd", WCD9330}, + {"tasha-slim-pgd", WCD9335}, + {"tavil-slim-pgd", WCD934X}, + {} +}; + +static struct slim_driver wcd_slim_driver = { + .driver = { + .name = "wcd-slim", + .owner = THIS_MODULE, + }, + .probe = wcd9xxx_slim_probe, + .remove = wcd9xxx_slim_remove, + .id_table = wcd_slim_device_id, + .resume = wcd9xxx_slim_resume, + .suspend = wcd9xxx_slim_suspend, + .device_up = wcd9xxx_slim_device_up, + .reset_device = wcd9xxx_slim_device_reset, + .device_down = wcd9xxx_slim_device_down, +}; + +static struct i2c_device_id wcd9xxx_id_table[] = { + {"wcd9xxx-i2c", WCD9XXX_I2C_TOP_LEVEL}, + {"wcd9xxx-i2c", WCD9XXX_I2C_ANALOG}, + {"wcd9xxx-i2c", WCD9XXX_I2C_DIGITAL_1}, + {"wcd9xxx-i2c", WCD9XXX_I2C_DIGITAL_2}, + {} +}; + +static struct i2c_device_id tasha_id_table[] = { + {"tasha-i2c-pgd", WCD9XXX_I2C_TOP_LEVEL}, + {} +}; + +static struct i2c_device_id tabla_id_table[] = { + {"tabla top level", WCD9XXX_I2C_TOP_LEVEL}, + {"tabla analog", WCD9XXX_I2C_ANALOG}, + {"tabla digital1", WCD9XXX_I2C_DIGITAL_1}, + {"tabla digital2", WCD9XXX_I2C_DIGITAL_2}, + {} +}; +MODULE_DEVICE_TABLE(i2c, tabla_id_table); + +static const struct dev_pm_ops wcd9xxx_i2c_pm_ops = { + .suspend = wcd9xxx_i2c_suspend, + .resume = wcd9xxx_i2c_resume, +}; + +static struct i2c_driver tabla_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tabla-i2c-core", + .pm = &wcd9xxx_i2c_pm_ops, + }, + .id_table = tabla_id_table, + .probe = wcd9xxx_i2c_probe, + .remove = wcd9xxx_i2c_remove, +}; + +static struct i2c_driver wcd9xxx_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "wcd9xxx-i2c-core", + .pm = &wcd9xxx_i2c_pm_ops, + }, + .id_table = wcd9xxx_id_table, + .probe = wcd9xxx_i2c_probe, + .remove = wcd9xxx_i2c_remove, +}; + +static struct i2c_driver wcd9335_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tasha-i2c-core", + .pm = &wcd9xxx_i2c_pm_ops, + }, + .id_table = tasha_id_table, + .probe = wcd9xxx_i2c_probe, + .remove = wcd9xxx_i2c_remove, +}; + +static int __init wcd9xxx_init(void) +{ + int ret[NUM_WCD9XXX_REG_RET] = {0}; + int i = 0; + + wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_PROBING); + + ret[0] = i2c_add_driver(&tabla_i2c_driver); + if (ret[0]) + pr_err("%s: Failed to add the tabla2x I2C driver: %d\n", + __func__, ret[0]); + + ret[1] = i2c_add_driver(&wcd9xxx_i2c_driver); + if (ret[1]) + pr_err("%s: Failed to add the wcd9xxx I2C driver: %d\n", + __func__, ret[1]); + + ret[2] = i2c_add_driver(&wcd9335_i2c_driver); + if (ret[2]) + pr_err("%s: Failed to add the wcd9335 I2C driver: %d\n", + __func__, ret[2]); + + ret[3] = slim_driver_register(&wcd_slim_driver); + if (ret[3]) + pr_err("%s: Failed to register wcd SB driver: %d\n", + __func__, ret[3]); + + for (i = 0; i < NUM_WCD9XXX_REG_RET; i++) { + if (ret[i]) + return ret[i]; + } + + return 0; +} +module_init(wcd9xxx_init); + +static void __exit wcd9xxx_exit(void) +{ + wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_PROBING); + + i2c_del_driver(&tabla_i2c_driver); + i2c_del_driver(&wcd9xxx_i2c_driver); + i2c_del_driver(&wcd9335_i2c_driver); + slim_driver_unregister(&wcd_slim_driver); +} +module_exit(wcd9xxx_exit); + +MODULE_DESCRIPTION("Codec core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c new file mode 100644 index 0000000000000000000000000000000000000000..1a50f37565f3913cfe0cc73edc2d4b371c47d841 --- /dev/null +++ b/drivers/mfd/wcd9xxx-irq.c @@ -0,0 +1,785 @@ +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE)) +#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE) + +#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 100 + +#ifndef NO_IRQ +#define NO_IRQ (-1) +#endif + +#ifdef CONFIG_OF +struct wcd9xxx_irq_drv_data { + struct irq_domain *domain; + int irq; +}; +#endif + +static int virq_to_phyirq( + struct wcd9xxx_core_resource *wcd9xxx_res, int virq); +static int phyirq_to_virq( + struct wcd9xxx_core_resource *wcd9xxx_res, int irq); +static unsigned int wcd9xxx_irq_get_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res); +static void wcd9xxx_irq_put_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res); +static int wcd9xxx_map_irq( + struct wcd9xxx_core_resource *wcd9xxx_res, int irq); + +static void wcd9xxx_irq_lock(struct irq_data *data) +{ + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + mutex_lock(&wcd9xxx_res->irq_lock); +} + +static void wcd9xxx_irq_sync_unlock(struct irq_data *data) +{ + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + int i; + + if ((ARRAY_SIZE(wcd9xxx_res->irq_masks_cur) > + WCD9XXX_MAX_IRQ_REGS) || + (ARRAY_SIZE(wcd9xxx_res->irq_masks_cache) > + WCD9XXX_MAX_IRQ_REGS)) { + pr_err("%s: Array Size out of bound\n", __func__); + return; + } + if (!wcd9xxx_res->wcd_core_regmap) { + pr_err("%s: Codec core regmap not defined\n", + __func__); + return; + } + + for (i = 0; i < ARRAY_SIZE(wcd9xxx_res->irq_masks_cur); i++) { + /* If there's been a change in the mask write it back + * to the hardware. + */ + if (wcd9xxx_res->irq_masks_cur[i] != + wcd9xxx_res->irq_masks_cache[i]) { + + wcd9xxx_res->irq_masks_cache[i] = + wcd9xxx_res->irq_masks_cur[i]; + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_MASK_BASE] + i, + wcd9xxx_res->irq_masks_cur[i]); + } + } + + mutex_unlock(&wcd9xxx_res->irq_lock); +} + +static void wcd9xxx_irq_enable(struct irq_data *data) +{ + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + int wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq); + int byte = BIT_BYTE(wcd9xxx_irq); + int size = ARRAY_SIZE(wcd9xxx_res->irq_masks_cur); + + if ((byte < size) && (byte >= 0)) { + wcd9xxx_res->irq_masks_cur[byte] &= + ~(BYTE_BIT_MASK(wcd9xxx_irq)); + } else { + pr_err("%s: Array size is %d but index is %d: Out of range\n", + __func__, size, byte); + } +} + +static void wcd9xxx_irq_disable(struct irq_data *data) +{ + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + int wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq); + int byte = BIT_BYTE(wcd9xxx_irq); + int size = ARRAY_SIZE(wcd9xxx_res->irq_masks_cur); + + if ((byte < size) && (byte >= 0)) { + wcd9xxx_res->irq_masks_cur[byte] + |= BYTE_BIT_MASK(wcd9xxx_irq); + } else { + pr_err("%s: Array size is %d but index is %d: Out of range\n", + __func__, size, byte); + } +} + +static void wcd9xxx_irq_ack(struct irq_data *data) +{ + int wcd9xxx_irq = 0; + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + + if (wcd9xxx_res == NULL) { + pr_err("%s: wcd9xxx_res is NULL\n", __func__); + return; + } + wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq); + pr_debug("%s: IRQ_ACK called for WCD9XXX IRQ: %d\n", + __func__, wcd9xxx_irq); +} + +static void wcd9xxx_irq_mask(struct irq_data *d) +{ + /* do nothing but required as linux calls irq_mask without NULL check */ +} + +static struct irq_chip wcd9xxx_irq_chip = { + .name = "wcd9xxx", + .irq_bus_lock = wcd9xxx_irq_lock, + .irq_bus_sync_unlock = wcd9xxx_irq_sync_unlock, + .irq_disable = wcd9xxx_irq_disable, + .irq_enable = wcd9xxx_irq_enable, + .irq_mask = wcd9xxx_irq_mask, + .irq_ack = wcd9xxx_irq_ack, +}; + +bool wcd9xxx_lock_sleep( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + enum wcd9xxx_pm_state os; + + /* + * wcd9xxx_{lock/unlock}_sleep will be called by wcd9xxx_irq_thread + * and its subroutines only motly. + * but btn0_lpress_fn is not wcd9xxx_irq_thread's subroutine and + * It can race with wcd9xxx_irq_thread. + * So need to embrace wlock_holders with mutex. + * + * If system didn't resume, we can simply return false so codec driver's + * IRQ handler can return without handling IRQ. + * As interrupt line is still active, codec will have another IRQ to + * retry shortly. + */ + mutex_lock(&wcd9xxx_res->pm_lock); + if (wcd9xxx_res->wlock_holders++ == 0) { + pr_debug("%s: holding wake lock\n", __func__); + pm_qos_update_request(&wcd9xxx_res->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + pm_stay_awake(wcd9xxx_res->dev); + } + mutex_unlock(&wcd9xxx_res->pm_lock); + + if (!wait_event_timeout(wcd9xxx_res->pm_wq, + ((os = wcd9xxx_pm_cmpxchg(wcd9xxx_res, + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE)) == + WCD9XXX_PM_SLEEPABLE || + (os == WCD9XXX_PM_AWAKE)), + msecs_to_jiffies( + WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) { + pr_warn("%s: system didn't resume within %dms, s %d, w %d\n", + __func__, + WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, wcd9xxx_res->pm_state, + wcd9xxx_res->wlock_holders); + wcd9xxx_unlock_sleep(wcd9xxx_res); + return false; + } + wake_up_all(&wcd9xxx_res->pm_wq); + return true; +} +EXPORT_SYMBOL(wcd9xxx_lock_sleep); + +void wcd9xxx_unlock_sleep( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + mutex_lock(&wcd9xxx_res->pm_lock); + if (--wcd9xxx_res->wlock_holders == 0) { + pr_debug("%s: releasing wake lock pm_state %d -> %d\n", + __func__, wcd9xxx_res->pm_state, WCD9XXX_PM_SLEEPABLE); + /* + * if wcd9xxx_lock_sleep failed, pm_state would be still + * WCD9XXX_PM_ASLEEP, don't overwrite + */ + if (likely(wcd9xxx_res->pm_state == WCD9XXX_PM_AWAKE)) + wcd9xxx_res->pm_state = WCD9XXX_PM_SLEEPABLE; + pm_qos_update_request(&wcd9xxx_res->pm_qos_req, + PM_QOS_DEFAULT_VALUE); + pm_relax(wcd9xxx_res->dev); + } + mutex_unlock(&wcd9xxx_res->pm_lock); + wake_up_all(&wcd9xxx_res->pm_wq); +} +EXPORT_SYMBOL(wcd9xxx_unlock_sleep); + +void wcd9xxx_nested_irq_lock(struct wcd9xxx_core_resource *wcd9xxx_res) +{ + mutex_lock(&wcd9xxx_res->nested_irq_lock); +} + +void wcd9xxx_nested_irq_unlock(struct wcd9xxx_core_resource *wcd9xxx_res) +{ + mutex_unlock(&wcd9xxx_res->nested_irq_lock); +} + + +static void wcd9xxx_irq_dispatch(struct wcd9xxx_core_resource *wcd9xxx_res, + struct intr_data *irqdata) +{ + int irqbit = irqdata->intr_num; + + if (!wcd9xxx_res->wcd_core_regmap) { + pr_err("%s: codec core regmap not defined\n", + __func__); + return; + } + + if (irqdata->clear_first) { + wcd9xxx_nested_irq_lock(wcd9xxx_res); + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE] + + BIT_BYTE(irqbit), + BYTE_BIT_MASK(irqbit)); + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT], + 0x02); + handle_nested_irq(phyirq_to_virq(wcd9xxx_res, irqbit)); + wcd9xxx_nested_irq_unlock(wcd9xxx_res); + } else { + wcd9xxx_nested_irq_lock(wcd9xxx_res); + handle_nested_irq(phyirq_to_virq(wcd9xxx_res, irqbit)); + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE] + + BIT_BYTE(irqbit), + BYTE_BIT_MASK(irqbit)); + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT], + 0x02); + + wcd9xxx_nested_irq_unlock(wcd9xxx_res); + } +} + +static irqreturn_t wcd9xxx_irq_thread(int irq, void *data) +{ + int ret; + int i; + struct intr_data irqdata; + char linebuf[128]; + static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 1); + struct wcd9xxx_core_resource *wcd9xxx_res = data; + int num_irq_regs = wcd9xxx_res->num_irq_regs; + u8 status[num_irq_regs], status1[num_irq_regs]; + + if (unlikely(wcd9xxx_lock_sleep(wcd9xxx_res) == false)) { + dev_err(wcd9xxx_res->dev, "Failed to hold suspend\n"); + return IRQ_NONE; + } + + if (!wcd9xxx_res->wcd_core_regmap) { + dev_err(wcd9xxx_res->dev, + "%s: Codec core regmap not supplied\n", + __func__); + goto err_disable_irq; + } + + ret = regmap_bulk_read(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_STATUS_BASE], + status, num_irq_regs); + + if (ret < 0) { + dev_err(wcd9xxx_res->dev, + "Failed to read interrupt status: %d\n", ret); + goto err_disable_irq; + } + + /* Apply masking */ + for (i = 0; i < num_irq_regs; i++) + status[i] &= ~wcd9xxx_res->irq_masks_cur[i]; + + memcpy(status1, status, sizeof(status1)); + + /* Find out which interrupt was triggered and call that interrupt's + * handler function + * + * Since codec has only one hardware irq line which is shared by + * codec's different internal interrupts, so it's possible master irq + * handler dispatches multiple nested irq handlers after breaking + * order. Dispatch interrupts in the order that is maintained by + * the interrupt table. + */ + for (i = 0; i < wcd9xxx_res->intr_table_size; i++) { + irqdata = wcd9xxx_res->intr_table[i]; + if (status[BIT_BYTE(irqdata.intr_num)] & + BYTE_BIT_MASK(irqdata.intr_num)) { + wcd9xxx_irq_dispatch(wcd9xxx_res, &irqdata); + status1[BIT_BYTE(irqdata.intr_num)] &= + ~BYTE_BIT_MASK(irqdata.intr_num); + } + } + + /* + * As a failsafe if unhandled irq is found, clear it to prevent + * interrupt storm. + * Note that we can say there was an unhandled irq only when no irq + * handled by nested irq handler since Taiko supports qdsp as irqs' + * destination for few irqs. Therefore driver shouldn't clear pending + * irqs when few handled while few others not. + */ + if (unlikely(!memcmp(status, status1, sizeof(status)))) { + if (__ratelimit(&ratelimit)) { + pr_warn("%s: Unhandled irq found\n", __func__); + hex_dump_to_buffer(status, sizeof(status), 16, 1, + linebuf, sizeof(linebuf), false); + pr_warn("%s: status0 : %s\n", __func__, linebuf); + hex_dump_to_buffer(status1, sizeof(status1), 16, 1, + linebuf, sizeof(linebuf), false); + pr_warn("%s: status1 : %s\n", __func__, linebuf); + } + + memset(status, 0xff, num_irq_regs); + + ret = regmap_bulk_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE], + status, num_irq_regs); + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT], + 0x02); + } + wcd9xxx_unlock_sleep(wcd9xxx_res); + + return IRQ_HANDLED; + +err_disable_irq: + dev_err(wcd9xxx_res->dev, + "Disable irq %d\n", wcd9xxx_res->irq); + + disable_irq_wake(wcd9xxx_res->irq); + disable_irq_nosync(wcd9xxx_res->irq); + wcd9xxx_unlock_sleep(wcd9xxx_res); + return IRQ_NONE; +} + +void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq, void *data) +{ + free_irq(phyirq_to_virq(wcd9xxx_res, irq), data); +} + +void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) +{ + if (wcd9xxx_res->irq) + enable_irq(phyirq_to_virq(wcd9xxx_res, irq)); +} + +void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) +{ + if (wcd9xxx_res->irq) + disable_irq_nosync(phyirq_to_virq(wcd9xxx_res, irq)); +} + +void wcd9xxx_disable_irq_sync( + struct wcd9xxx_core_resource *wcd9xxx_res, int irq) +{ + if (wcd9xxx_res->irq) + disable_irq(phyirq_to_virq(wcd9xxx_res, irq)); +} + +static int wcd9xxx_irq_setup_downstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + int irq, virq, ret; + + pr_debug("%s: enter\n", __func__); + + for (irq = 0; irq < wcd9xxx_res->num_irqs; irq++) { + /* Map OF irq */ + virq = wcd9xxx_map_irq(wcd9xxx_res, irq); + pr_debug("%s: irq %d -> %d\n", __func__, irq, virq); + if (virq == NO_IRQ) { + pr_err("%s, No interrupt specifier for irq %d\n", + __func__, irq); + return NO_IRQ; + } + + ret = irq_set_chip_data(virq, wcd9xxx_res); + if (ret) { + pr_err("%s: Failed to configure irq %d (%d)\n", + __func__, irq, ret); + return ret; + } + + if (wcd9xxx_res->irq_level_high[irq]) + irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip, + handle_level_irq); + else + irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip, + handle_edge_irq); + + irq_set_nested_thread(virq, 1); + } + + pr_debug("%s: leave\n", __func__); + + return 0; +} + +int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res) +{ + int i, ret; + u8 irq_level[wcd9xxx_res->num_irq_regs]; + struct irq_domain *domain; + struct device_node *pnode; + + mutex_init(&wcd9xxx_res->irq_lock); + mutex_init(&wcd9xxx_res->nested_irq_lock); + + pnode = of_irq_find_parent(wcd9xxx_res->dev->of_node); + if (unlikely(!pnode)) + return -EINVAL; + + domain = irq_find_host(pnode); + if (unlikely(!domain)) + return -EINVAL; + + wcd9xxx_res->domain = domain; + + wcd9xxx_res->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx_res); + if (!wcd9xxx_res->irq) { + pr_warn("%s: irq driver is not yet initialized\n", __func__); + mutex_destroy(&wcd9xxx_res->irq_lock); + mutex_destroy(&wcd9xxx_res->nested_irq_lock); + return -EPROBE_DEFER; + } + pr_debug("%s: probed irq %d\n", __func__, wcd9xxx_res->irq); + + /* Setup downstream IRQs */ + ret = wcd9xxx_irq_setup_downstream_irq(wcd9xxx_res); + if (ret) { + pr_err("%s: Failed to setup downstream IRQ\n", __func__); + wcd9xxx_irq_put_upstream_irq(wcd9xxx_res); + mutex_destroy(&wcd9xxx_res->irq_lock); + mutex_destroy(&wcd9xxx_res->nested_irq_lock); + return ret; + } + + /* All other wcd9xxx interrupts are edge triggered */ + wcd9xxx_res->irq_level_high[0] = true; + + /* mask all the interrupts */ + memset(irq_level, 0, wcd9xxx_res->num_irq_regs); + for (i = 0; i < wcd9xxx_res->num_irqs; i++) { + wcd9xxx_res->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i); + wcd9xxx_res->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i); + irq_level[BIT_BYTE(i)] |= + wcd9xxx_res->irq_level_high[i] << (i % BITS_PER_BYTE); + } + + if (!wcd9xxx_res->wcd_core_regmap) { + dev_err(wcd9xxx_res->dev, + "%s: Codec core regmap not defined\n", + __func__); + ret = -EINVAL; + goto fail_irq_init; + } + + for (i = 0; i < wcd9xxx_res->num_irq_regs; i++) { + /* Initialize interrupt mask and level registers */ + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_LEVEL_BASE] + i, + irq_level[i]); + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_MASK_BASE] + i, + wcd9xxx_res->irq_masks_cur[i]); + } + + ret = request_threaded_irq(wcd9xxx_res->irq, NULL, wcd9xxx_irq_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "wcd9xxx", wcd9xxx_res); + if (ret != 0) + dev_err(wcd9xxx_res->dev, "Failed to request IRQ %d: %d\n", + wcd9xxx_res->irq, ret); + else { + ret = enable_irq_wake(wcd9xxx_res->irq); + if (ret) + dev_err(wcd9xxx_res->dev, + "Failed to set wake interrupt on IRQ %d: %d\n", + wcd9xxx_res->irq, ret); + if (ret) + free_irq(wcd9xxx_res->irq, wcd9xxx_res); + } + + if (ret) + goto fail_irq_init; + + return ret; + +fail_irq_init: + dev_err(wcd9xxx_res->dev, + "%s: Failed to init wcd9xxx irq\n", __func__); + wcd9xxx_irq_put_upstream_irq(wcd9xxx_res); + mutex_destroy(&wcd9xxx_res->irq_lock); + mutex_destroy(&wcd9xxx_res->nested_irq_lock); + return ret; +} + +int wcd9xxx_request_irq(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + int virq; + + virq = phyirq_to_virq(wcd9xxx_res, irq); + + return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING, + name, data); +} + +void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res) +{ + dev_dbg(wcd9xxx_res->dev, "%s: Cleaning up irq %d\n", __func__, + wcd9xxx_res->irq); + + if (wcd9xxx_res->irq) { + disable_irq_wake(wcd9xxx_res->irq); + free_irq(wcd9xxx_res->irq, wcd9xxx_res); + wcd9xxx_res->irq = 0; + wcd9xxx_irq_put_upstream_irq(wcd9xxx_res); + } + mutex_destroy(&wcd9xxx_res->irq_lock); + mutex_destroy(&wcd9xxx_res->nested_irq_lock); +} + +#ifndef CONFIG_OF +static int phyirq_to_virq( + struct wcd9xxx_core_resource *wcd9xxx_res, + int offset) +{ + return wcd9xxx_res->irq_base + offset; +} + +static int virq_to_phyirq( + struct wcd9xxx_core_resource *wcd9xxx_res, + int virq) +{ + return virq - wcd9xxx_res->irq_base; +} + +static unsigned int wcd9xxx_irq_get_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + return wcd9xxx_res->irq; +} + +static void wcd9xxx_irq_put_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + /* Do nothing */ +} + +static int wcd9xxx_map_irq( + struct wcd9xxx_core_resource *wcd9xxx_core_res, int irq) +{ + return phyirq_to_virq(wcd9xxx_core_res, irq); +} +#else +static struct wcd9xxx_irq_drv_data * +wcd9xxx_irq_add_domain(struct device_node *node, + struct device_node *parent) +{ + struct wcd9xxx_irq_drv_data *data = NULL; + + pr_debug("%s: node %s, node parent %s\n", __func__, + node->name, node->parent->name); + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return NULL; + + /* + * wcd9xxx_intc interrupt controller supports N to N irq mapping with + * single cell binding with irq numbers(offsets) only. + * Use irq_domain_simple_ops that has irq_domain_simple_map and + * irq_domain_xlate_onetwocell. + */ + data->domain = irq_domain_add_linear(node, WCD9XXX_MAX_NUM_IRQS, + &irq_domain_simple_ops, data); + if (!data->domain) { + kfree(data); + return NULL; + } + + return data; +} + +static struct wcd9xxx_irq_drv_data * +wcd9xxx_get_irq_drv_d(const struct wcd9xxx_core_resource *wcd9xxx_res) +{ + struct irq_domain *domain; + + domain = wcd9xxx_res->domain; + + if (domain) + return domain->host_data; + else + return NULL; +} + +static int phyirq_to_virq(struct wcd9xxx_core_resource *wcd9xxx_res, int offset) +{ + struct wcd9xxx_irq_drv_data *data; + + data = wcd9xxx_get_irq_drv_d(wcd9xxx_res); + if (!data) { + pr_warn("%s: not registered to interrupt controller\n", + __func__); + return -EINVAL; + } + return irq_linear_revmap(data->domain, offset); +} + +static int virq_to_phyirq(struct wcd9xxx_core_resource *wcd9xxx_res, int virq) +{ + struct irq_data *irq_data = irq_get_irq_data(virq); + + if (unlikely(!irq_data)) { + pr_err("%s: irq_data is NULL", __func__); + return -EINVAL; + } + return irq_data->hwirq; +} + +static unsigned int wcd9xxx_irq_get_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + struct wcd9xxx_irq_drv_data *data; + + data = wcd9xxx_get_irq_drv_d(wcd9xxx_res); + if (!data) { + pr_err("%s: interrupt controller is not registered\n", + __func__); + return 0; + } + + /* Make sure data is updated before return. */ + rmb(); + return data->irq; +} + +static void wcd9xxx_irq_put_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + wcd9xxx_res->domain = NULL; +} + +static int wcd9xxx_map_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) +{ + return of_irq_to_resource(wcd9xxx_res->dev->of_node, irq, NULL); +} + +static int wcd9xxx_irq_probe(struct platform_device *pdev) +{ + int irq; + struct wcd9xxx_irq_drv_data *data; + struct device_node *node = pdev->dev.of_node; + int ret = -EINVAL; + + irq = of_get_named_gpio(node, "qcom,gpio-connect", 0); + if (!gpio_is_valid(irq)) { + dev_err(&pdev->dev, "TLMM connect gpio not found\n"); + return -EPROBE_DEFER; + } + irq = gpio_to_irq(irq); + if (irq < 0) { + dev_err(&pdev->dev, "Unable to configure irq\n"); + return irq; + } + dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq); + data = wcd9xxx_irq_add_domain(node, node->parent); + if (!data) { + pr_err("%s: irq_add_domain failed\n", __func__); + return -EINVAL; + } + data->irq = irq; + + /* Make sure irq is saved before return. */ + wmb(); + ret = 0; + + return ret; +} + +static int wcd9xxx_irq_remove(struct platform_device *pdev) +{ + struct irq_domain *domain; + struct wcd9xxx_irq_drv_data *data; + + domain = irq_find_host(pdev->dev.of_node); + if (unlikely(!domain)) { + pr_err("%s: domain is NULL", __func__); + return -EINVAL; + } + data = (struct wcd9xxx_irq_drv_data *)domain->host_data; + data->irq = 0; + + /* Make sure irq variable is updated in data, before irq removal. */ + wmb(); + irq_domain_remove(data->domain); + kfree(data); + domain->host_data = NULL; + + return 0; +} + +static const struct of_device_id of_match[] = { + { .compatible = "qcom,wcd9xxx-irq" }, + { } +}; + +static struct platform_driver wcd9xxx_irq_driver = { + .probe = wcd9xxx_irq_probe, + .remove = wcd9xxx_irq_remove, + .driver = { + .name = "wcd9xxx_intc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_match), + }, +}; + +static int wcd9xxx_irq_drv_init(void) +{ + return platform_driver_register(&wcd9xxx_irq_driver); +} +subsys_initcall(wcd9xxx_irq_drv_init); + +static void wcd9xxx_irq_drv_exit(void) +{ + platform_driver_unregister(&wcd9xxx_irq_driver); +} +module_exit(wcd9xxx_irq_drv_exit); +#endif /* CONFIG_OF */ diff --git a/drivers/mfd/wcd9xxx-regmap.h b/drivers/mfd/wcd9xxx-regmap.h new file mode 100644 index 0000000000000000000000000000000000000000..6db8fc55acae1b164da76de777b324dbee835d5e --- /dev/null +++ b/drivers/mfd/wcd9xxx-regmap.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _WCD9XXX_REGMAP_ +#define _WCD9XXX_REGMAP_ + +#include +#include + +typedef int (*regmap_patch_fptr)(struct regmap *, int); + +#ifdef CONFIG_WCD934X_CODEC +extern struct regmap_config wcd934x_regmap_config; +extern int wcd934x_regmap_register_patch(struct regmap *regmap, + int version); +#endif + +#ifdef CONFIG_WCD9335_CODEC +extern struct regmap_config wcd9335_regmap_config; +extern int wcd9335_regmap_register_patch(struct regmap *regmap, + int version); +#endif + +#ifdef CONFIG_WCD9330_CODEC +extern struct regmap_config wcd9330_regmap_config; +#endif + +static inline struct regmap_config *wcd9xxx_get_regmap_config(int type) +{ + struct regmap_config *regmap_config; + + switch (type) { +#ifdef CONFIG_WCD934X_CODEC + case WCD934X: + regmap_config = &wcd934x_regmap_config; + break; +#endif +#ifdef CONFIG_WCD9335_CODEC + case WCD9335: + regmap_config = &wcd9335_regmap_config; + break; +#endif +#ifdef CONFIG_WCD9330_CODEC + case WCD9330: + regmap_config = &wcd9330_regmap_config; + break; +#endif + default: + regmap_config = NULL; + break; + }; + + return regmap_config; +} + +static inline regmap_patch_fptr wcd9xxx_get_regmap_reg_patch(int type) +{ + regmap_patch_fptr apply_patch; + + switch (type) { +#ifdef CONFIG_WCD9335_CODEC + case WCD9335: + apply_patch = wcd9335_regmap_register_patch; + break; +#endif +#ifdef CONFIG_WCD934X_CODEC + case WCD934X: + apply_patch = wcd934x_regmap_register_patch; + break; +#endif + default: + apply_patch = NULL; + break; + } + + return apply_patch; +} + +#endif diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c new file mode 100644 index 0000000000000000000000000000000000000000..d3a926c2b0c1f5b76b855aa2a526eb23d3700a93 --- /dev/null +++ b/drivers/mfd/wcd9xxx-slimslave.c @@ -0,0 +1,564 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include + +struct wcd9xxx_slim_sch { + u16 rx_port_ch_reg_base; + u16 port_tx_cfg_reg_base; + u16 port_rx_cfg_reg_base; +}; + +static struct wcd9xxx_slim_sch sh_ch; + +static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx, + u8 wcd9xxx_pgd_la, u32 cnt, + struct wcd9xxx_ch *channels, u32 path); + +static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim, + u32 cnt, struct wcd9xxx_ch *channels); + +static int wcd9xxx_configure_ports(struct wcd9xxx *wcd9xxx) +{ + if (wcd9xxx->codec_type->slim_slave_type == + WCD9XXX_SLIM_SLAVE_ADDR_TYPE_0) { + sh_ch.rx_port_ch_reg_base = 0x180; + sh_ch.port_rx_cfg_reg_base = 0x040; + sh_ch.port_tx_cfg_reg_base = 0x040; + } else { + sh_ch.rx_port_ch_reg_base = + 0x180 - (TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4); + sh_ch.port_rx_cfg_reg_base = + 0x040 - TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS; + sh_ch.port_tx_cfg_reg_base = 0x050; + } + + return 0; +} + + +int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + int ret = 0; + int i; + + ret = wcd9xxx_configure_ports(wcd9xxx); + if (ret) { + pr_err("%s: Failed to configure register address offset\n", + __func__); + goto err; + } + + if (wcd9xxx->rx_chs) { + wcd9xxx->num_rx_port = rx_num; + for (i = 0; i < rx_num; i++) { + wcd9xxx->rx_chs[i].ch_num = rx_slot[i]; + INIT_LIST_HEAD(&wcd9xxx->rx_chs[i].list); + } + ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la, + wcd9xxx->num_rx_port, + wcd9xxx->rx_chs, + SLIM_SINK); + if (ret) { + pr_err("%s: Failed to alloc %d rx slimbus channels\n", + __func__, wcd9xxx->num_rx_port); + kfree(wcd9xxx->rx_chs); + wcd9xxx->rx_chs = NULL; + wcd9xxx->num_rx_port = 0; + } + } else { + pr_err("Not able to allocate memory for %d slimbus rx ports\n", + wcd9xxx->num_rx_port); + } + + if (wcd9xxx->tx_chs) { + wcd9xxx->num_tx_port = tx_num; + for (i = 0; i < tx_num; i++) { + wcd9xxx->tx_chs[i].ch_num = tx_slot[i]; + INIT_LIST_HEAD(&wcd9xxx->tx_chs[i].list); + } + ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la, + wcd9xxx->num_tx_port, + wcd9xxx->tx_chs, + SLIM_SRC); + if (ret) { + pr_err("%s: Failed to alloc %d tx slimbus channels\n", + __func__, wcd9xxx->num_tx_port); + kfree(wcd9xxx->tx_chs); + wcd9xxx->tx_chs = NULL; + wcd9xxx->num_tx_port = 0; + } + } else { + pr_err("Not able to allocate memory for %d slimbus tx ports\n", + wcd9xxx->num_tx_port); + } + return 0; +err: + return ret; +} + +int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx) +{ + if (wcd9xxx->num_rx_port) { + wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim, + wcd9xxx->num_rx_port, + wcd9xxx->rx_chs); + wcd9xxx->num_rx_port = 0; + } + if (wcd9xxx->num_tx_port) { + wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim, + wcd9xxx->num_tx_port, + wcd9xxx->tx_chs); + wcd9xxx->num_tx_port = 0; + } + return 0; +} + + +static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx, + u8 wcd9xxx_pgd_la, u32 cnt, + struct wcd9xxx_ch *channels, u32 path) +{ + int ret = 0; + u32 ch_idx; + + /* The slimbus channel allocation seem take longer time + * so do the allocation up front to avoid delay in start of + * playback + */ + pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la); + for (ch_idx = 0; ch_idx < cnt; ch_idx++) { + ret = slim_get_slaveport(wcd9xxx_pgd_la, + channels[ch_idx].port, + &channels[ch_idx].sph, path); + pr_debug("%s: pgd_la[%d] channels[%d].port[%d]\n" + "channels[%d].sph[%d] path[%d]\n", + __func__, wcd9xxx_pgd_la, ch_idx, + channels[ch_idx].port, + ch_idx, channels[ch_idx].sph, path); + if (ret < 0) { + pr_err("%s: slave port failure id[%d] ret[%d]\n", + __func__, channels[ch_idx].ch_num, ret); + goto err; + } + + ret = slim_query_ch(wcd9xxx->slim, + channels[ch_idx].ch_num, + &channels[ch_idx].ch_h); + if (ret < 0) { + pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n", + __func__, channels[ch_idx].ch_num, ret); + goto err; + } + } +err: + return ret; +} + +static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim, + u32 cnt, struct wcd9xxx_ch *channels) +{ + int idx = 0; + int ret = 0; + /* slim_dealloc_ch */ + for (idx = 0; idx < cnt; idx++) { + ret = slim_dealloc_ch(slim, channels[idx].ch_h); + if (ret < 0) { + pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n", + __func__, ret, channels[idx].ch_h); + } + } + return ret; +} + +/* Enable slimbus slave device for RX path */ +int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + unsigned int rate, unsigned int bit_width, + u16 *grph) +{ + u8 ch_cnt = 0; + u16 ch_h[SLIM_MAX_RX_PORTS] = {0}; + u8 payload = 0; + u16 codec_port = 0; + int ret; + struct slim_ch prop; + struct wcd9xxx_ch *rx; + int size = ARRAY_SIZE(ch_h); + + /* Configure slave interface device */ + + list_for_each_entry(rx, wcd9xxx_ch_list, list) { + payload |= 1 << rx->shift; + if (ch_cnt < size) { + ch_h[ch_cnt] = rx->ch_h; + ch_cnt++; + pr_debug("list ch->ch_h %d ch->sph %d\n", + rx->ch_h, rx->sph); + } else { + pr_err("%s: allocated channel number %u is out of max rangae %d\n", + __func__, ch_cnt, + size); + ret = EINVAL; + goto err; + } + } + pr_debug("%s: ch_cnt[%d] rate=%d WATER_MARK_VAL %d\n", + __func__, ch_cnt, rate, WATER_MARK_VAL); + /* slim_define_ch api */ + prop.prot = SLIM_AUTO_ISO; + if ((rate == 44100) || (rate == 88200) || (rate == 176400) || + (rate == 352800)) { + prop.baser = SLIM_RATE_11025HZ; + prop.ratem = (rate/11025); + } else { + prop.baser = SLIM_RATE_4000HZ; + prop.ratem = (rate/4000); + } + prop.dataf = SLIM_CH_DATAF_NOT_DEFINED; + prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE; + prop.sampleszbits = bit_width; + + pr_debug("Before slim_define_ch:\n" + "ch_cnt %d,ch_h[0] %d ch_h[1] %d, grph %d\n", + ch_cnt, ch_h[0], ch_h[1], *grph); + ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, + true, grph); + if (ret < 0) { + pr_err("%s: slim_define_ch failed ret[%d]\n", + __func__, ret); + goto err; + } + + list_for_each_entry(rx, wcd9xxx_ch_list, list) { + codec_port = rx->port; + pr_debug("%s: codec_port %d rx 0x%p, payload %d\n" + "sh_ch.rx_port_ch_reg_base0 0x%x\n" + "sh_ch.port_rx_cfg_reg_base 0x%x\n", + __func__, codec_port, rx, payload, + sh_ch.rx_port_ch_reg_base, + sh_ch.port_rx_cfg_reg_base); + + /* look for the valid port range and chose the + * payload accordingly + */ + /* write to interface device */ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_RX_PORT_MULTI_CHANNEL_0( + sh_ch.rx_port_ch_reg_base, codec_port), + payload); + + if (ret < 0) { + pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n", + __func__, + SB_PGD_RX_PORT_MULTI_CHANNEL_0( + sh_ch.rx_port_ch_reg_base, codec_port), + payload, ret); + goto err; + } + /* configure the slave port for water mark and enable*/ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_PORT_CFG_BYTE_ADDR( + sh_ch.port_rx_cfg_reg_base, codec_port), + WATER_MARK_VAL); + if (ret < 0) { + pr_err("%s:watermark set failure for port[%d] ret[%d]", + __func__, codec_port, ret); + } + + ret = slim_connect_sink(wcd9xxx->slim, &rx->sph, 1, rx->ch_h); + if (ret < 0) { + pr_err("%s: slim_connect_sink failed ret[%d]\n", + __func__, ret); + goto err_close_slim_sch; + } + } + /* slim_control_ch */ + ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE, + true); + if (ret < 0) { + pr_err("%s: slim_control_ch failed ret[%d]\n", + __func__, ret); + goto err_close_slim_sch; + } + return 0; + +err_close_slim_sch: + /* release all acquired handles */ + wcd9xxx_close_slim_sch_rx(wcd9xxx, wcd9xxx_ch_list, *grph); +err: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_cfg_slim_sch_rx); + +/* Enable slimbus slave device for RX path */ +int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + unsigned int rate, unsigned int bit_width, + u16 *grph) +{ + u16 ch_cnt = 0; + u16 payload = 0; + u16 ch_h[SLIM_MAX_TX_PORTS] = {0}; + u16 codec_port; + int ret = 0; + struct wcd9xxx_ch *tx; + int size = ARRAY_SIZE(ch_h); + + struct slim_ch prop; + + list_for_each_entry(tx, wcd9xxx_ch_list, list) { + payload |= 1 << tx->shift; + if (ch_cnt < size) { + ch_h[ch_cnt] = tx->ch_h; + ch_cnt++; + } else { + pr_err("%s: allocated channel number %u is out of max rangae %d\n", + __func__, ch_cnt, + size); + ret = EINVAL; + goto err; + } + } + + /* slim_define_ch api */ + prop.prot = SLIM_AUTO_ISO; + prop.baser = SLIM_RATE_4000HZ; + prop.dataf = SLIM_CH_DATAF_NOT_DEFINED; + prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE; + prop.ratem = (rate/4000); + prop.sampleszbits = bit_width; + ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, + true, grph); + if (ret < 0) { + pr_err("%s: slim_define_ch failed ret[%d]\n", + __func__, ret); + goto err; + } + + pr_debug("%s: ch_cnt[%d] rate[%d] bitwidth[%u]\n", __func__, ch_cnt, + rate, bit_width); + list_for_each_entry(tx, wcd9xxx_ch_list, list) { + codec_port = tx->port; + pr_debug("%s: codec_port %d tx 0x%p, payload 0x%x\n", + __func__, codec_port, tx, payload); + /* write to interface device */ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port), + payload & 0x00FF); + if (ret < 0) { + pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n", + __func__, + SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port), + payload, ret); + goto err; + } + /* ports 8,9 */ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port), + (payload & 0xFF00)>>8); + if (ret < 0) { + pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n", + __func__, + SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port), + payload, ret); + goto err; + } + /* configure the slave port for water mark and enable*/ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_PORT_CFG_BYTE_ADDR( + sh_ch.port_tx_cfg_reg_base, codec_port), + WATER_MARK_VAL); + if (ret < 0) { + pr_err("%s:watermark set failure for port[%d] ret[%d]", + __func__, codec_port, ret); + } + + ret = slim_connect_src(wcd9xxx->slim, tx->sph, tx->ch_h); + + if (ret < 0) { + pr_err("%s: slim_connect_src failed ret[%d]\n", + __func__, ret); + goto err; + } + } + /* slim_control_ch */ + ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE, + true); + if (ret < 0) { + pr_err("%s: slim_control_ch failed ret[%d]\n", + __func__, ret); + goto err; + } + return 0; +err: + /* release all acquired handles */ + wcd9xxx_close_slim_sch_tx(wcd9xxx, wcd9xxx_ch_list, *grph); + return ret; +} +EXPORT_SYMBOL(wcd9xxx_cfg_slim_sch_tx); + +int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph) +{ + u32 sph[SLIM_MAX_RX_PORTS] = {0}; + int ch_cnt = 0; + int ret = 0; + struct wcd9xxx_ch *rx; + + list_for_each_entry(rx, wcd9xxx_ch_list, list) + sph[ch_cnt++] = rx->sph; + + pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", __func__, ch_cnt, + sph[0], sph[1]); + + /* slim_control_ch (REMOVE) */ + pr_debug("%s before slim_control_ch grph %d\n", __func__, grph); + ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true); + if (ret < 0) { + pr_err("%s: slim_control_ch failed ret[%d]\n", __func__, ret); + goto err; + } +err: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_close_slim_sch_rx); + +int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + u16 grph) +{ + u32 sph[SLIM_MAX_TX_PORTS] = {0}; + int ret = 0; + int ch_cnt = 0; + struct wcd9xxx_ch *tx; + + pr_debug("%s\n", __func__); + list_for_each_entry(tx, wcd9xxx_ch_list, list) + sph[ch_cnt++] = tx->sph; + + pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", + __func__, ch_cnt, sph[0], sph[1]); + /* slim_control_ch (REMOVE) */ + ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true); + if (ret < 0) { + pr_err("%s: slim_control_ch failed ret[%d]\n", + __func__, ret); + goto err; + } +err: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_close_slim_sch_tx); + +int wcd9xxx_get_slave_port(unsigned int ch_num) +{ + int ret = 0; + + ret = (ch_num - BASE_CH_NUM); + pr_debug("%s: ch_num[%d] slave port[%d]\n", __func__, ch_num, ret); + if (ret < 0) { + pr_err("%s: Error:- Invalid slave port found = %d\n", + __func__, ret); + return -EINVAL; + } + return ret; +} +EXPORT_SYMBOL(wcd9xxx_get_slave_port); + +int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph) +{ + u32 sph[SLIM_MAX_TX_PORTS + SLIM_MAX_RX_PORTS] = {0}; + int ch_cnt = 0; + int ret = 0; + struct wcd9xxx_ch *slim_ch; + + list_for_each_entry(slim_ch, wcd9xxx_ch_list, list) + sph[ch_cnt++] = slim_ch->sph; + + /* slim_disconnect_port */ + ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt); + if (ret < 0) { + pr_err("%s: slim_disconnect_ports failed ret[%d]\n", + __func__, ret); + } + return ret; +} +EXPORT_SYMBOL(wcd9xxx_disconnect_port); + +/* This function is called with mutex acquired */ +int wcd9xxx_rx_vport_validation(u32 port_id, + struct list_head *codec_dai_list) +{ + struct wcd9xxx_ch *ch; + int ret = 0; + + pr_debug("%s: port_id %u\n", __func__, port_id); + + list_for_each_entry(ch, + codec_dai_list, list) { + pr_debug("%s: ch->port %u\n", __func__, ch->port); + if (ch->port == port_id) { + ret = -EINVAL; + break; + } + } + return ret; +} +EXPORT_SYMBOL(wcd9xxx_rx_vport_validation); + + +/* This function is called with mutex acquired */ +int wcd9xxx_tx_vport_validation(u32 table, u32 port_id, + struct wcd9xxx_codec_dai_data *codec_dai, + u32 num_codec_dais) +{ + struct wcd9xxx_ch *ch; + int ret = 0; + u32 index; + unsigned long vtable = table; + u32 size = sizeof(table) * BITS_PER_BYTE; + + pr_debug("%s: vtable 0x%lx port_id %u size %d\n", __func__, + vtable, port_id, size); + for_each_set_bit(index, &vtable, size) { + if (index < num_codec_dais) { + list_for_each_entry(ch, + &codec_dai[index].wcd9xxx_ch_list, + list) { + pr_debug("%s: index %u ch->port %u vtable 0x%lx\n", + __func__, index, ch->port, + vtable); + if (ch->port == port_id) { + pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n", + __func__, port_id + 1, + (index + 1)/2); + ret = -EINVAL; + break; + } + } + } else { + pr_err("%s: Invalid index %d of codec dai", + __func__, index); + ret = -EINVAL; + } + if (ret) + break; + } + return ret; +} +EXPORT_SYMBOL(wcd9xxx_tx_vport_validation); diff --git a/drivers/mfd/wcd9xxx-utils.c b/drivers/mfd/wcd9xxx-utils.c new file mode 100644 index 0000000000000000000000000000000000000000..6062fb8fdcdea37c49ae67a6d5381e251a0d4fec --- /dev/null +++ b/drivers/mfd/wcd9xxx-utils.c @@ -0,0 +1,1198 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_BYTES 2 +#define VAL_BYTES 1 +/* + * Page Register Address that APP Proc uses to + * access WCD9335 Codec registers is identified + * as 0x00 + */ +#define PAGE_REG_ADDR 0x00 + +static enum wcd9xxx_intf_status wcd9xxx_intf = -1; + +static struct mfd_cell tavil_devs[] = { + { + .name = "qcom-wcd-pinctrl", + .of_compatible = "qcom,wcd-pinctrl", + }, + { + .name = "tavil_codec", + }, +}; + +static struct mfd_cell tasha_devs[] = { + { + .name = "tasha_codec", + }, +}; + +static struct mfd_cell tomtom_devs[] = { + { + .name = "tomtom_codec", + }, +}; + +static int wcd9xxx_read_of_property_u32(struct device *dev, const char *name, + u32 *val) +{ + int rc = 0; + + rc = of_property_read_u32(dev->of_node, name, val); + if (rc) + dev_err(dev, "%s: Looking up %s property in node %s failed", + __func__, name, dev->of_node->full_name); + + return rc; +} + +static void wcd9xxx_dt_parse_micbias_info(struct device *dev, + struct wcd9xxx_micbias_setting *mb) +{ + u32 prop_val; + int rc; + + if (of_find_property(dev->of_node, "qcom,cdc-micbias-ldoh-v", NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias-ldoh-v", + &prop_val); + if (!rc) + mb->ldoh_v = (u8)prop_val; + } + + /* MB1 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias-cfilt1-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias-cfilt1-mv", + &prop_val); + if (!rc) + mb->cfilt1_mv = prop_val; + + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias1-cfilt-sel", + &prop_val); + if (!rc) + mb->bias1_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias1-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias1-mv", + &prop_val); + if (!rc) + mb->micb1_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias1 DT property not found\n", + __func__); + } + + /* MB2 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias-cfilt2-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias-cfilt2-mv", + &prop_val); + if (!rc) + mb->cfilt2_mv = prop_val; + + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias2-cfilt-sel", + &prop_val); + if (!rc) + mb->bias2_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias2-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias2-mv", + &prop_val); + if (!rc) + mb->micb2_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias2 DT property not found\n", + __func__); + } + + /* MB3 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias-cfilt3-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias-cfilt3-mv", + &prop_val); + if (!rc) + mb->cfilt3_mv = prop_val; + + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias3-cfilt-sel", + &prop_val); + if (!rc) + mb->bias3_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias3-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias3-mv", + &prop_val); + if (!rc) + mb->micb3_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias3 DT property not found\n", + __func__); + } + + /* MB4 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias4-cfilt-sel", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias4-cfilt-sel", + &prop_val); + if (!rc) + mb->bias4_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias4-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias4-mv", + &prop_val); + if (!rc) + mb->micb4_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias4 DT property not found\n", + __func__); + } + + mb->bias1_cap_mode = + (of_property_read_bool(dev->of_node, "qcom,cdc-micbias1-ext-cap") ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + mb->bias2_cap_mode = + (of_property_read_bool(dev->of_node, "qcom,cdc-micbias2-ext-cap") ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + mb->bias3_cap_mode = + (of_property_read_bool(dev->of_node, "qcom,cdc-micbias3-ext-cap") ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + mb->bias4_cap_mode = + (of_property_read_bool(dev->of_node, "qcom,cdc-micbias4-ext-cap") ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + + mb->bias2_is_headset_only = + of_property_read_bool(dev->of_node, + "qcom,cdc-micbias2-headset-only"); + + /* Print micbias info */ + dev_dbg(dev, "%s: ldoh_v %u cfilt1_mv %u cfilt2_mv %u cfilt3_mv %u", + __func__, (u32)mb->ldoh_v, (u32)mb->cfilt1_mv, + (u32)mb->cfilt2_mv, (u32)mb->cfilt3_mv); + + dev_dbg(dev, "%s: micb1_mv %u micb2_mv %u micb3_mv %u micb4_mv %u", + __func__, mb->micb1_mv, mb->micb2_mv, + mb->micb3_mv, mb->micb4_mv); + + dev_dbg(dev, "%s: bias1_cfilt_sel %u bias2_cfilt_sel %u\n", + __func__, (u32)mb->bias1_cfilt_sel, (u32)mb->bias2_cfilt_sel); + + dev_dbg(dev, "%s: bias3_cfilt_sel %u bias4_cfilt_sel %u\n", + __func__, (u32)mb->bias3_cfilt_sel, (u32)mb->bias4_cfilt_sel); + + dev_dbg(dev, "%s: bias1_ext_cap %d bias2_ext_cap %d\n", + __func__, mb->bias1_cap_mode, mb->bias2_cap_mode); + + dev_dbg(dev, "%s: bias3_ext_cap %d bias4_ext_cap %d\n", + __func__, mb->bias3_cap_mode, mb->bias4_cap_mode); + + dev_dbg(dev, "%s: bias2_is_headset_only %d\n", + __func__, mb->bias2_is_headset_only); +} + +/* + * wcd9xxx_validate_dmic_sample_rate: + * Given the dmic_sample_rate and mclk rate, validate the + * dmic_sample_rate. If dmic rate is found to be invalid, + * assign the dmic rate as undefined, so individual codec + * drivers can use their own defaults + * @dev: the device for which the dmic is to be configured + * @dmic_sample_rate: The input dmic_sample_rate + * @mclk_rate: The input codec mclk rate + * @dmic_rate_type: String to indicate the type of dmic sample + * rate, used for debug/error logging. + */ +static u32 wcd9xxx_validate_dmic_sample_rate(struct device *dev, + u32 dmic_sample_rate, u32 mclk_rate, + const char *dmic_rate_type) +{ + u32 div_factor; + + if (dmic_sample_rate == WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED || + mclk_rate % dmic_sample_rate != 0) + goto undefined_rate; + + div_factor = mclk_rate / dmic_sample_rate; + + switch (div_factor) { + case 2: + case 3: + case 4: + case 8: + case 16: + /* Valid dmic DIV factors */ + dev_dbg(dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n", + __func__, div_factor, mclk_rate); + break; + case 6: + /* + * DIV 6 is valid for both 9.6MHz and 12.288MHz + * MCLK on Tavil. Older codecs support DIV6 only + * for 12.288MHz MCLK. + */ + if ((mclk_rate == WCD9XXX_MCLK_CLK_9P6HZ) && + (of_device_is_compatible(dev->of_node, + "qcom,tavil-slim-pgd"))) + dev_dbg(dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n", + __func__, div_factor, mclk_rate); + else if (mclk_rate != WCD9XXX_MCLK_CLK_12P288MHZ) + goto undefined_rate; + break; + default: + /* Any other DIV factor is invalid */ + goto undefined_rate; + } + + return dmic_sample_rate; + +undefined_rate: + dev_info(dev, "%s: Invalid %s = %d, for mclk %d\n", + __func__, dmic_rate_type, dmic_sample_rate, mclk_rate); + dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED; + + return dmic_sample_rate; +} + +/* + * wcd9xxx_populate_dt_data: + * Parse device tree properties for the given codec device + * + * @dev: pointer to codec device + * + * Returns pointer to the platform data resulting from parsing + * device tree. + */ +struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev) +{ + struct wcd9xxx_pdata *pdata; + u32 dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED; + u32 mad_dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED; + u32 ecpp_dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED; + u32 dmic_clk_drive = WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED; + u32 prop_val; + int rc = 0; + + if (!dev || !dev->of_node) + return NULL; + + pdata = devm_kzalloc(dev, sizeof(struct wcd9xxx_pdata), + GFP_KERNEL); + if (!pdata) + return NULL; + + /* Parse power supplies */ + msm_cdc_get_power_supplies(dev, &pdata->regulator, + &pdata->num_supplies); + if (!pdata->regulator || (pdata->num_supplies <= 0)) { + dev_err(dev, "%s: no power supplies defined for codec\n", + __func__); + goto err_power_sup; + } + + /* Parse micbias info */ + wcd9xxx_dt_parse_micbias_info(dev, &pdata->micbias); + + pdata->wcd_rst_np = of_parse_phandle(dev->of_node, + "qcom,wcd-rst-gpio-node", 0); + if (!pdata->wcd_rst_np) { + dev_err(dev, "%s: Looking up %s property in node %s failed\n", + __func__, "qcom,wcd-rst-gpio-node", + dev->of_node->full_name); + goto err_parse_dt_prop; + } + + if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-mclk-clk-rate", + &prop_val))) + pdata->mclk_rate = prop_val; + + if (pdata->mclk_rate != WCD9XXX_MCLK_CLK_9P6HZ && + pdata->mclk_rate != WCD9XXX_MCLK_CLK_12P288MHZ) { + dev_err(dev, "%s: Invalid mclk_rate = %u\n", __func__, + pdata->mclk_rate); + goto err_parse_dt_prop; + } + + if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-dmic-sample-rate", + &prop_val))) + dmic_sample_rate = prop_val; + + pdata->dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev, + dmic_sample_rate, + pdata->mclk_rate, + "audio_dmic_rate"); + if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-mad-dmic-rate", + &prop_val))) + mad_dmic_sample_rate = prop_val; + + pdata->mad_dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev, + mad_dmic_sample_rate, + pdata->mclk_rate, + "mad_dmic_rate"); + + if (of_find_property(dev->of_node, "qcom,cdc-ecpp-dmic-rate", NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-ecpp-dmic-rate", + &prop_val); + if (!rc) + ecpp_dmic_sample_rate = prop_val; + } + + pdata->ecpp_dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev, + ecpp_dmic_sample_rate, + pdata->mclk_rate, + "ecpp_dmic_rate"); + + if (!(of_property_read_u32(dev->of_node, + "qcom,cdc-dmic-clk-drv-strength", + &prop_val))) { + dmic_clk_drive = prop_val; + + if (dmic_clk_drive != 2 && dmic_clk_drive != 4 && + dmic_clk_drive != 8 && dmic_clk_drive != 16) + dev_err(dev, "Invalid cdc-dmic-clk-drv-strength %d\n", + dmic_clk_drive); + } + + pdata->dmic_clk_drv = dmic_clk_drive; + + return pdata; + +err_parse_dt_prop: + devm_kfree(dev, pdata->regulator); + pdata->regulator = NULL; + pdata->num_supplies = 0; +err_power_sup: + devm_kfree(dev, pdata); + return NULL; +} +EXPORT_SYMBOL(wcd9xxx_populate_dt_data); + +static bool is_wcd9xxx_reg_power_down(struct wcd9xxx *wcd9xxx, u16 rreg) +{ + bool ret = false; + int i; + struct wcd9xxx_power_region *wcd9xxx_pwr; + + if (!wcd9xxx) + return ret; + + for (i = 0; i < WCD9XXX_MAX_PWR_REGIONS; i++) { + wcd9xxx_pwr = wcd9xxx->wcd9xxx_pwr[i]; + if (!wcd9xxx_pwr) + continue; + if (((wcd9xxx_pwr->pwr_collapse_reg_min == 0) && + (wcd9xxx_pwr->pwr_collapse_reg_max == 0)) || + (wcd9xxx_pwr->power_state == + WCD_REGION_POWER_COLLAPSE_REMOVE)) + ret = false; + else if (((wcd9xxx_pwr->power_state == + WCD_REGION_POWER_DOWN) || + (wcd9xxx_pwr->power_state == + WCD_REGION_POWER_COLLAPSE_BEGIN)) && + (rreg >= wcd9xxx_pwr->pwr_collapse_reg_min) && + (rreg <= wcd9xxx_pwr->pwr_collapse_reg_max)) + ret = true; + } + return ret; +} + +/* + * wcd9xxx_page_write: + * Retrieve page number from register and + * write that page number to the page address. + * Called under io_lock acquisition. + * + * @wcd9xxx: pointer to wcd9xxx + * @reg: Register address from which page number is retrieved + * + * Returns 0 for success and negative error code for failure. + */ +int wcd9xxx_page_write(struct wcd9xxx *wcd9xxx, unsigned short *reg) +{ + int ret = 0; + unsigned short c_reg, reg_addr; + u8 pg_num, prev_pg_num; + + if (wcd9xxx->type != WCD9335 && wcd9xxx->type != WCD934X) + return ret; + + c_reg = *reg; + pg_num = c_reg >> 8; + reg_addr = c_reg & 0xff; + if (wcd9xxx->prev_pg_valid) { + prev_pg_num = wcd9xxx->prev_pg; + if (prev_pg_num != pg_num) { + ret = wcd9xxx->write_dev( + wcd9xxx, PAGE_REG_ADDR, 1, + (void *) &pg_num, false); + if (ret < 0) + pr_err("page write error, pg_num: 0x%x\n", + pg_num); + else { + wcd9xxx->prev_pg = pg_num; + dev_dbg(wcd9xxx->dev, "%s: Page 0x%x Write to 0x00\n", + __func__, pg_num); + } + } + } else { + ret = wcd9xxx->write_dev( + wcd9xxx, PAGE_REG_ADDR, 1, (void *) &pg_num, + false); + if (ret < 0) + pr_err("page write error, pg_num: 0x%x\n", pg_num); + else { + wcd9xxx->prev_pg = pg_num; + wcd9xxx->prev_pg_valid = true; + dev_dbg(wcd9xxx->dev, "%s: Page 0x%x Write to 0x00\n", + __func__, pg_num); + } + } + *reg = reg_addr; + return ret; +} +EXPORT_SYMBOL(wcd9xxx_page_write); + +static int regmap_bus_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + unsigned short c_reg, rreg; + int ret, i; + + if (!wcd9xxx) { + dev_err(dev, "%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return -EINVAL; + } + + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return -EINVAL; + } + + mutex_lock(&wcd9xxx->io_lock); + c_reg = *(u16 *)reg; + rreg = c_reg; + + if (is_wcd9xxx_reg_power_down(wcd9xxx, rreg)) { + ret = 0; + for (i = 0; i < val_size; i++) + ((u8 *)val)[i] = 0; + goto err; + } + ret = wcd9xxx_page_write(wcd9xxx, &c_reg); + if (ret) + goto err; + ret = wcd9xxx->read_dev(wcd9xxx, c_reg, val_size, val, false); + if (ret < 0) + dev_err(dev, "%s: Codec read failed (%d), reg: 0x%x, size:%zd\n", + __func__, ret, rreg, val_size); + else { + for (i = 0; i < val_size; i++) + dev_dbg(dev, "%s: Read 0x%02x from 0x%x\n", + __func__, ((u8 *)val)[i], rreg + i); + } +err: + mutex_unlock(&wcd9xxx->io_lock); + + return ret; +} + +static int regmap_bus_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct device *dev = context; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + unsigned short c_reg, rreg; + int ret, i; + + if (!wcd9xxx) { + dev_err(dev, "%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return -EINVAL; + } + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return -EINVAL; + } + mutex_lock(&wcd9xxx->io_lock); + c_reg = *(u16 *)reg; + rreg = c_reg; + + if (is_wcd9xxx_reg_power_down(wcd9xxx, rreg)) { + ret = 0; + goto err; + } + ret = wcd9xxx_page_write(wcd9xxx, &c_reg); + if (ret) + goto err; + + for (i = 0; i < val_size; i++) + dev_dbg(dev, "Write %02x to 0x%x\n", ((u8 *)val)[i], + rreg + i); + + ret = wcd9xxx->write_dev(wcd9xxx, c_reg, val_size, (void *) val, + false); + if (ret < 0) + dev_err(dev, "%s: Codec write failed (%d), reg:0x%x, size:%zd\n", + __func__, ret, rreg, val_size); + +err: + mutex_unlock(&wcd9xxx->io_lock); + return ret; +} + +static int regmap_bus_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + + if (!wcd9xxx) + return -EINVAL; + + WARN_ON(count < REG_BYTES); + + if (count > (REG_BYTES + VAL_BYTES)) { + if (wcd9xxx->multi_reg_write) + return wcd9xxx->multi_reg_write(wcd9xxx, + data, count); + } else + return regmap_bus_gather_write(context, data, REG_BYTES, + data + REG_BYTES, + count - REG_BYTES); + + dev_err(dev, "%s: bus multi reg write failure\n", __func__); + + return -EINVAL; +} + +static struct regmap_bus regmap_bus_config = { + .write = regmap_bus_write, + .gather_write = regmap_bus_gather_write, + .read = regmap_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +/* + * wcd9xxx_regmap_init: + * Initialize wcd9xxx register map + * + * @dev: pointer to wcd device + * @config: pointer to register map config + * + * Returns pointer to regmap structure for success + * or NULL in case of failure. + */ +struct regmap *wcd9xxx_regmap_init(struct device *dev, + const struct regmap_config *config) +{ + return devm_regmap_init(dev, ®map_bus_config, dev, config); +} +EXPORT_SYMBOL(wcd9xxx_regmap_init); + +/* + * wcd9xxx_reset: + * Reset wcd9xxx codec + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_reset(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + int value; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->wcd_rst_np) { + dev_err(dev, "%s: reset gpio device node not specified\n", + __func__); + return -EINVAL; + } + + value = msm_cdc_get_gpio_state(wcd9xxx->wcd_rst_np); + if (value > 0) { + wcd9xxx->avoid_cdc_rstlow = 1; + return 0; + } + + rc = msm_cdc_pinctrl_select_sleep_state(wcd9xxx->wcd_rst_np); + if (rc) { + dev_err(dev, "%s: wcd sleep state request fail!\n", + __func__); + return rc; + } + + /* 20ms sleep required after pulling the reset gpio to LOW */ + msleep(20); + + rc = msm_cdc_pinctrl_select_active_state(wcd9xxx->wcd_rst_np); + if (rc) { + dev_err(dev, "%s: wcd active state request fail!\n", + __func__); + return rc; + } + msleep(20); + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_reset); + +/* + * wcd9xxx_reset_low: + * Pull the wcd9xxx codec reset_n to low + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_reset_low(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->wcd_rst_np) { + dev_err(dev, "%s: reset gpio device node not specified\n", + __func__); + return -EINVAL; + } + if (wcd9xxx->avoid_cdc_rstlow) { + wcd9xxx->avoid_cdc_rstlow = 0; + dev_dbg(dev, "%s: avoid pull down of reset GPIO\n", __func__); + return 0; + } + + rc = msm_cdc_pinctrl_select_sleep_state(wcd9xxx->wcd_rst_np); + if (rc) + dev_err(dev, "%s: wcd sleep state request fail!\n", + __func__); + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_reset_low); + +/* + * wcd9xxx_bringup: + * Toggle reset analog and digital cores of wcd9xxx codec + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_bringup(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + codec_bringup_fn cdc_bup_fn; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + cdc_bup_fn = wcd9xxx_bringup_fn(wcd9xxx->type); + if (!cdc_bup_fn) { + dev_err(dev, "%s: Codec bringup fn NULL!\n", + __func__); + return -EINVAL; + } + rc = cdc_bup_fn(wcd9xxx); + if (rc) + dev_err(dev, "%s: Codec bringup error, rc: %d\n", + __func__, rc); + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_bringup); + +/* + * wcd9xxx_bringup: + * Set analog and digital cores of wcd9xxx codec in reset state + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_bringdown(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + codec_bringdown_fn cdc_bdown_fn; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + cdc_bdown_fn = wcd9xxx_bringdown_fn(wcd9xxx->type); + if (!cdc_bdown_fn) { + dev_err(dev, "%s: Codec bring down fn NULL!\n", + __func__); + return -EINVAL; + } + rc = cdc_bdown_fn(wcd9xxx); + if (rc) + dev_err(dev, "%s: Codec bring down error, rc: %d\n", + __func__, rc); + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_bringdown); + +/* + * wcd9xxx_get_codec_info: + * Fill codec specific information like interrupts, version + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_get_codec_info(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + codec_type_fn cdc_type_fn; + struct wcd9xxx_codec_type *cinfo; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + cdc_type_fn = wcd9xxx_get_codec_info_fn(wcd9xxx->type); + if (!cdc_type_fn) { + dev_err(dev, "%s: Codec fill type fn NULL!\n", + __func__); + return -EINVAL; + } + + cinfo = wcd9xxx->codec_type; + if (!cinfo) + return -EINVAL; + + rc = cdc_type_fn(wcd9xxx, cinfo); + if (rc) { + dev_err(dev, "%s: Codec type fill failed, rc:%d\n", + __func__, rc); + return rc; + + } + + switch (wcd9xxx->type) { + case WCD934X: + cinfo->dev = tavil_devs; + cinfo->size = ARRAY_SIZE(tavil_devs); + break; + case WCD9335: + cinfo->dev = tasha_devs; + cinfo->size = ARRAY_SIZE(tasha_devs); + break; + case WCD9330: + cinfo->dev = tomtom_devs; + cinfo->size = ARRAY_SIZE(tomtom_devs); + break; + default: + cinfo->dev = NULL; + cinfo->size = 0; + break; + } + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_get_codec_info); + +/* + * wcd9xxx_core_irq_init: + * Initialize wcd9xxx codec irq instance + * + * @wcd9xxx_core_res: pointer to wcd core resource + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_core_irq_init( + struct wcd9xxx_core_resource *wcd9xxx_core_res) +{ + int ret = 0; + + if (!wcd9xxx_core_res) + return -EINVAL; + + if (wcd9xxx_core_res->irq != 1) { + ret = wcd9xxx_irq_init(wcd9xxx_core_res); + if (ret) + pr_err("IRQ initialization failed\n"); + } + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_core_irq_init); + +/* + * wcd9xxx_assign_irq: + * Assign irq and irq_base to wcd9xxx core resource + * + * @wcd9xxx_core_res: pointer to wcd core resource + * @irq: irq number + * @irq_base: base irq number + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_assign_irq( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + unsigned int irq, + unsigned int irq_base) +{ + if (!wcd9xxx_core_res) + return -EINVAL; + + wcd9xxx_core_res->irq = irq; + wcd9xxx_core_res->irq_base = irq_base; + + return 0; +} +EXPORT_SYMBOL(wcd9xxx_assign_irq); + +/* + * wcd9xxx_core_res_init: + * Initialize wcd core resource instance + * + * @wcd9xxx_core_res: pointer to wcd core resource + * @num_irqs: number of irqs for wcd9xxx core + * @num_irq_regs: number of irq registers + * @wcd_regmap: pointer to the wcd register map + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_core_res_init( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + int num_irqs, int num_irq_regs, struct regmap *wcd_regmap) +{ + if (!wcd9xxx_core_res || !wcd_regmap) + return -EINVAL; + + mutex_init(&wcd9xxx_core_res->pm_lock); + wcd9xxx_core_res->wlock_holders = 0; + wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE; + init_waitqueue_head(&wcd9xxx_core_res->pm_wq); + pm_qos_add_request(&wcd9xxx_core_res->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + wcd9xxx_core_res->num_irqs = num_irqs; + wcd9xxx_core_res->num_irq_regs = num_irq_regs; + wcd9xxx_core_res->wcd_core_regmap = wcd_regmap; + + pr_info("%s: num_irqs = %d, num_irq_regs = %d\n", + __func__, wcd9xxx_core_res->num_irqs, + wcd9xxx_core_res->num_irq_regs); + + return 0; +} +EXPORT_SYMBOL(wcd9xxx_core_res_init); + +/* + * wcd9xxx_core_res_deinit: + * Deinit wcd core resource instance + * + * @wcd9xxx_core_res: pointer to wcd core resource + */ +void wcd9xxx_core_res_deinit(struct wcd9xxx_core_resource *wcd9xxx_core_res) +{ + if (!wcd9xxx_core_res) + return; + + pm_qos_remove_request(&wcd9xxx_core_res->pm_qos_req); + mutex_destroy(&wcd9xxx_core_res->pm_lock); +} +EXPORT_SYMBOL(wcd9xxx_core_res_deinit); + +/* + * wcd9xxx_pm_cmpxchg: + * Check old state and exchange with pm new state + * if old state matches with current state + * + * @wcd9xxx_core_res: pointer to wcd core resource + * @o: pm old state + * @n: pm new state + * + * Returns old state + */ +enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + enum wcd9xxx_pm_state o, + enum wcd9xxx_pm_state n) +{ + enum wcd9xxx_pm_state old; + + if (!wcd9xxx_core_res) + return o; + + mutex_lock(&wcd9xxx_core_res->pm_lock); + old = wcd9xxx_core_res->pm_state; + if (old == o) + wcd9xxx_core_res->pm_state = n; + mutex_unlock(&wcd9xxx_core_res->pm_lock); + + return old; +} +EXPORT_SYMBOL(wcd9xxx_pm_cmpxchg); + +/* + * wcd9xxx_core_res_suspend: + * Suspend callback function for wcd9xxx core + * + * @wcd9xxx_core_res: pointer to wcd core resource + * @pm_message_t: pm message + * + * Returns 0 for success or negative error code for failure/busy + */ +int wcd9xxx_core_res_suspend( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + pm_message_t pmesg) +{ + int ret = 0; + + pr_debug("%s: enter\n", __func__); + /* + * pm_qos_update_request() can be called after this suspend chain call + * started. thus suspend can be called while lock is being held + */ + mutex_lock(&wcd9xxx_core_res->pm_lock); + if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_SLEEPABLE) { + pr_debug("%s: suspending system, state %d, wlock %d\n", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + wcd9xxx_core_res->pm_state = WCD9XXX_PM_ASLEEP; + } else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_AWAKE) { + /* + * unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE + * then set to WCD9XXX_PM_ASLEEP + */ + pr_debug("%s: waiting to suspend system, state %d, wlock %d\n", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + mutex_unlock(&wcd9xxx_core_res->pm_lock); + if (!(wait_event_timeout(wcd9xxx_core_res->pm_wq, + wcd9xxx_pm_cmpxchg(wcd9xxx_core_res, + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_ASLEEP) == + WCD9XXX_PM_SLEEPABLE, + HZ))) { + pr_debug("%s: suspend failed state %d, wlock %d\n", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + ret = -EBUSY; + } else { + pr_debug("%s: done, state %d, wlock %d\n", __func__, + wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + } + mutex_lock(&wcd9xxx_core_res->pm_lock); + } else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) { + pr_warn("%s: system is already suspended, state %d, wlock %dn", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + } + mutex_unlock(&wcd9xxx_core_res->pm_lock); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_core_res_suspend); + +/* + * wcd9xxx_core_res_resume: + * Resume callback function for wcd9xxx core + * + * @wcd9xxx_core_res: pointer to wcd core resource + * + * Returns 0 for success or negative error code for failure/busy + */ +int wcd9xxx_core_res_resume( + struct wcd9xxx_core_resource *wcd9xxx_core_res) +{ + int ret = 0; + + pr_debug("%s: enter\n", __func__); + mutex_lock(&wcd9xxx_core_res->pm_lock); + if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) { + pr_debug("%s: resuming system, state %d, wlock %d\n", __func__, + wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE; + } else { + pr_warn("%s: system is already awake, state %d wlock %d\n", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + } + mutex_unlock(&wcd9xxx_core_res->pm_lock); + wake_up_all(&wcd9xxx_core_res->pm_wq); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_core_res_resume); + +/* + * wcd9xxx_get_intf_type: + * Get interface type of wcd9xxx core + * + * Returns interface type + */ +enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void) +{ + return wcd9xxx_intf; +} +EXPORT_SYMBOL(wcd9xxx_get_intf_type); + +/* + * wcd9xxx_set_intf_type: + * Set interface type of wcd9xxx core + * + */ +void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status intf_status) +{ + wcd9xxx_intf = intf_status; +} +EXPORT_SYMBOL(wcd9xxx_set_intf_type); + +/* + * wcd9xxx_set_power_state: set power state for the region + * @wcd9xxx: handle to wcd core + * @state: power state to be set + * @region: region index + * + * Returns error code in case of failure or 0 for success + */ +int wcd9xxx_set_power_state(struct wcd9xxx *wcd9xxx, + enum codec_power_states state, + enum wcd_power_regions region) +{ + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + + if ((region < 0) || (region >= WCD9XXX_MAX_PWR_REGIONS)) { + dev_err(wcd9xxx->dev, "%s: region index %d out of bounds\n", + __func__, region); + return -EINVAL; + } + if (!wcd9xxx->wcd9xxx_pwr[region]) { + dev_err(wcd9xxx->dev, "%s: memory not created for region: %d\n", + __func__, region); + return -EINVAL; + } + mutex_lock(&wcd9xxx->io_lock); + wcd9xxx->wcd9xxx_pwr[region]->power_state = state; + mutex_unlock(&wcd9xxx->io_lock); + + return 0; +} +EXPORT_SYMBOL(wcd9xxx_set_power_state); + +/* + * wcd9xxx_get_current_power_state: Get power state of the region + * @wcd9xxx: handle to wcd core + * @region: region index + * + * Returns current power state of the region or error code for failure + */ +int wcd9xxx_get_current_power_state(struct wcd9xxx *wcd9xxx, + enum wcd_power_regions region) +{ + int state; + + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + + if ((region < 0) || (region >= WCD9XXX_MAX_PWR_REGIONS)) { + dev_err(wcd9xxx->dev, "%s: region index %d out of bounds\n", + __func__, region); + return -EINVAL; + } + if (!wcd9xxx->wcd9xxx_pwr[region]) { + dev_err(wcd9xxx->dev, "%s: memory not created for region: %d\n", + __func__, region); + return -EINVAL; + } + + mutex_lock(&wcd9xxx->io_lock); + state = wcd9xxx->wcd9xxx_pwr[region]->power_state; + mutex_unlock(&wcd9xxx->io_lock); + + return state; +} +EXPORT_SYMBOL(wcd9xxx_get_current_power_state); diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 6c27099d9f5436905cafbd45ebc83c81cce9201d..fa119a6bfa72493cf0dbab51372a7f6127bddc58 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -510,3 +510,12 @@ config MSM_AVTIMER help This driver gets the Q6 out of power collapsed state and exposes ioctl control to read avtimer tick. + +config WCD_DSP_GLINK + tristate "WCD DSP GLINK Driver" + depends on MSM_GLINK + default y if SND_SOC_WCD934X=y + help + This option enables driver which provides communication interface + between MSM and WCD DSP over glink transport protocol. This driver + provides read and write interface via char device. diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 7ea7114bad4595bd155e872bfe38844d215e150c..943efd76bf725a8013ef8149a69acb340a53d0c2 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -59,3 +59,4 @@ ifdef CONFIG_MSM_SUBSYSTEM_RESTART obj-y += ramdump.o endif obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o +obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c new file mode 100644 index 0000000000000000000000000000000000000000..3f9f22969d784dfe00add9b3eec595ca2b303b4e --- /dev/null +++ b/drivers/soc/qcom/wcd-dsp-glink.c @@ -0,0 +1,1101 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound/wcd-dsp-glink.h" + +#define WDSP_GLINK_DRIVER_NAME "wcd-dsp-glink" +#define WDSP_MAX_WRITE_SIZE (512 * 1024) +#define WDSP_MAX_READ_SIZE (4 * 1024) + +#define MINOR_NUMBER_COUNT 1 +#define WDSP_EDGE "wdsp" +#define RESP_QUEUE_SIZE 3 +#define QOS_PKT_SIZE 1024 +#define TIMEOUT_MS 1000 + +struct wdsp_glink_dev { + struct class *cls; + struct device *dev; + struct cdev cdev; + dev_t dev_num; +}; + +struct wdsp_glink_rsp_que { + /* Size of valid data in buffer */ + u32 buf_size; + + /* Response buffer */ + u8 buf[WDSP_MAX_READ_SIZE]; +}; + +struct wdsp_glink_tx_buf { + struct work_struct tx_work; + + /* Glink channel information */ + struct wdsp_glink_ch *ch; + + /* Tx buffer to send to glink */ + u8 buf[0]; +}; + +struct wdsp_glink_ch { + struct wdsp_glink_priv *wpriv; + + /* Glink channel handle */ + void *handle; + + /* Channel states like connect, disconnect */ + int channel_state; + struct mutex mutex; + + /* To free up the channel memory */ + bool free_mem; + + /* Glink local channel open work */ + struct work_struct lcl_ch_open_wrk; + + /* Glink local channel close work */ + struct work_struct lcl_ch_cls_wrk; + + /* Wait for ch connect state before sending any command */ + wait_queue_head_t ch_connect_wait; + + /* + * Glink channel configuration. This has to be the last + * member of the strucuture as it has variable size + */ + struct wdsp_glink_ch_cfg ch_cfg; +}; + +struct wdsp_glink_state { + /* Glink link state information */ + enum glink_link_state link_state; + void *handle; +}; + +struct wdsp_glink_priv { + /* Respone buffer related */ + u8 rsp_cnt; + struct wdsp_glink_rsp_que rsp[RESP_QUEUE_SIZE]; + struct completion rsp_complete; + struct mutex rsp_mutex; + + /* Glink channel related */ + struct mutex glink_mutex; + struct wdsp_glink_state glink_state; + struct wdsp_glink_ch **ch; + u8 no_of_channels; + struct work_struct ch_open_cls_wrk; + struct workqueue_struct *work_queue; + + wait_queue_head_t link_state_wait; + + struct device *dev; +}; + +static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch); +static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch); + +/* + * wdsp_glink_notify_rx - Glink notify rx callback for responses + * handle: Opaque Channel handle returned by GLink + * priv: Private pointer to the channel + * pkt_priv: Private pointer to the packet + * ptr: Pointer to the Rx data + * size: Size of the Rx data + */ +static void wdsp_glink_notify_rx(void *handle, const void *priv, + const void *pkt_priv, const void *ptr, + size_t size) +{ + u8 *rx_buf; + u8 rsp_cnt; + struct wdsp_glink_ch *ch; + struct wdsp_glink_priv *wpriv; + + if (!ptr || !priv) { + pr_err("%s: Invalid parameters\n", __func__); + return; + } + + ch = (struct wdsp_glink_ch *)priv; + wpriv = ch->wpriv; + rx_buf = (u8 *)ptr; + if (size > WDSP_MAX_READ_SIZE) { + dev_err(wpriv->dev, "%s: Size %zd is greater than allowed %d\n", + __func__, size, WDSP_MAX_READ_SIZE); + size = WDSP_MAX_READ_SIZE; + } + + mutex_lock(&wpriv->rsp_mutex); + rsp_cnt = wpriv->rsp_cnt; + if (rsp_cnt >= RESP_QUEUE_SIZE) { + dev_err(wpriv->dev, "%s: Resp Queue is Full\n", __func__); + rsp_cnt = 0; + } + dev_dbg(wpriv->dev, "%s: copy into buffer %d\n", __func__, rsp_cnt); + + memcpy(wpriv->rsp[rsp_cnt].buf, rx_buf, size); + wpriv->rsp[rsp_cnt].buf_size = size; + wpriv->rsp_cnt = ++rsp_cnt; + mutex_unlock(&wpriv->rsp_mutex); + + glink_rx_done(handle, ptr, true); + complete(&wpriv->rsp_complete); +} + +/* + * wdsp_glink_notify_tx_done - Glink notify tx done callback to + * free tx buffer + * handle: Opaque Channel handle returned by GLink + * priv: Private pointer to the channel + * pkt_priv: Private pointer to the packet + * ptr: Pointer to the Tx data + */ +static void wdsp_glink_notify_tx_done(void *handle, const void *priv, + const void *pkt_priv, const void *ptr) +{ + if (!pkt_priv) { + pr_err("%s: Invalid parameter\n", __func__); + return; + } + /* Free tx pkt */ + kfree(pkt_priv); +} + +/* + * wdsp_glink_notify_tx_abort - Glink notify tx abort callback to + * free tx buffer + * handle: Opaque Channel handle returned by GLink + * priv: Private pointer to the channel + * pkt_priv: Private pointer to the packet + */ +static void wdsp_glink_notify_tx_abort(void *handle, const void *priv, + const void *pkt_priv) +{ + if (!pkt_priv) { + pr_err("%s: Invalid parameter\n", __func__); + return; + } + /* Free tx pkt */ + kfree(pkt_priv); +} + +/* + * wdsp_glink_notify_rx_intent_req - Glink notify rx intent request callback + * to queue buffer to receive from remote client + * handle: Opaque channel handle returned by GLink + * priv: Private pointer to the channel + * req_size: Size of intent to be queued + */ +static bool wdsp_glink_notify_rx_intent_req(void *handle, const void *priv, + size_t req_size) +{ + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_ch *ch; + int rc = 0; + bool ret = false; + + if (!priv) { + pr_err("%s: Invalid priv\n", __func__); + goto done; + } + if (req_size > WDSP_MAX_READ_SIZE) { + pr_err("%s: Invalid req_size %zd\n", __func__, req_size); + goto done; + } + + ch = (struct wdsp_glink_ch *)priv; + wpriv = ch->wpriv; + + dev_dbg(wpriv->dev, "%s: intent size %zd requested for ch name %s", + __func__, req_size, ch->ch_cfg.name); + + mutex_lock(&ch->mutex); + rc = glink_queue_rx_intent(ch->handle, ch, req_size); + if (IS_ERR_VALUE(rc)) { + dev_err(wpriv->dev, "%s: Failed to queue rx intent, rc = %d\n", + __func__, rc); + mutex_unlock(&ch->mutex); + goto done; + } + mutex_unlock(&ch->mutex); + ret = true; + +done: + return ret; +} + +/* + * wdsp_glink_lcl_ch_open_wrk - Work function to open channel again + * when local disconnect event happens + * work: Work structure + */ +static void wdsp_glink_lcl_ch_open_wrk(struct work_struct *work) +{ + struct wdsp_glink_ch *ch; + + ch = container_of(work, struct wdsp_glink_ch, + lcl_ch_open_wrk); + + wdsp_glink_open_ch(ch); +} + +/* + * wdsp_glink_lcl_ch_cls_wrk - Work function to close channel locally + * when remote disconnect event happens + * work: Work structure + */ +static void wdsp_glink_lcl_ch_cls_wrk(struct work_struct *work) +{ + struct wdsp_glink_ch *ch; + + ch = container_of(work, struct wdsp_glink_ch, + lcl_ch_cls_wrk); + + wdsp_glink_close_ch(ch); +} + +/* + * wdsp_glink_notify_state - Glink channel state information event callback + * handle: Opaque Channel handle returned by GLink + * priv: Private pointer to the channel + * event: channel state event + */ +static void wdsp_glink_notify_state(void *handle, const void *priv, + unsigned int event) +{ + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_ch *ch; + int i, ret = 0; + + if (!priv) { + pr_err("%s: Invalid priv\n", __func__); + return; + } + + ch = (struct wdsp_glink_ch *)priv; + wpriv = ch->wpriv; + + mutex_lock(&ch->mutex); + ch->channel_state = event; + if (event == GLINK_CONNECTED) { + dev_dbg(wpriv->dev, "%s: glink channel: %s connected\n", + __func__, ch->ch_cfg.name); + + for (i = 0; i < ch->ch_cfg.no_of_intents; i++) { + dev_dbg(wpriv->dev, "%s: intent_size = %d\n", __func__, + ch->ch_cfg.intents_size[i]); + ret = glink_queue_rx_intent(ch->handle, ch, + ch->ch_cfg.intents_size[i]); + if (IS_ERR_VALUE(ret)) + dev_warn(wpriv->dev, "%s: Failed to queue intent %d of size %d\n", + __func__, i, + ch->ch_cfg.intents_size[i]); + } + + ret = glink_qos_latency(ch->handle, ch->ch_cfg.latency_in_us, + QOS_PKT_SIZE); + if (IS_ERR_VALUE(ret)) + dev_warn(wpriv->dev, "%s: Failed to request qos %d for ch %s\n", + __func__, ch->ch_cfg.latency_in_us, + ch->ch_cfg.name); + + wake_up(&ch->ch_connect_wait); + mutex_unlock(&ch->mutex); + } else if (event == GLINK_LOCAL_DISCONNECTED) { + /* + * Don't use dev_dbg here as dev may not be valid if channel + * closed from driver close. + */ + pr_debug("%s: channel: %s disconnected locally\n", + __func__, ch->ch_cfg.name); + mutex_unlock(&ch->mutex); + + if (ch->free_mem) { + kfree(ch); + ch = NULL; + } + } else if (event == GLINK_REMOTE_DISCONNECTED) { + dev_dbg(wpriv->dev, "%s: remote channel: %s disconnected remotely\n", + __func__, ch->ch_cfg.name); + mutex_unlock(&ch->mutex); + /* + * If remote disconnect happens, local side also has + * to close the channel as per glink design in a + * separate work_queue. + */ + queue_work(wpriv->work_queue, &ch->lcl_ch_cls_wrk); + } +} + +/* + * wdsp_glink_close_ch - Internal function to close glink channel + * ch: Glink Channel structure. + */ +static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch) +{ + struct wdsp_glink_priv *wpriv = ch->wpriv; + int ret = 0; + + mutex_lock(&wpriv->glink_mutex); + if (ch->handle) { + ret = glink_close(ch->handle); + if (IS_ERR_VALUE(ret)) { + dev_err(wpriv->dev, "%s: glink_close is failed, ret = %d\n", + __func__, ret); + } else { + ch->handle = NULL; + dev_dbg(wpriv->dev, "%s: ch %s is closed\n", __func__, + ch->ch_cfg.name); + } + } else { + dev_dbg(wpriv->dev, "%s: ch %s is already closed\n", __func__, + ch->ch_cfg.name); + } + mutex_unlock(&wpriv->glink_mutex); + + + return ret; +} + +/* + * wdsp_glink_open_ch - Internal function to open glink channel + * ch: Glink Channel structure. + */ +static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch) +{ + struct wdsp_glink_priv *wpriv = ch->wpriv; + struct glink_open_config open_cfg; + int ret = 0; + + mutex_lock(&wpriv->glink_mutex); + if (!ch->handle) { + memset(&open_cfg, 0, sizeof(open_cfg)); + open_cfg.options = GLINK_OPT_INITIAL_XPORT; + open_cfg.edge = WDSP_EDGE; + open_cfg.notify_rx = wdsp_glink_notify_rx; + open_cfg.notify_tx_done = wdsp_glink_notify_tx_done; + open_cfg.notify_tx_abort = wdsp_glink_notify_tx_abort; + open_cfg.notify_state = wdsp_glink_notify_state; + open_cfg.notify_rx_intent_req = wdsp_glink_notify_rx_intent_req; + open_cfg.priv = ch; + open_cfg.name = ch->ch_cfg.name; + + dev_dbg(wpriv->dev, "%s: ch->ch_cfg.name = %s, latency_in_us = %d, intents = %d\n", + __func__, ch->ch_cfg.name, ch->ch_cfg.latency_in_us, + ch->ch_cfg.no_of_intents); + + ch->handle = glink_open(&open_cfg); + if (IS_ERR_OR_NULL(ch->handle)) { + dev_err(wpriv->dev, "%s: glink_open failed for ch %s\n", + __func__, ch->ch_cfg.name); + ch->handle = NULL; + ret = -EINVAL; + } + } else { + dev_err(wpriv->dev, "%s: ch %s is already opened\n", __func__, + ch->ch_cfg.name); + } + mutex_unlock(&wpriv->glink_mutex); + + return ret; +} + +/* + * wdsp_glink_close_all_ch - Internal function to close all glink channels + * wpriv: Wdsp_glink private structure + */ +static void wdsp_glink_close_all_ch(struct wdsp_glink_priv *wpriv) +{ + int i; + + for (i = 0; i < wpriv->no_of_channels; i++) + if (wpriv->ch && wpriv->ch[i]) + wdsp_glink_close_ch(wpriv->ch[i]); +} + +/* + * wdsp_glink_open_all_ch - Internal function to open all glink channels + * wpriv: Wdsp_glink private structure + */ +static int wdsp_glink_open_all_ch(struct wdsp_glink_priv *wpriv) +{ + int ret = 0, i, j; + + for (i = 0; i < wpriv->no_of_channels; i++) { + if (wpriv->ch && wpriv->ch[i]) { + ret = wdsp_glink_open_ch(wpriv->ch[i]); + if (IS_ERR_VALUE(ret)) + goto err_open; + } + } + goto done; + +err_open: + for (j = 0; j < i; j++) + if (wpriv->ch[i]) + wdsp_glink_close_ch(wpriv->ch[j]); + +done: + return ret; +} + +/* + * wdsp_glink_ch_open_wq - Work function to open glink channels + * work: Work structure + */ +static void wdsp_glink_ch_open_cls_wrk(struct work_struct *work) +{ + struct wdsp_glink_priv *wpriv; + + wpriv = container_of(work, struct wdsp_glink_priv, + ch_open_cls_wrk); + + if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) { + dev_info(wpriv->dev, "%s: GLINK_LINK_STATE_DOWN\n", + __func__); + + wdsp_glink_close_all_ch(wpriv); + } else if (wpriv->glink_state.link_state == GLINK_LINK_STATE_UP) { + dev_info(wpriv->dev, "%s: GLINK_LINK_STATE_UP\n", + __func__); + + wdsp_glink_open_all_ch(wpriv); + } +} + +/* + * wdsp_glink_link_state_cb - Glink link state callback to inform + * about link states + * cb_info: Glink link state callback information structure + * priv: Private structure of link state passed while register + */ +static void wdsp_glink_link_state_cb(struct glink_link_state_cb_info *cb_info, + void *priv) +{ + struct wdsp_glink_priv *wpriv; + + if (!cb_info || !priv) { + pr_err("%s: Invalid parameters\n", __func__); + return; + } + + wpriv = (struct wdsp_glink_priv *)priv; + + mutex_lock(&wpriv->glink_mutex); + wpriv->glink_state.link_state = cb_info->link_state; + wake_up(&wpriv->link_state_wait); + mutex_unlock(&wpriv->glink_mutex); + + queue_work(wpriv->work_queue, &wpriv->ch_open_cls_wrk); +} + +/* + * wdsp_glink_ch_info_init- Internal function to allocate channel memory + * and register with glink + * wpriv: Wdsp_glink private structure. + * pkt: Glink registration packet contains glink channel information. + */ +static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, + struct wdsp_reg_pkt *pkt) +{ + int ret = 0, i, j; + struct glink_link_info link_info; + struct wdsp_glink_ch_cfg *ch_cfg; + struct wdsp_glink_ch **ch; + u8 no_of_channels; + u8 *payload; + u32 ch_size, ch_cfg_size; + + payload = (u8 *)pkt->payload; + no_of_channels = pkt->no_of_channels; + + ch = kcalloc(no_of_channels, sizeof(struct wdsp_glink_ch *), + GFP_KERNEL); + if (!ch) { + ret = -ENOMEM; + goto done; + } + + for (i = 0; i < no_of_channels; i++) { + ch_cfg = (struct wdsp_glink_ch_cfg *)payload; + ch_cfg_size = sizeof(struct wdsp_glink_ch_cfg) + + (sizeof(u32) * ch_cfg->no_of_intents); + ch_size = sizeof(struct wdsp_glink_ch) + + (sizeof(u32) * ch_cfg->no_of_intents); + + dev_dbg(wpriv->dev, "%s: channels = %d, ch_cfg_size %d", + __func__, no_of_channels, ch_cfg_size); + + ch[i] = kzalloc(ch_size, GFP_KERNEL); + if (!ch[i]) { + ret = -ENOMEM; + goto err_ch_mem; + } + ch[i]->channel_state = GLINK_LOCAL_DISCONNECTED; + memcpy(&ch[i]->ch_cfg, payload, ch_cfg_size); + payload += ch_cfg_size; + + mutex_init(&ch[i]->mutex); + ch[i]->wpriv = wpriv; + INIT_WORK(&ch[i]->lcl_ch_open_wrk, wdsp_glink_lcl_ch_open_wrk); + INIT_WORK(&ch[i]->lcl_ch_cls_wrk, wdsp_glink_lcl_ch_cls_wrk); + init_waitqueue_head(&ch[i]->ch_connect_wait); + } + wpriv->ch = ch; + wpriv->no_of_channels = no_of_channels; + + INIT_WORK(&wpriv->ch_open_cls_wrk, wdsp_glink_ch_open_cls_wrk); + + /* Register glink link_state notification */ + link_info.glink_link_state_notif_cb = wdsp_glink_link_state_cb; + link_info.transport = NULL; + link_info.edge = WDSP_EDGE; + + wpriv->glink_state.link_state = GLINK_LINK_STATE_DOWN; + wpriv->glink_state.handle = glink_register_link_state_cb(&link_info, + wpriv); + if (!wpriv->glink_state.handle) { + dev_err(wpriv->dev, "%s: Unable to register wdsp link state\n", + __func__); + ret = -EINVAL; + goto err_ch_mem; + } + goto done; + +err_ch_mem: + for (j = 0; j < i; j++) { + mutex_destroy(&ch[j]->mutex); + kfree(wpriv->ch[j]); + wpriv->ch[j] = NULL; + } + kfree(wpriv->ch); + wpriv->ch = NULL; + wpriv->no_of_channels = 0; + +done: + return ret; +} + +/* + * wdsp_glink_tx_buf_work - Work queue function to send tx buffer to glink + * work: Work structure + */ +static void wdsp_glink_tx_buf_work(struct work_struct *work) +{ + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_ch *ch; + struct wdsp_glink_tx_buf *tx_buf; + struct wdsp_write_pkt *wpkt; + struct wdsp_cmd_pkt *cpkt; + int ret = 0; + + tx_buf = container_of(work, struct wdsp_glink_tx_buf, + tx_work); + ch = tx_buf->ch; + wpriv = ch->wpriv; + wpkt = (struct wdsp_write_pkt *)tx_buf->buf; + cpkt = (struct wdsp_cmd_pkt *)wpkt->payload; + dev_dbg(wpriv->dev, "%s: ch name = %s, payload size = %d\n", + __func__, cpkt->ch_name, cpkt->payload_size); + + mutex_lock(&tx_buf->ch->mutex); + if (ch->channel_state == GLINK_CONNECTED) { + mutex_unlock(&tx_buf->ch->mutex); + ret = glink_tx(ch->handle, tx_buf, + cpkt->payload, cpkt->payload_size, + GLINK_TX_REQ_INTENT); + if (IS_ERR_VALUE(ret)) { + dev_err(wpriv->dev, "%s: glink tx failed, ret = %d\n", + __func__, ret); + /* + * If glink_tx() is failed then free tx_buf here as + * there won't be any tx_done notification to + * free the buffer. + */ + kfree(tx_buf); + } + } else { + mutex_unlock(&tx_buf->ch->mutex); + dev_err(wpriv->dev, "%s: channel %s is not in connected state\n", + __func__, ch->ch_cfg.name); + /* + * Free tx_buf here as there won't be any tx_done + * notification in this case also. + */ + kfree(tx_buf); + } +} + +/* + * wdsp_glink_read - Read API to send the data to userspace + * file: Pointer to the file structure + * buf: Pointer to the userspace buffer + * count: Number bytes to read from the file + * ppos: Pointer to the position into the file + */ +static ssize_t wdsp_glink_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int ret = 0, ret1 = 0; + struct wdsp_glink_rsp_que *rsp; + struct wdsp_glink_priv *wpriv; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + ret = -EINVAL; + goto done; + } + + if (count > WDSP_MAX_READ_SIZE) { + dev_info(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n", + __func__, count); + count = WDSP_MAX_READ_SIZE; + } + /* + * Complete signal has given from glink rx notification callback + * or from flush API. Also use interruptible wait_for_completion API + * to allow the system to go in suspend. + */ + ret = wait_for_completion_interruptible(&wpriv->rsp_complete); + if (ret) + goto done; + + mutex_lock(&wpriv->rsp_mutex); + if (wpriv->rsp_cnt) { + wpriv->rsp_cnt--; + dev_dbg(wpriv->dev, "%s: read from buffer %d\n", + __func__, wpriv->rsp_cnt); + + rsp = &wpriv->rsp[wpriv->rsp_cnt]; + if (count < rsp->buf_size) { + ret1 = copy_to_user(buf, &rsp->buf, count); + /* Return the number of bytes copied */ + ret = count; + } else { + ret1 = copy_to_user(buf, &rsp->buf, rsp->buf_size); + /* Return the number of bytes copied */ + ret = rsp->buf_size; + } + + if (ret1) { + mutex_unlock(&wpriv->rsp_mutex); + dev_err(wpriv->dev, "%s: copy_to_user failed %d\n", + __func__, ret); + ret = -EFAULT; + goto done; + } + } else { + /* + * This will execute only if flush API is called or + * something wrong with ref_cnt + */ + dev_dbg(wpriv->dev, "%s: resp count = %d\n", __func__, + wpriv->rsp_cnt); + ret = -EINVAL; + } + mutex_unlock(&wpriv->rsp_mutex); + +done: + return ret; +} + +/* + * wdsp_glink_write - Write API to receive the data from userspace + * file: Pointer to the file structure + * buf: Pointer to the userspace buffer + * count: Number bytes to read from the file + * ppos: Pointer to the position into the file + */ +static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int ret = 0, i, tx_buf_size; + struct wdsp_write_pkt *wpkt; + struct wdsp_cmd_pkt *cpkt; + struct wdsp_glink_tx_buf *tx_buf; + struct wdsp_glink_priv *wpriv; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + ret = -EINVAL; + goto done; + } + + dev_dbg(wpriv->dev, "%s: count = %zd\n", __func__, count); + + if (count > WDSP_MAX_WRITE_SIZE) { + dev_info(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_WRITE_SIZE\n", + __func__, count); + count = WDSP_MAX_WRITE_SIZE; + } + + tx_buf_size = count + sizeof(struct wdsp_glink_tx_buf); + tx_buf = kzalloc(tx_buf_size, GFP_KERNEL); + if (!tx_buf) { + ret = -ENOMEM; + goto done; + } + + ret = copy_from_user(tx_buf->buf, buf, count); + if (ret) { + dev_err(wpriv->dev, "%s: copy_from_user failed %d\n", + __func__, ret); + ret = -EFAULT; + goto free_buf; + } + + wpkt = (struct wdsp_write_pkt *)tx_buf->buf; + switch (wpkt->pkt_type) { + case WDSP_REG_PKT: + ret = wdsp_glink_ch_info_init(wpriv, + (struct wdsp_reg_pkt *)wpkt->payload); + if (IS_ERR_VALUE(ret)) + dev_err(wpriv->dev, "%s: glink register failed, ret = %d\n", + __func__, ret); + kfree(tx_buf); + break; + case WDSP_READY_PKT: + ret = wait_event_timeout(wpriv->link_state_wait, + (wpriv->glink_state.link_state == + GLINK_LINK_STATE_UP), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + dev_err(wpriv->dev, "%s: Link state wait timeout\n", + __func__); + ret = -ETIMEDOUT; + goto free_buf; + } + ret = 0; + kfree(tx_buf); + break; + case WDSP_CMD_PKT: + mutex_lock(&wpriv->glink_mutex); + if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) { + mutex_unlock(&wpriv->glink_mutex); + dev_err(wpriv->dev, "%s: Link state is Down\n", + __func__); + + ret = -ENETRESET; + goto free_buf; + } + mutex_unlock(&wpriv->glink_mutex); + + cpkt = (struct wdsp_cmd_pkt *)wpkt->payload; + dev_dbg(wpriv->dev, "%s: requested ch_name: %s\n", __func__, + cpkt->ch_name); + for (i = 0; i < wpriv->no_of_channels; i++) { + if (wpriv->ch && wpriv->ch[i] && + (!strcmp(cpkt->ch_name, + wpriv->ch[i]->ch_cfg.name))) { + tx_buf->ch = wpriv->ch[i]; + break; + } + } + if (!tx_buf->ch) { + dev_err(wpriv->dev, "%s: Failed to get glink channel\n", + __func__); + ret = -EINVAL; + goto free_buf; + } + + ret = wait_event_timeout(tx_buf->ch->ch_connect_wait, + (tx_buf->ch->channel_state == + GLINK_CONNECTED), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + dev_err(wpriv->dev, "%s: glink channel %s is not in connected state %d\n", + __func__, tx_buf->ch->ch_cfg.name, + tx_buf->ch->channel_state); + ret = -ETIMEDOUT; + goto free_buf; + } + ret = 0; + + INIT_WORK(&tx_buf->tx_work, wdsp_glink_tx_buf_work); + queue_work(wpriv->work_queue, &tx_buf->tx_work); + break; + default: + dev_err(wpriv->dev, "%s: Invalid packet type\n", __func__); + ret = -EINVAL; + kfree(tx_buf); + break; + } + goto done; + +free_buf: + kfree(tx_buf); + +done: + return ret; +} + +/* + * wdsp_glink_open - Open API to initialize private data + * inode: Pointer to the inode structure + * file: Pointer to the file structure + */ +static int wdsp_glink_open(struct inode *inode, struct file *file) +{ + int ret = 0; + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_dev *wdev; + + if (!inode->i_cdev) { + pr_err("%s: cdev is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + wdev = container_of(inode->i_cdev, struct wdsp_glink_dev, cdev); + + wpriv = kzalloc(sizeof(struct wdsp_glink_priv), GFP_KERNEL); + if (!wpriv) { + ret = -ENOMEM; + goto done; + } + wpriv->dev = wdev->dev; + wpriv->work_queue = create_singlethread_workqueue("wdsp_glink_wq"); + if (!wpriv->work_queue) { + dev_err(wpriv->dev, "%s: Error creating wdsp_glink_wq\n", + __func__); + ret = -EINVAL; + goto err_wq; + } + + init_completion(&wpriv->rsp_complete); + init_waitqueue_head(&wpriv->link_state_wait); + mutex_init(&wpriv->rsp_mutex); + mutex_init(&wpriv->glink_mutex); + file->private_data = wpriv; + + goto done; + +err_wq: + kfree(wpriv); + +done: + return ret; +} + +/* + * wdsp_glink_flush - Flush API to unblock read. + * file: Pointer to the file structure + * id: Lock owner ID + */ +static int wdsp_glink_flush(struct file *file, fl_owner_t id) +{ + struct wdsp_glink_priv *wpriv; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + return -EINVAL; + } + + complete(&wpriv->rsp_complete); + + return 0; +} + +/* + * wdsp_glink_release - Release API to clean up resources. + * Whenever a file structure is shared across multiple threads, + * release won't be invoked until all copies are closed + * (file->f_count.counter should be 0). If we need to flush pending + * data when any copy is closed, you should implement the flush method. + * + * inode: Pointer to the inode structure + * file: Pointer to the file structure + */ +static int wdsp_glink_release(struct inode *inode, struct file *file) +{ + int i, ret = 0; + struct wdsp_glink_priv *wpriv; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + ret = -EINVAL; + goto done; + } + + if (wpriv->glink_state.handle) + glink_unregister_link_state_cb(wpriv->glink_state.handle); + + flush_workqueue(wpriv->work_queue); + destroy_workqueue(wpriv->work_queue); + + /* + * Clean up glink channel memory in channel state + * callback only if close channels are called from here. + */ + if (wpriv->ch) { + for (i = 0; i < wpriv->no_of_channels; i++) { + if (wpriv->ch[i]) { + wpriv->ch[i]->free_mem = true; + /* + * Channel handle NULL means channel is already + * closed. Free the channel memory here itself. + */ + if (!wpriv->ch[i]->handle) { + kfree(wpriv->ch[i]); + wpriv->ch[i] = NULL; + } else { + wdsp_glink_close_ch(wpriv->ch[i]); + } + } + } + + kfree(wpriv->ch); + wpriv->ch = NULL; + } + + mutex_destroy(&wpriv->glink_mutex); + mutex_destroy(&wpriv->rsp_mutex); + kfree(wpriv); + file->private_data = NULL; + +done: + return ret; +} + +static const struct file_operations wdsp_glink_fops = { + .owner = THIS_MODULE, + .open = wdsp_glink_open, + .read = wdsp_glink_read, + .write = wdsp_glink_write, + .flush = wdsp_glink_flush, + .release = wdsp_glink_release, +}; + +/* + * wdsp_glink_probe - Driver probe to expose char device + * pdev: Pointer to device tree data. + */ +static int wdsp_glink_probe(struct platform_device *pdev) +{ + int ret; + struct wdsp_glink_dev *wdev; + + wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); + if (!wdev) { + ret = -ENOMEM; + goto done; + } + + ret = alloc_chrdev_region(&wdev->dev_num, 0, MINOR_NUMBER_COUNT, + WDSP_GLINK_DRIVER_NAME); + if (IS_ERR_VALUE(ret)) { + dev_err(&pdev->dev, "%s: Failed to alloc char dev, err = %d\n", + __func__, ret); + goto err_chrdev; + } + + wdev->cls = class_create(THIS_MODULE, WDSP_GLINK_DRIVER_NAME); + if (IS_ERR(wdev->cls)) { + ret = PTR_ERR(wdev->cls); + dev_err(&pdev->dev, "%s: Failed to create class, err = %d\n", + __func__, ret); + goto err_class; + } + + wdev->dev = device_create(wdev->cls, NULL, wdev->dev_num, + NULL, WDSP_GLINK_DRIVER_NAME); + if (IS_ERR(wdev->dev)) { + ret = PTR_ERR(wdev->dev); + dev_err(&pdev->dev, "%s: Failed to create device, err = %d\n", + __func__, ret); + goto err_dev_create; + } + + cdev_init(&wdev->cdev, &wdsp_glink_fops); + ret = cdev_add(&wdev->cdev, wdev->dev_num, MINOR_NUMBER_COUNT); + if (IS_ERR_VALUE(ret)) { + dev_err(&pdev->dev, "%s: Failed to register char dev, err = %d\n", + __func__, ret); + goto err_cdev_add; + } + platform_set_drvdata(pdev, wdev); + goto done; + +err_cdev_add: + device_destroy(wdev->cls, wdev->dev_num); + +err_dev_create: + class_destroy(wdev->cls); + +err_class: + unregister_chrdev_region(0, MINOR_NUMBER_COUNT); + +err_chrdev: + devm_kfree(&pdev->dev, wdev); + +done: + return ret; +} + +/* + * wdsp_glink_remove - Driver remove to handle cleanup + * pdev: Pointer to device tree data. + */ +static int wdsp_glink_remove(struct platform_device *pdev) +{ + struct wdsp_glink_dev *wdev = platform_get_drvdata(pdev); + + if (wdev) { + cdev_del(&wdev->cdev); + device_destroy(wdev->cls, wdev->dev_num); + class_destroy(wdev->cls); + unregister_chrdev_region(0, MINOR_NUMBER_COUNT); + devm_kfree(&pdev->dev, wdev); + } else { + dev_err(&pdev->dev, "%s: Invalid device data\n", __func__); + } + + return 0; +} + +static const struct of_device_id wdsp_glink_of_match[] = { + {.compatible = "qcom,wcd-dsp-glink"}, + { } +}; +MODULE_DEVICE_TABLE(of, wdsp_glink_of_match); + +static struct platform_driver wdsp_glink_driver = { + .probe = wdsp_glink_probe, + .remove = wdsp_glink_remove, + .driver = { + .name = WDSP_GLINK_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = wdsp_glink_of_match, + }, +}; + +module_platform_driver(wdsp_glink_driver); + +MODULE_DESCRIPTION("SoC WCD_DSP GLINK Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..ef8a351ea5031d0435364facd484453c1e12eeaa --- /dev/null +++ b/drivers/soundwire/Kconfig @@ -0,0 +1,17 @@ +# +# SOUNDWIRE driver configuration +# +menuconfig SOUNDWIRE + bool "Soundwire support" + help + Soundwire is a two wire interface for audio to connect + simple peripheral components in mobile devices. + +if SOUNDWIRE +config SOUNDWIRE_WCD_CTRL + depends on WCD9335_CODEC + tristate "QTI WCD CODEC Soundwire controller" + default n + help + Select driver for QTI's Soundwire Master Component. +endif diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..53acff15c4c49f019fb26b5153e8f39df72adf5d --- /dev/null +++ b/drivers/soundwire/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for kernel soundwire framework. +# +obj-$(CONFIG_SOUNDWIRE) += soundwire.o +obj-$(CONFIG_SOUNDWIRE_WCD_CTRL) += swr-wcd-ctrl.o diff --git a/drivers/soundwire/soundwire.c b/drivers/soundwire/soundwire.c new file mode 100644 index 0000000000000000000000000000000000000000..68655a52c69f4bf19786127421b6d53a12c29100 --- /dev/null +++ b/drivers/soundwire/soundwire.c @@ -0,0 +1,1051 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct boardinfo { + struct list_head list; + struct swr_boardinfo board_info; +}; + +static LIST_HEAD(board_list); +static LIST_HEAD(swr_master_list); +static DEFINE_MUTEX(board_lock); +static DEFINE_IDR(master_idr); +static DEFINE_MUTEX(swr_lock); + +static struct device_type swr_dev_type; + +#define SOUNDWIRE_NAME_SIZE 32 + +static void swr_master_put(struct swr_master *master) +{ + if (master) + put_device(&master->dev); +} + +static struct swr_master *swr_master_get(struct swr_master *master) +{ + if (!master || !get_device(&master->dev)) + return NULL; + return master; +} + +static void swr_dev_release(struct device *dev) +{ + struct swr_device *swr_dev = to_swr_device(dev); + struct swr_master *master; + + if (!swr_dev) + return; + master = swr_dev->master; + if (!master) + return; + mutex_lock(&master->mlock); + list_del_init(&swr_dev->dev_list); + mutex_unlock(&master->mlock); + swr_master_put(swr_dev->master); + kfree(swr_dev); +} + +/** + * swr_new_device - instantiate a new soundwire device + * @master: Controller to which device is connected + * @info: Describes the soundwire device + * Context: can sleep + * + * Create a soundwire device. Binding is handled through driver model + * probe/remove methods. A driver may be bound to this device when + * the function gets returned. + * + * Returns a soundwire new device or NULL + */ +struct swr_device *swr_new_device(struct swr_master *master, + struct swr_boardinfo const *info) +{ + int result; + struct swr_device *swr; + + if (!master || !swr_master_get(master)) { + pr_err("%s: master is NULL\n", __func__); + return NULL; + } + + swr = kzalloc(sizeof(*swr), GFP_KERNEL); + if (!swr) { + put_device(&master->dev); + return NULL; + } + swr->master = master; + swr->addr = info->addr; + strlcpy(swr->name, info->name, sizeof(swr->name)); + swr->dev.type = &swr_dev_type; + swr->dev.parent = &master->dev; + swr->dev.bus = &soundwire_type; + swr->dev.release = swr_dev_release; + swr->dev.of_node = info->of_node; + mutex_lock(&master->mlock); + list_add_tail(&swr->dev_list, &master->devices); + mutex_unlock(&master->mlock); + + dev_set_name(&swr->dev, "%s.%lx", swr->name, swr->addr); + result = device_register(&swr->dev); + if (result) { + dev_err(&master->dev, "device [%s] register failed err %d\n", + swr->name, result); + goto err_out; + } + dev_dbg(&master->dev, "Device [%s] registered with bus id %s\n", + swr->name, dev_name(&swr->dev)); + return swr; + +err_out: + dev_dbg(&master->dev, "Failed to register swr device %s at 0x%lx %d\n", + swr->name, swr->addr, result); + swr_master_put(master); + kfree(swr); + return NULL; +} +EXPORT_SYMBOL(swr_new_device); + +/** + * swr_startup_devices - perform additional initialization for child devices + * + * @swr_dev: pointer to soundwire slave device + * + * Performs any additional initialization needed for a soundwire slave device. + * This is a optional functionality defined by slave devices. + * Removes the slave node from the list, in case there is any failure. + */ +int swr_startup_devices(struct swr_device *swr_dev) +{ + struct swr_driver *swr_drv; + struct device *dev; + int ret = 0; + + if (!swr_dev) + return -EINVAL; + + dev = &swr_dev->dev; + if (!dev) + return -EINVAL; + + swr_drv = to_swr_driver(dev->driver); + if (!swr_drv) + return -EINVAL; + + if (swr_drv->startup) { + ret = swr_drv->startup(swr_dev); + if (ret) + goto out; + + dev_dbg(&swr_dev->dev, + "%s: startup complete for device %lx\n", + __func__, swr_dev->addr); + } + +out: + return ret; +} +EXPORT_SYMBOL(swr_startup_devices); + +/** + * of_register_swr_devices - register child devices on to the soundwire bus + * @master: pointer to soundwire master device + * + * Registers a soundwire device for each child node of master node which has + * a "swr-devid" property + * + */ +int of_register_swr_devices(struct swr_master *master) +{ + struct swr_device *swr; + struct device_node *node; + + if (!master->dev.of_node) + return -EINVAL; + + for_each_available_child_of_node(master->dev.of_node, node) { + struct swr_boardinfo info = {}; + u64 addr; + + dev_dbg(&master->dev, "of_swr:register %s\n", node->full_name); + + if (of_modalias_node(node, info.name, sizeof(info.name)) < 0) { + dev_err(&master->dev, "of_swr:modalias failure %s\n", + node->full_name); + continue; + } + if (of_property_read_u64(node, "reg", &addr)) { + dev_err(&master->dev, "of_swr:invalid reg %s\n", + node->full_name); + continue; + } + info.addr = addr; + info.of_node = of_node_get(node); + swr = swr_new_device(master, &info); + if (!swr) { + dev_err(&master->dev, "of_swr: Register failed %s\n", + node->full_name); + of_node_put(node); + continue; + } + master->num_dev++; + } + return 0; +} +EXPORT_SYMBOL(of_register_swr_devices); + +/** + * swr_port_response - response from master to free the completed transaction + * @mstr: pointer to soundwire master device + * @tid: transaction id that indicates transaction to be freed. + * + * Master calls this function to free the compeleted transaction memory + */ +void swr_port_response(struct swr_master *mstr, u8 tid) +{ + struct swr_params *txn; + + txn = mstr->port_txn[tid]; + + if (txn == NULL) { + dev_err(&mstr->dev, "%s: transaction is already NULL\n", + __func__); + return; + } + mstr->port_txn[tid] = NULL; + kfree(txn); +} +EXPORT_SYMBOL(swr_port_response); + +/** + * swr_remove_from_group - remove soundwire slave devices from group + * @dev: pointer to the soundwire slave device + * dev_num: device number of the soundwire slave device + * + * Returns error code for failure and 0 for success + */ +int swr_remove_from_group(struct swr_device *dev, u8 dev_num) +{ + struct swr_master *master; + + if (!dev) + return -ENODEV; + + master = dev->master; + if (!master) + return -EINVAL; + + if (!dev->group_id) + return 0; + + if (master->gr_sid == dev_num) + return 0; + + if (master->remove_from_group && master->remove_from_group(master)) + dev_dbg(&master->dev, "%s: falling back to GROUP_NONE\n", + __func__); + + return 0; +} +EXPORT_SYMBOL(swr_remove_from_group); + +/** + * swr_slvdev_datapath_control - Enables/Disables soundwire slave device + * data path + * @dev: pointer to soundwire slave device + * @dev_num: device number of the soundwire slave device + * + * Returns error code for failure and 0 for success + */ +int swr_slvdev_datapath_control(struct swr_device *dev, u8 dev_num, + bool enable) +{ + struct swr_master *master; + + if (!dev) + return -ENODEV; + + master = dev->master; + if (!master) + return -EINVAL; + + if (dev->group_id) { + /* Broadcast */ + if (master->gr_sid != dev_num) { + if (!master->gr_sid) + master->gr_sid = dev_num; + else + return 0; + } + } + + if (master->slvdev_datapath_control) + master->slvdev_datapath_control(master, enable); + + return 0; +} +EXPORT_SYMBOL(swr_slvdev_datapath_control); + +/** + * swr_connect_port - enable soundwire slave port(s) + * @dev: pointer to soundwire slave device + * @port_id: logical port id(s) of soundwire slave device + * @num_port: number of slave device ports need to be enabled + * @ch_mask: channels for each port that needs to be enabled + * @ch_rate: rate at which each port/channels operate + * @num_ch: number of channels for each port + * + * soundwire slave device call swr_connect_port API to enable all/some of + * its ports and corresponding channels and channel rate. This API will + * call master connect_port callback function to calculate frame structure + * and enable master and slave ports + */ +int swr_connect_port(struct swr_device *dev, u8 *port_id, u8 num_port, + u8 *ch_mask, u32 *ch_rate, u8 *num_ch) +{ + u8 i = 0; + int ret = 0; + struct swr_params *txn = NULL; + struct swr_params **temp_txn = NULL; + struct swr_master *master = dev->master; + + if (!master) { + pr_err("%s: Master is NULL\n", __func__); + return -EINVAL; + } + if (num_port > SWR_MAX_DEV_PORT_NUM) { + dev_err(&master->dev, "%s: num_port %d exceeds max port %d\n", + __func__, num_port, SWR_MAX_DEV_PORT_NUM); + return -EINVAL; + } + + /* + * create "txn" to accommodate ports enablement of + * different slave devices calling swr_connect_port at the + * same time. Once master process the txn data, it calls + * swr_port_response() to free the transaction. Maximum + * of 256 transactions can be allocated. + */ + txn = kzalloc(sizeof(struct swr_params), GFP_KERNEL); + if (!txn) + return -ENOMEM; + + mutex_lock(&master->mlock); + for (i = 0; i < master->last_tid; i++) { + if (master->port_txn[i] == NULL) + break; + } + if (i >= master->last_tid) { + if (master->last_tid == 255) { + mutex_unlock(&master->mlock); + kfree(txn); + dev_err(&master->dev, "%s Max tid reached\n", + __func__); + return -ENOMEM; + } + temp_txn = krealloc(master->port_txn, + (i + 1) * sizeof(struct swr_params *), + GFP_KERNEL); + if (!temp_txn) { + mutex_unlock(&master->mlock); + kfree(txn); + dev_err(&master->dev, "%s Not able to allocate\n" + "master port transaction memory\n", + __func__); + return -ENOMEM; + } + master->port_txn = temp_txn; + master->last_tid++; + } + master->port_txn[i] = txn; + mutex_unlock(&master->mlock); + txn->tid = i; + + txn->dev_id = dev->dev_num; + txn->num_port = num_port; + for (i = 0; i < num_port; i++) { + txn->port_id[i] = port_id[i]; + txn->num_ch[i] = num_ch[i]; + txn->ch_rate[i] = ch_rate[i]; + txn->ch_en[i] = ch_mask[i]; + } + ret = master->connect_port(master, txn); + return ret; +} +EXPORT_SYMBOL(swr_connect_port); + +/** + * swr_disconnect_port - disable soundwire slave port(s) + * @dev: pointer to soundwire slave device + * @port_id: logical port id(s) of soundwire slave device + * @num_port: number of slave device ports need to be disabled + * + * soundwire slave device call swr_disconnect_port API to disable all/some of + * its ports. This API will call master disconnect_port callback function to + * disable master and slave port and (re)configure frame structure + */ +int swr_disconnect_port(struct swr_device *dev, u8 *port_id, u8 num_port) +{ + u8 i = 0; + int ret; + struct swr_params *txn = NULL; + struct swr_params **temp_txn = NULL; + struct swr_master *master = dev->master; + + if (!master) { + pr_err("%s: Master is NULL\n", __func__); + return -EINVAL; + } + + if (num_port > SWR_MAX_DEV_PORT_NUM) { + dev_err(&master->dev, "%s: num_port %d exceeds max port %d\n", + __func__, num_port, SWR_MAX_DEV_PORT_NUM); + return -EINVAL; + } + + txn = kzalloc(sizeof(struct swr_params), GFP_KERNEL); + if (!txn) + return -ENOMEM; + + mutex_lock(&master->mlock); + for (i = 0; i < master->last_tid; i++) { + if (master->port_txn[i] == NULL) + break; + } + if (i >= master->last_tid) { + if (master->last_tid == 255) { + mutex_unlock(&master->mlock); + kfree(txn); + dev_err(&master->dev, "%s Max tid reached\n", + __func__); + return -ENOMEM; + } + temp_txn = krealloc(master->port_txn, + (i + 1) * sizeof(struct swr_params *), + GFP_KERNEL); + if (!temp_txn) { + mutex_unlock(&master->mlock); + kfree(txn); + dev_err(&master->dev, "%s Not able to allocate\n" + "master port transaction memory\n", + __func__); + return -ENOMEM; + } + master->port_txn = temp_txn; + master->last_tid++; + } + master->port_txn[i] = txn; + mutex_unlock(&master->mlock); + txn->tid = i; + + txn->dev_id = dev->dev_num; + txn->num_port = num_port; + for (i = 0; i < num_port; i++) + txn->port_id[i] = port_id[i]; + ret = master->disconnect_port(master, txn); + return ret; +} +EXPORT_SYMBOL(swr_disconnect_port); + +/** + * swr_get_logical_dev_num - Get soundwire slave logical device number + * @dev: pointer to soundwire slave device + * @dev_id: physical device id of soundwire slave device + * @dev_num: pointer to logical device num of soundwire slave device + * + * This API will get the logical device number of soundwire slave device + */ +int swr_get_logical_dev_num(struct swr_device *dev, u64 dev_id, + u8 *dev_num) +{ + int ret = 0; + struct swr_master *master = dev->master; + + if (!master) { + pr_err("%s: Master is NULL\n", __func__); + return -EINVAL; + } + mutex_lock(&master->mlock); + ret = master->get_logical_dev_num(master, dev_id, dev_num); + if (ret) { + pr_err("%s: Error %d to get logical addr for device %llx\n", + __func__, ret, dev_id); + } + mutex_unlock(&master->mlock); + return ret; +} +EXPORT_SYMBOL(swr_get_logical_dev_num); + +/** + * swr_read - read soundwire slave device registers + * @dev: pointer to soundwire slave device + * @dev_num: logical device num of soundwire slave device + * @reg_addr: base register address that needs to be read + * @buf: pointer to store the values of registers from base address + * @len: length of the buffer + * + * This API will read the value of the register address from + * soundwire slave device + */ +int swr_read(struct swr_device *dev, u8 dev_num, u16 reg_addr, + void *buf, u32 len) +{ + struct swr_master *master = dev->master; + + if (!master) + return -EINVAL; + return master->read(master, dev_num, reg_addr, buf, len); +} +EXPORT_SYMBOL(swr_read); + +/** + * swr_bulk_write - write soundwire slave device registers + * @dev: pointer to soundwire slave device + * @dev_num: logical device num of soundwire slave device + * @reg_addr: register address of soundwire slave device + * @buf: contains value of register address + * @len: indicates number of registers + * + * This API will write the value of the register address to + * soundwire slave device + */ +int swr_bulk_write(struct swr_device *dev, u8 dev_num, void *reg, + const void *buf, size_t len) +{ + struct swr_master *master; + + if (!dev || !dev->master) + return -EINVAL; + + master = dev->master; + if (dev->group_id) { + if (master->gr_sid != dev_num) { + if (!master->gr_sid) + master->gr_sid = dev_num; + else + return 0; + } + dev_num = dev->group_id; + } + if (master->bulk_write) + return master->bulk_write(master, dev_num, reg, buf, len); + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL(swr_bulk_write); + +/** + * swr_write - write soundwire slave device registers + * @dev: pointer to soundwire slave device + * @dev_num: logical device num of soundwire slave device + * @reg_addr: register address of soundwire slave device + * @buf: contains value of register address + * + * This API will write the value of the register address to + * soundwire slave device + */ +int swr_write(struct swr_device *dev, u8 dev_num, u16 reg_addr, + const void *buf) +{ + struct swr_master *master = dev->master; + + if (!master) + return -EINVAL; + + if (dev->group_id) { + if (master->gr_sid != dev_num) { + if (!master->gr_sid) + master->gr_sid = dev_num; + else + return 0; + } + dev_num = dev->group_id; + } + return master->write(master, dev_num, reg_addr, buf); +} +EXPORT_SYMBOL(swr_write); + +/** + * swr_device_up - Function to bringup the soundwire slave device + * @swr_dev: pointer to soundwire slave device + * Context: can sleep + * + * This API will be called by soundwire master to bringup the slave + * device. + */ +int swr_device_up(struct swr_device *swr_dev) +{ + struct device *dev; + const struct swr_driver *sdrv; + + if (!swr_dev) + return -EINVAL; + + dev = &swr_dev->dev; + sdrv = to_swr_driver(dev->driver); + if (!sdrv) + return -EINVAL; + + if (sdrv->device_up) + return sdrv->device_up(to_swr_device(dev)); + + return -ENODEV; +} +EXPORT_SYMBOL(swr_device_up); + +/** + * swr_device_down - Function to call soundwire slave device down + * @swr_dev: pointer to soundwire slave device + * Context: can sleep + * + * This API will be called by soundwire master to put slave device in + * shutdown state. + */ +int swr_device_down(struct swr_device *swr_dev) +{ + struct device *dev; + const struct swr_driver *sdrv; + + if (!swr_dev) + return -EINVAL; + + dev = &swr_dev->dev; + sdrv = to_swr_driver(dev->driver); + if (!sdrv) + return -EINVAL; + + if (sdrv->device_down) + return sdrv->device_down(to_swr_device(dev)); + + return -ENODEV; +} +EXPORT_SYMBOL(swr_device_down); + +/** + * swr_reset_device - reset soundwire slave device + * @swr_dev: pointer to soundwire slave device + * Context: can sleep + * + * This API will be called by soundwire master to reset the slave + * device when the slave device is not responding or in undefined + * state + */ +int swr_reset_device(struct swr_device *swr_dev) +{ + struct device *dev; + const struct swr_driver *sdrv; + + if (!swr_dev) + return -EINVAL; + + dev = &swr_dev->dev; + sdrv = to_swr_driver(dev->driver); + if (!sdrv) + return -EINVAL; + + if (sdrv->reset_device) + return sdrv->reset_device(to_swr_device(dev)); + + return -ENODEV; +} +EXPORT_SYMBOL(swr_reset_device); + +/** + * swr_set_device_group - Assign group id to the slave devices + * @swr_dev: pointer to soundwire slave device + * @id: group id to be assigned to slave device + * Context: can sleep + * + * This API will be called either from soundwire master or slave + * device to assign group id. + */ +int swr_set_device_group(struct swr_device *swr_dev, u8 id) +{ + struct swr_master *master; + + if (!swr_dev) + return -EINVAL; + + swr_dev->group_id = id; + master = swr_dev->master; + if (!id && master) + master->gr_sid = 0; + + return 0; +} +EXPORT_SYMBOL(swr_set_device_group); + +static int swr_drv_probe(struct device *dev) +{ + const struct swr_driver *sdrv = to_swr_driver(dev->driver); + + if (!sdrv) + return -EINVAL; + + if (sdrv->probe) + return sdrv->probe(to_swr_device(dev)); + return -ENODEV; +} + +static int swr_drv_remove(struct device *dev) +{ + const struct swr_driver *sdrv = to_swr_driver(dev->driver); + + if (!sdrv) + return -EINVAL; + + if (sdrv->remove) + return sdrv->remove(to_swr_device(dev)); + return -ENODEV; +} + +static void swr_drv_shutdown(struct device *dev) +{ + const struct swr_driver *sdrv = to_swr_driver(dev->driver); + + if (!sdrv) + return; + + if (sdrv->shutdown) + sdrv->shutdown(to_swr_device(dev)); +} + +/** + * swr_driver_register - register a soundwire driver + * @drv: the driver to register + * Context: can sleep + */ +int swr_driver_register(struct swr_driver *drv) +{ + drv->driver.bus = &soundwire_type; + if (drv->probe) + drv->driver.probe = swr_drv_probe; + if (drv->remove) + drv->driver.remove = swr_drv_remove; + + if (drv->shutdown) + drv->driver.shutdown = swr_drv_shutdown; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL(swr_driver_register); + +/** + * swr_driver_unregister - unregister a soundwire driver + * @drv: the driver to unregister + */ +void swr_driver_unregister(struct swr_driver *drv) +{ + if (drv) + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL(swr_driver_unregister); + +static void swr_match_ctrl_to_boardinfo(struct swr_master *master, + struct swr_boardinfo *bi) +{ + struct swr_device *swr; + + if (master->bus_num != bi->bus_num) { + dev_dbg(&master->dev, + "%s: master# %d and bi# %d does not match\n", + __func__, master->bus_num, bi->bus_num); + return; + } + + swr = swr_new_device(master, bi); + if (!swr) + dev_err(&master->dev, "can't create new device for %s\n", + bi->swr_slave->name); +} + +/** + * swr_master_add_boarddevices - Add devices registered by board info + * @master: master to which these devices are to be added to. + * + * This API is called by master when it is up and running. If devices + * on a master were registered before master, this will make sure that + * they get probed when master is up. + */ +void swr_master_add_boarddevices(struct swr_master *master) +{ + struct boardinfo *bi; + + mutex_lock(&board_lock); + list_add_tail(&master->list, &swr_master_list); + list_for_each_entry(bi, &board_list, list) + swr_match_ctrl_to_boardinfo(master, &bi->board_info); + mutex_unlock(&board_lock); +} +EXPORT_SYMBOL(swr_master_add_boarddevices); + +static void swr_unregister_device(struct swr_device *swr) +{ + if (swr) + device_unregister(&swr->dev); +} + +static void swr_master_release(struct device *dev) +{ + struct swr_master *master = to_swr_master(dev); + + kfree(master); +} + +#define swr_master_attr_gr NULL +static struct device_type swr_master_type = { + .groups = swr_master_attr_gr, + .release = swr_master_release, +}; + +static int __unregister(struct device *dev, void *null) +{ + swr_unregister_device(to_swr_device(dev)); + return 0; +} + +/** + * swr_unregister_master - unregister soundwire master controller + * @master: the master being unregistered + * + * This API is called by master controller driver to unregister + * master controller that was registered by swr_register_master API. + */ +void swr_unregister_master(struct swr_master *master) +{ + int dummy; + struct swr_master *m_ctrl; + + mutex_lock(&swr_lock); + m_ctrl = idr_find(&master_idr, master->bus_num); + mutex_unlock(&swr_lock); + if (m_ctrl != master) + return; + + mutex_lock(&board_lock); + list_del(&master->list); + mutex_unlock(&board_lock); + + /* free bus id */ + mutex_lock(&swr_lock); + idr_remove(&master_idr, master->bus_num); + mutex_unlock(&swr_lock); + + dummy = device_for_each_child(&master->dev, NULL, __unregister); + device_unregister(&master->dev); +} +EXPORT_SYMBOL(swr_unregister_master); + +/** + * swr_register_master - register soundwire master controller + * @master: master to be registered + * + * This API will register master with the framework. master->bus_num + * is the desired number with which soundwire framework registers the + * master. + */ +int swr_register_master(struct swr_master *master) +{ + int id; + int status = 0; + + mutex_lock(&swr_lock); + id = idr_alloc(&master_idr, master, master->bus_num, + master->bus_num+1, GFP_KERNEL); + mutex_unlock(&swr_lock); + if (id < 0) + return id; + master->bus_num = id; + + /* Can't register until driver model init */ + if (WARN_ON(!soundwire_type.p)) { + status = -EAGAIN; + goto done; + } + + dev_set_name(&master->dev, "swr%u", master->bus_num); + master->dev.bus = &soundwire_type; + master->dev.type = &swr_master_type; + mutex_init(&master->mlock); + status = device_register(&master->dev); + if (status < 0) + goto done; + + INIT_LIST_HEAD(&master->devices); + pr_debug("%s: SWR master registered successfully %s\n", + __func__, dev_name(&master->dev)); + return 0; + +done: + idr_remove(&master_idr, master->bus_num); + return status; +} +EXPORT_SYMBOL(swr_register_master); + +#define swr_device_attr_gr NULL +#define swr_device_uevent NULL +static struct device_type swr_dev_type = { + .groups = swr_device_attr_gr, + .uevent = swr_device_uevent, + .release = swr_dev_release, +}; + +static const struct swr_device_id *swr_match(const struct swr_device_id *id, + const struct swr_device *swr_dev) +{ + while (id->name[0]) { + if (strcmp(swr_dev->name, id->name) == 0) + return id; + id++; + } + return NULL; +} + +static int swr_device_match(struct device *dev, struct device_driver *driver) +{ + struct swr_device *swr_dev; + struct swr_driver *drv = to_swr_driver(driver); + + if (!drv) + return -EINVAL; + + if (dev->type == &swr_dev_type) + swr_dev = to_swr_device(dev); + else + return 0; + if (drv->id_table) + return swr_match(drv->id_table, swr_dev) != NULL; + + if (driver->name) + return strcmp(swr_dev->name, driver->name) == 0; + return 0; +} +#ifdef CONFIG_PM_SLEEP +static int swr_legacy_suspend(struct device *dev, pm_message_t mesg) +{ + struct swr_device *swr_dev = NULL; + struct swr_driver *driver; + + if (dev->type == &swr_dev_type) + swr_dev = to_swr_device(dev); + + if (!swr_dev || !dev->driver) + return 0; + + driver = to_swr_driver(dev->driver); + if (!driver->suspend) + return 0; + + return driver->suspend(swr_dev, mesg); +} + +static int swr_legacy_resume(struct device *dev) +{ + struct swr_device *swr_dev = NULL; + struct swr_driver *driver; + + if (dev->type == &swr_dev_type) + swr_dev = to_swr_device(dev); + + if (!swr_dev || !dev->driver) + return 0; + + driver = to_swr_driver(dev->driver); + if (!driver->resume) + return 0; + + return driver->resume(swr_dev); +} + +static int swr_pm_suspend(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_suspend(dev); + else + return swr_legacy_suspend(dev, PMSG_SUSPEND); +} + +static int swr_pm_resume(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_resume(dev); + else + return swr_legacy_resume(dev); +} +#else +#define swr_pm_suspend NULL +#define swr_pm_resume NULL +#endif /*CONFIG_PM_SLEEP*/ + +static const struct dev_pm_ops soundwire_pm = { + .suspend = swr_pm_suspend, + .resume = swr_pm_resume, + SET_RUNTIME_PM_OPS( + pm_generic_suspend, + pm_generic_resume, + NULL + ) +}; + +struct device soundwire_dev = { + .init_name = "soundwire", +}; + +struct bus_type soundwire_type = { + .name = "soundwire", + .match = swr_device_match, + .pm = &soundwire_pm, +}; +EXPORT_SYMBOL(soundwire_type); + +static void __exit soundwire_exit(void) +{ + device_unregister(&soundwire_dev); + bus_unregister(&soundwire_type); +} + +static int __init soundwire_init(void) +{ + int retval; + + retval = bus_register(&soundwire_type); + if (!retval) + retval = device_register(&soundwire_dev); + + if (retval) + bus_unregister(&soundwire_type); + + return retval; +} +postcore_initcall(soundwire_init); +module_exit(soundwire_exit); + + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Soundwire module"); +MODULE_ALIAS("platform:soundwire"); diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c new file mode 100644 index 0000000000000000000000000000000000000000..b9984f2a10cd26dbf32c29585384ef821485aab2 --- /dev/null +++ b/drivers/soundwire/swr-wcd-ctrl.c @@ -0,0 +1,1853 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "swrm_registers.h" +#include "swr-wcd-ctrl.h" + +#define SWR_BROADCAST_CMD_ID 0x0F +#define SWR_AUTO_SUSPEND_DELAY 3 /* delay in sec */ +#define SWR_DEV_ID_MASK 0xFFFFFFFF +#define SWR_REG_VAL_PACK(data, dev, id, reg) \ + ((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24)) + +/* pm runtime auto suspend timer in msecs */ +static int auto_suspend_timer = SWR_AUTO_SUSPEND_DELAY * 1000; +module_param(auto_suspend_timer, int, 0664); +MODULE_PARM_DESC(auto_suspend_timer, "timer for auto suspend"); + +static u8 mstr_ports[] = {100, 101, 102, 103, 104, 105, 106, 107}; +static u8 mstr_port_type[] = {SWR_DAC_PORT, SWR_COMP_PORT, SWR_BOOST_PORT, + SWR_DAC_PORT, SWR_COMP_PORT, SWR_BOOST_PORT, + SWR_VISENSE_PORT, SWR_VISENSE_PORT}; + +struct usecase uc[] = { + {0, 0, 0}, /* UC0: no ports */ + {1, 1, 2400}, /* UC1: Spkr */ + {1, 4, 600}, /* UC2: Compander */ + {1, 2, 300}, /* UC3: Smart Boost */ + {1, 2, 1200}, /* UC4: VI Sense */ + {4, 9, 4500}, /* UC5: Spkr + Comp + SB + VI */ + {8, 18, 9000}, /* UC6: 2*(Spkr + Comp + SB + VI) */ + {2, 2, 4800}, /* UC7: 2*Spkr */ + {2, 5, 3000}, /* UC8: Spkr + Comp */ + {4, 10, 6000}, /* UC9: 2*(Spkr + Comp) */ + {3, 7, 3300}, /* UC10: Spkr + Comp + SB */ + {6, 14, 6600}, /* UC11: 2*(Spkr + Comp + SB) */ + {2, 3, 2700}, /* UC12: Spkr + SB */ + {4, 6, 5400}, /* UC13: 2*(Spkr + SB) */ + {3, 5, 3900}, /* UC14: Spkr + SB + VI */ + {6, 10, 7800}, /* UC15: 2*(Spkr + SB + VI) */ + {2, 3, 3600}, /* UC16: Spkr + VI */ + {4, 6, 7200}, /* UC17: 2*(Spkr + VI) */ +}; +#define MAX_USECASE ARRAY_SIZE(uc) + +struct port_params pp[MAX_USECASE][SWR_MSTR_PORT_LEN] = { + /* UC 0 */ + { + {0, 0, 0}, + }, + /* UC 1 */ + { + {7, 1, 0}, + }, + /* UC 2 */ + { + {31, 2, 0}, + }, + /* UC 3 */ + { + {63, 12, 31}, + }, + /* UC 4 */ + { + {15, 7, 0}, + }, + /* UC 5 */ + { + {7, 1, 0}, + {31, 2, 0}, + {63, 12, 31}, + {15, 7, 0}, + }, + /* UC 6 */ + { + {7, 1, 0}, + {31, 2, 0}, + {63, 12, 31}, + {15, 7, 0}, + {7, 6, 0}, + {31, 18, 0}, + {63, 13, 31}, + {15, 10, 0}, + }, + /* UC 7 */ + { + {7, 1, 0}, + {7, 6, 0}, + + }, + /* UC 8 */ + { + {7, 1, 0}, + {31, 2, 0}, + }, + /* UC 9 */ + { + {7, 1, 0}, + {31, 2, 0}, + {7, 6, 0}, + {31, 18, 0}, + }, + /* UC 10 */ + { + {7, 1, 0}, + {31, 2, 0}, + {63, 12, 31}, + }, + /* UC 11 */ + { + {7, 1, 0}, + {31, 2, 0}, + {63, 12, 31}, + {7, 6, 0}, + {31, 18, 0}, + {63, 13, 31}, + }, + /* UC 12 */ + { + {7, 1, 0}, + {63, 12, 31}, + }, + /* UC 13 */ + { + {7, 1, 0}, + {63, 12, 31}, + {7, 6, 0}, + {63, 13, 31}, + }, + /* UC 14 */ + { + {7, 1, 0}, + {63, 12, 31}, + {15, 7, 0}, + }, + /* UC 15 */ + { + {7, 1, 0}, + {63, 12, 31}, + {15, 7, 0}, + {7, 6, 0}, + {63, 13, 31}, + {15, 10, 0}, + }, + /* UC 16 */ + { + {7, 1, 0}, + {15, 7, 0}, + }, + /* UC 17 */ + { + {7, 1, 0}, + {15, 7, 0}, + {7, 6, 0}, + {15, 10, 0}, + }, +}; + +enum { + SWR_NOT_PRESENT, /* Device is detached/not present on the bus */ + SWR_ATTACHED_OK, /* Device is attached */ + SWR_ALERT, /* Device alters master for any interrupts */ + SWR_RESERVED, /* Reserved */ +}; + +#define SWRM_MAX_PORT_REG 40 +#define SWRM_MAX_INIT_REG 8 + +#define SWR_MSTR_MAX_REG_ADDR 0x1740 +#define SWR_MSTR_START_REG_ADDR 0x00 +#define SWR_MSTR_MAX_BUF_LEN 32 +#define BYTES_PER_LINE 12 +#define SWR_MSTR_RD_BUF_LEN 8 +#define SWR_MSTR_WR_BUF_LEN 32 + +static void swrm_copy_data_port_config(struct swr_master *master, + u8 inactive_bank); +static struct swr_mstr_ctrl *dbgswrm; +static struct dentry *debugfs_swrm_dent; +static struct dentry *debugfs_peek; +static struct dentry *debugfs_poke; +static struct dentry *debugfs_reg_dump; +static unsigned int read_data; + +static int swrm_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int get_parameters(char *buf, u32 *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtou32(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + return 0; +} + +static ssize_t swrm_reg_show(char __user *ubuf, size_t count, + loff_t *ppos) +{ + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[SWR_MSTR_MAX_BUF_LEN]; + + if (!ubuf || !ppos) + return 0; + + for (i = (((int) *ppos / BYTES_PER_LINE) + SWR_MSTR_START_REG_ADDR); + i <= SWR_MSTR_MAX_REG_ADDR; i += 4) { + reg_val = dbgswrm->read(dbgswrm->handle, i); + len = snprintf(tmp_buf, 25, "0x%.3x: 0x%.2x\n", i, reg_val); + if ((total + len) >= count - 1) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + pr_err("%s: fail to copy reg dump\n", __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } + +copy_err: + return total; +} + +static ssize_t swrm_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char lbuf[SWR_MSTR_RD_BUF_LEN]; + char *access_str; + ssize_t ret_cnt; + + if (!count || !file || !ppos || !ubuf) + return -EINVAL; + + access_str = file->private_data; + if (*ppos < 0) + return -EINVAL; + + if (!strcmp(access_str, "swrm_peek")) { + snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data); + ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf, + strnlen(lbuf, 7)); + } else if (!strcmp(access_str, "swrm_reg_dump")) { + ret_cnt = swrm_reg_show(ubuf, count, ppos); + } else { + pr_err("%s: %s not permitted to read\n", __func__, access_str); + ret_cnt = -EPERM; + } + return ret_cnt; +} + +static ssize_t swrm_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char lbuf[SWR_MSTR_WR_BUF_LEN]; + int rc; + u32 param[5]; + char *access_str; + + if (!filp || !ppos || !ubuf) + return -EINVAL; + + access_str = filp->private_data; + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + if (!strcmp(access_str, "swrm_poke")) { + /* write */ + rc = get_parameters(lbuf, param, 2); + if ((param[0] <= SWR_MSTR_MAX_REG_ADDR) && + (param[1] <= 0xFFFFFFFF) && + (rc == 0)) + rc = dbgswrm->write(dbgswrm->handle, param[0], + param[1]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "swrm_peek")) { + /* read */ + rc = get_parameters(lbuf, param, 1); + if ((param[0] <= SWR_MSTR_MAX_REG_ADDR) && (rc == 0)) + read_data = dbgswrm->read(dbgswrm->handle, param[0]); + else + rc = -EINVAL; + } + if (rc == 0) + rc = cnt; + else + pr_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations swrm_debug_ops = { + .open = swrm_debug_open, + .write = swrm_debug_write, + .read = swrm_debug_read, +}; + +static int swrm_set_ch_map(struct swr_mstr_ctrl *swrm, void *data) +{ + struct swr_mstr_port *pinfo = (struct swr_mstr_port *)data; + + swrm->mstr_port = kzalloc(sizeof(struct swr_mstr_port), GFP_KERNEL); + if (swrm->mstr_port == NULL) + return -ENOMEM; + swrm->mstr_port->num_port = pinfo->num_port; + swrm->mstr_port->port = kzalloc((pinfo->num_port * sizeof(u8)), + GFP_KERNEL); + if (!swrm->mstr_port->port) { + kfree(swrm->mstr_port); + swrm->mstr_port = NULL; + return -ENOMEM; + } + memcpy(swrm->mstr_port->port, pinfo->port, pinfo->num_port); + return 0; +} + +static bool swrm_is_port_en(struct swr_master *mstr) +{ + return !!(mstr->num_port); +} + +static int swrm_clk_request(struct swr_mstr_ctrl *swrm, bool enable) +{ + if (!swrm->clk || !swrm->handle) + return -EINVAL; + + if (enable) { + swrm->clk(swrm->handle, true); + swrm->state = SWR_MSTR_UP; + } else { + swrm->clk(swrm->handle, false); + swrm->state = SWR_MSTR_DOWN; + } + return 0; +} + +static int swrm_get_port_config(struct swr_master *master) +{ + u32 ch_rate = 0; + u32 num_ch = 0; + int i, uc_idx; + u32 portcount = 0; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + if (master->port[i].port_en) { + ch_rate += master->port[i].ch_rate; + num_ch += master->port[i].num_ch; + portcount++; + } + } + for (i = 0; i < ARRAY_SIZE(uc); i++) { + if ((uc[i].num_port == portcount) && + (uc[i].num_ch == num_ch) && + (uc[i].chrate == ch_rate)) { + uc_idx = i; + break; + } + } + + if (i >= ARRAY_SIZE(uc)) { + dev_err(&master->dev, + "%s: usecase port:%d, num_ch:%d, chrate:%d not found\n", + __func__, master->num_port, num_ch, ch_rate); + return -EINVAL; + } + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + if (master->port[i].port_en) { + master->port[i].sinterval = pp[uc_idx][i].si; + master->port[i].offset1 = pp[uc_idx][i].off1; + master->port[i].offset2 = pp[uc_idx][i].off2; + } + } + return 0; +} + +static int swrm_get_master_port(u8 *mstr_port_id, u8 slv_port_id) +{ + int i; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + if (mstr_ports[i] == slv_port_id) { + *mstr_port_id = i; + return 0; + } + } + return -EINVAL; +} + +static u32 swrm_get_packed_reg_val(u8 *cmd_id, u8 cmd_data, + u8 dev_addr, u16 reg_addr) +{ + u32 val; + u8 id = *cmd_id; + + if (id != SWR_BROADCAST_CMD_ID) { + if (id < 14) + id += 1; + else + id = 0; + *cmd_id = id; + } + val = SWR_REG_VAL_PACK(cmd_data, dev_addr, id, reg_addr); + + return val; +} + +static int swrm_cmd_fifo_rd_cmd(struct swr_mstr_ctrl *swrm, int *cmd_data, + u8 dev_addr, u8 cmd_id, u16 reg_addr, + u32 len) +{ + u32 val; + int ret = 0; + + val = swrm_get_packed_reg_val(&swrm->rcmd_id, len, dev_addr, reg_addr); + ret = swrm->write(swrm->handle, SWRM_CMD_FIFO_RD_CMD, val); + if (ret < 0) { + dev_err(swrm->dev, "%s: reg 0x%x write failed, err:%d\n", + __func__, val, ret); + goto err; + } + *cmd_data = swrm->read(swrm->handle, SWRM_CMD_FIFO_RD_FIFO_ADDR); + dev_dbg(swrm->dev, + "%s: reg: 0x%x, cmd_id: 0x%x, dev_id: 0x%x, cmd_data: 0x%x\n", + __func__, reg_addr, cmd_id, dev_addr, *cmd_data); +err: + return ret; +} + +static int swrm_cmd_fifo_wr_cmd(struct swr_mstr_ctrl *swrm, u8 cmd_data, + u8 dev_addr, u8 cmd_id, u16 reg_addr) +{ + u32 val; + int ret = 0; + + if (!cmd_id) + val = swrm_get_packed_reg_val(&swrm->wcmd_id, cmd_data, + dev_addr, reg_addr); + else + val = swrm_get_packed_reg_val(&cmd_id, cmd_data, + dev_addr, reg_addr); + + dev_dbg(swrm->dev, + "%s: reg: 0x%x, cmd_id: 0x%x, dev_id: 0x%x, cmd_data: 0x%x\n", + __func__, reg_addr, cmd_id, dev_addr, cmd_data); + ret = swrm->write(swrm->handle, SWRM_CMD_FIFO_WR_CMD, val); + if (ret < 0) { + dev_err(swrm->dev, "%s: reg 0x%x write failed, err:%d\n", + __func__, val, ret); + goto err; + } + if (cmd_id == 0xF) + wait_for_completion_timeout(&swrm->broadcast, (2 * HZ/10)); +err: + return ret; +} + +static int swrm_read(struct swr_master *master, u8 dev_num, u16 reg_addr, + void *buf, u32 len) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int ret = 0; + int val; + u8 *reg_val = (u8 *)buf; + + if (!swrm) { + dev_err(&master->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + + if (dev_num) + ret = swrm_cmd_fifo_rd_cmd(swrm, &val, dev_num, 0, reg_addr, + len); + else + val = swrm->read(swrm->handle, reg_addr); + + *reg_val = (u8)val; + pm_runtime_mark_last_busy(&swrm->pdev->dev); + + return ret; +} + +static int swrm_write(struct swr_master *master, u8 dev_num, u16 reg_addr, + const void *buf) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int ret = 0; + u8 reg_val = *(u8 *)buf; + + if (!swrm) { + dev_err(&master->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + + if (dev_num) + ret = swrm_cmd_fifo_wr_cmd(swrm, reg_val, dev_num, 0, reg_addr); + else + ret = swrm->write(swrm->handle, reg_addr, reg_val); + + pm_runtime_mark_last_busy(&swrm->pdev->dev); + + return ret; +} + +static int swrm_bulk_write(struct swr_master *master, u8 dev_num, void *reg, + const void *buf, size_t len) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int ret = 0; + int i; + u32 *val; + u32 *swr_fifo_reg; + + if (!swrm || !swrm->handle) { + dev_err(&master->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + if (len <= 0) + return -EINVAL; + + if (dev_num) { + swr_fifo_reg = kcalloc(len, sizeof(u32), GFP_KERNEL); + if (!swr_fifo_reg) { + ret = -ENOMEM; + goto err; + } + val = kcalloc(len, sizeof(u32), GFP_KERNEL); + if (!val) { + ret = -ENOMEM; + goto mem_fail; + } + + for (i = 0; i < len; i++) { + val[i] = swrm_get_packed_reg_val(&swrm->wcmd_id, + ((u8 *)buf)[i], + dev_num, + ((u16 *)reg)[i]); + swr_fifo_reg[i] = SWRM_CMD_FIFO_WR_CMD; + } + ret = swrm->bulk_write(swrm->handle, swr_fifo_reg, val, len); + if (ret) { + dev_err(&master->dev, "%s: bulk write failed\n", + __func__); + ret = -EINVAL; + } + } else { + dev_err(&master->dev, + "%s: No support of Bulk write for master regs\n", + __func__); + ret = -EINVAL; + goto err; + } + kfree(val); +mem_fail: + kfree(swr_fifo_reg); +err: + pm_runtime_mark_last_busy(&swrm->pdev->dev); + return ret; +} + +static u8 get_inactive_bank_num(struct swr_mstr_ctrl *swrm) +{ + return (swrm->read(swrm->handle, SWRM_MCP_STATUS) & + SWRM_MCP_STATUS_BANK_NUM_MASK) ? 0 : 1; +} + +static void enable_bank_switch(struct swr_mstr_ctrl *swrm, u8 bank, + u8 row, u8 col) +{ + swrm_cmd_fifo_wr_cmd(swrm, ((row << 3) | col), 0xF, 0xF, + SWRS_SCP_FRAME_CTRL_BANK(bank)); +} + +static struct swr_port_info *swrm_get_port(struct swr_master *master, + u8 port_id) +{ + int i; + struct swr_port_info *port = NULL; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + port = &master->port[i]; + if (port->port_id == port_id) { + dev_dbg(&master->dev, "%s: port_id: %d, index: %d\n", + __func__, port_id, i); + return port; + } + } + + return NULL; +} + +static struct swr_port_info *swrm_get_avail_port(struct swr_master *master) +{ + int i; + struct swr_port_info *port = NULL; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + port = &master->port[i]; + if (port->port_en) + continue; + + dev_dbg(&master->dev, "%s: port_id: %d, index: %d\n", + __func__, port->port_id, i); + return port; + } + + return NULL; +} + +static struct swr_port_info *swrm_get_enabled_port(struct swr_master *master, + u8 port_id) +{ + int i; + struct swr_port_info *port = NULL; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + port = &master->port[i]; + if ((port->port_id == port_id) && (port->port_en == true)) + break; + } + if (i == SWR_MSTR_PORT_LEN) + port = NULL; + return port; +} + +static bool swrm_remove_from_group(struct swr_master *master) +{ + struct swr_device *swr_dev; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + bool is_removed = false; + + if (!swrm) + goto end; + + mutex_lock(&swrm->mlock); + if ((swrm->num_rx_chs > 1) && + (swrm->num_rx_chs == swrm->num_cfg_devs)) { + list_for_each_entry(swr_dev, &master->devices, + dev_list) { + swr_dev->group_id = SWR_GROUP_NONE; + master->gr_sid = 0; + } + is_removed = true; + } + mutex_unlock(&swrm->mlock); + +end: + return is_removed; +} + +static void swrm_cleanup_disabled_data_ports(struct swr_master *master, + u8 bank) +{ + u32 value; + struct swr_port_info *port; + int i; + int port_type; + struct swrm_mports *mport, *mport_next = NULL; + int port_disable_cnt = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return; + } + + dev_dbg(swrm->dev, "%s: master num_port: %d\n", __func__, + master->num_port); + + mport = list_first_entry_or_null(&swrm->mport_list, + struct swrm_mports, + list); + if (!mport) { + dev_err(swrm->dev, "%s: list is empty\n", __func__); + return; + } + + for (i = 0; i < master->num_port; i++) { + port = swrm_get_port(master, mstr_ports[mport->id]); + if (!port || port->ch_en) + goto inc_loop; + + port_disable_cnt++; + port_type = mstr_port_type[mport->id]; + value = ((port->ch_en) + << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); + value |= ((port->offset2) + << SWRM_DP_PORT_CTRL_OFFSET2_SHFT); + value |= ((port->offset1) + << SWRM_DP_PORT_CTRL_OFFSET1_SHFT); + value |= port->sinterval; + + swrm->write(swrm->handle, + SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank), + value); + swrm_cmd_fifo_wr_cmd(swrm, 0x00, port->dev_id, 0x00, + SWRS_DP_CHANNEL_ENABLE_BANK(port_type, bank)); + + dev_dbg(swrm->dev, "%s: mport :%d, reg: 0x%x, val: 0x%x\n", + __func__, mport->id, + (SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank)), value); + +inc_loop: + mport_next = list_next_entry(mport, list); + if (port && !port->ch_en) { + list_del(&mport->list); + kfree(mport); + } + if (!mport_next) { + dev_err(swrm->dev, "%s: end of list\n", __func__); + break; + } + mport = mport_next; + } + master->num_port -= port_disable_cnt; + + dev_dbg(swrm->dev, "%s:disable ports: %d, active ports (rem): %d\n", + __func__, port_disable_cnt, master->num_port); +} + +static void swrm_slvdev_datapath_control(struct swr_master *master, + bool enable) +{ + u8 bank; + u32 value, n_col; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int mask = (SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK | + SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK | + SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_BMSK); + u8 inactive_bank; + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return; + } + + bank = get_inactive_bank_num(swrm); + + dev_dbg(swrm->dev, "%s: enable: %d, cfg_devs: %d\n", + __func__, enable, swrm->num_cfg_devs); + + if (enable) { + /* set Row = 48 and col = 16 */ + n_col = SWR_MAX_COL; + } else { + /* + * Do not change to 48x2 if number of channels configured + * as stereo and if disable datapath is called for the + * first slave device + */ + if (swrm->num_cfg_devs > 0) + n_col = SWR_MAX_COL; + else + n_col = SWR_MIN_COL; + + /* + * All ports are already disabled, no need to perform + * bank-switch and copy operation. This case can arise + * when speaker channels are enabled in stereo mode with + * BROADCAST and disabled in GROUP_NONE + */ + if (master->num_port == 0) + return; + } + + value = swrm->read(swrm->handle, SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank)); + value &= (~mask); + value |= ((0 << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT) | + (n_col << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT) | + (0 << SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT)); + swrm->write(swrm->handle, SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank), value); + + dev_dbg(swrm->dev, "%s: regaddr: 0x%x, value: 0x%x\n", __func__, + SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank), value); + + enable_bank_switch(swrm, bank, SWR_MAX_ROW, n_col); + + inactive_bank = bank ? 0 : 1; + if (enable) + swrm_copy_data_port_config(master, inactive_bank); + else + swrm_cleanup_disabled_data_ports(master, inactive_bank); + + if (!swrm_is_port_en(master)) { + dev_dbg(&master->dev, "%s: pm_runtime auto suspend triggered\n", + __func__); + pm_runtime_mark_last_busy(&swrm->pdev->dev); + pm_runtime_put_autosuspend(&swrm->pdev->dev); + } +} + +static void swrm_apply_port_config(struct swr_master *master) +{ + u8 bank; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: Invalid handle to swr controller\n", + __func__); + return; + } + + bank = get_inactive_bank_num(swrm); + dev_dbg(swrm->dev, "%s: enter bank: %d master_ports: %d\n", + __func__, bank, master->num_port); + + + swrm_cmd_fifo_wr_cmd(swrm, 0x01, 0xF, 0x00, + SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(bank)); + + swrm_copy_data_port_config(master, bank); +} + +static void swrm_copy_data_port_config(struct swr_master *master, u8 bank) +{ + u32 value; + struct swr_port_info *port; + int i; + int port_type; + struct swrm_mports *mport; + u32 reg[SWRM_MAX_PORT_REG]; + u32 val[SWRM_MAX_PORT_REG]; + int len = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return; + } + + dev_dbg(swrm->dev, "%s: master num_port: %d\n", __func__, + master->num_port); + + mport = list_first_entry_or_null(&swrm->mport_list, + struct swrm_mports, + list); + if (!mport) { + dev_err(swrm->dev, "%s: list is empty\n", __func__); + return; + } + for (i = 0; i < master->num_port; i++) { + + port = swrm_get_enabled_port(master, mstr_ports[mport->id]); + if (!port) + continue; + port_type = mstr_port_type[mport->id]; + if (!port->dev_id || (port->dev_id > master->num_dev)) { + dev_dbg(swrm->dev, "%s: invalid device id = %d\n", + __func__, port->dev_id); + continue; + } + value = ((port->ch_en) + << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); + value |= ((port->offset2) + << SWRM_DP_PORT_CTRL_OFFSET2_SHFT); + value |= ((port->offset1) + << SWRM_DP_PORT_CTRL_OFFSET1_SHFT); + value |= port->sinterval; + + reg[len] = SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank); + val[len++] = value; + + dev_dbg(swrm->dev, "%s: mport :%d, reg: 0x%x, val: 0x%x\n", + __func__, mport->id, + (SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank)), value); + + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port->ch_en, port->dev_id, 0x00, + SWRS_DP_CHANNEL_ENABLE_BANK(port_type, bank)); + + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port->sinterval, + port->dev_id, 0x00, + SWRS_DP_SAMPLE_CONTROL_1_BANK(port_type, bank)); + + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port->offset1, + port->dev_id, 0x00, + SWRS_DP_OFFSET_CONTROL_1_BANK(port_type, bank)); + + if (port_type != 0) { + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port->offset2, + port->dev_id, 0x00, + SWRS_DP_OFFSET_CONTROL_2_BANK(port_type, + bank)); + } + mport = list_next_entry(mport, list); + if (!mport) { + dev_err(swrm->dev, "%s: end of list\n", __func__); + break; + } + } + swrm->bulk_write(swrm->handle, reg, val, len); +} + +static int swrm_connect_port(struct swr_master *master, + struct swr_params *portinfo) +{ + int i; + struct swr_port_info *port; + int ret = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + struct swrm_mports *mport; + struct list_head *ptr, *next; + + dev_dbg(&master->dev, "%s: enter\n", __func__); + if (!portinfo) + return -EINVAL; + + if (!swrm) { + dev_err(&master->dev, + "%s: Invalid handle to swr controller\n", + __func__); + return -EINVAL; + } + + mutex_lock(&swrm->mlock); + if (!swrm_is_port_en(master)) + pm_runtime_get_sync(&swrm->pdev->dev); + + for (i = 0; i < portinfo->num_port; i++) { + mport = kzalloc(sizeof(struct swrm_mports), GFP_KERNEL); + if (!mport) { + ret = -ENOMEM; + goto mem_fail; + } + ret = swrm_get_master_port(&mport->id, + portinfo->port_id[i]); + if (ret < 0) { + dev_err(&master->dev, + "%s: mstr portid for slv port %d not found\n", + __func__, portinfo->port_id[i]); + goto port_fail; + } + port = swrm_get_avail_port(master); + if (!port) { + dev_err(&master->dev, + "%s: avail ports not found!\n", __func__); + goto port_fail; + } + list_add(&mport->list, &swrm->mport_list); + port->dev_id = portinfo->dev_id; + port->port_id = portinfo->port_id[i]; + port->num_ch = portinfo->num_ch[i]; + port->ch_rate = portinfo->ch_rate[i]; + port->ch_en = portinfo->ch_en[i]; + port->port_en = true; + dev_dbg(&master->dev, + "%s: mstr port %d, slv port %d ch_rate %d num_ch %d\n", + __func__, mport->id, port->port_id, port->ch_rate, + port->num_ch); + } + master->num_port += portinfo->num_port; + if (master->num_port >= SWR_MSTR_PORT_LEN) + master->num_port = SWR_MSTR_PORT_LEN; + + swrm_get_port_config(master); + swr_port_response(master, portinfo->tid); + swrm->num_cfg_devs += 1; + dev_dbg(&master->dev, "%s: cfg_devs: %d, rx_chs: %d\n", + __func__, swrm->num_cfg_devs, swrm->num_rx_chs); + if (swrm->num_rx_chs > 1) { + if (swrm->num_rx_chs == swrm->num_cfg_devs) + swrm_apply_port_config(master); + } else { + swrm_apply_port_config(master); + } + mutex_unlock(&swrm->mlock); + return 0; + +port_fail: + kfree(mport); +mem_fail: + list_for_each_safe(ptr, next, &swrm->mport_list) { + mport = list_entry(ptr, struct swrm_mports, list); + for (i = 0; i < portinfo->num_port; i++) { + if (portinfo->port_id[i] == mstr_ports[mport->id]) { + port = swrm_get_port(master, + portinfo->port_id[i]); + if (port) + port->ch_en = false; + list_del(&mport->list); + kfree(mport); + break; + } + } + } + mutex_unlock(&swrm->mlock); + return ret; +} + +static int swrm_disconnect_port(struct swr_master *master, + struct swr_params *portinfo) +{ + int i; + struct swr_port_info *port; + u8 bank; + u32 value; + int ret = 0; + u8 mport_id = 0; + int port_type = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + dev_err(&master->dev, + "%s: Invalid handle to swr controller\n", + __func__); + return -EINVAL; + } + + if (!portinfo) { + dev_err(&master->dev, "%s: portinfo is NULL\n", __func__); + return -EINVAL; + } + mutex_lock(&swrm->mlock); + bank = get_inactive_bank_num(swrm); + for (i = 0; i < portinfo->num_port; i++) { + ret = swrm_get_master_port(&mport_id, + portinfo->port_id[i]); + if (ret < 0) { + dev_err(&master->dev, + "%s: mstr portid for slv port %d not found\n", + __func__, portinfo->port_id[i]); + mutex_unlock(&swrm->mlock); + return -EINVAL; + } + port = swrm_get_enabled_port(master, portinfo->port_id[i]); + if (!port) { + dev_dbg(&master->dev, "%s: port %d already disabled\n", + __func__, portinfo->port_id[i]); + continue; + } + port_type = mstr_port_type[mport_id]; + port->dev_id = portinfo->dev_id; + port->port_en = false; + port->ch_en = 0; + value = port->ch_en << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT; + value |= (port->offset2 << SWRM_DP_PORT_CTRL_OFFSET2_SHFT); + value |= (port->offset1 << SWRM_DP_PORT_CTRL_OFFSET1_SHFT); + value |= port->sinterval; + + + swrm->write(swrm->handle, + SWRM_DP_PORT_CTRL_BANK((mport_id+1), bank), + value); + swrm_cmd_fifo_wr_cmd(swrm, 0x00, port->dev_id, 0x00, + SWRS_DP_CHANNEL_ENABLE_BANK(port_type, bank)); + } + + swr_port_response(master, portinfo->tid); + swrm->num_cfg_devs -= 1; + dev_dbg(&master->dev, "%s: cfg_devs: %d, rx_chs: %d, active ports: %d\n", + __func__, swrm->num_cfg_devs, swrm->num_rx_chs, + master->num_port); + mutex_unlock(&swrm->mlock); + + return 0; +} + +static int swrm_check_slave_change_status(struct swr_mstr_ctrl *swrm, + int status, u8 *devnum) +{ + int i; + int new_sts = status; + int ret = SWR_NOT_PRESENT; + + if (status != swrm->slave_status) { + for (i = 0; i < (swrm->master.num_dev + 1); i++) { + if ((status & SWRM_MCP_SLV_STATUS_MASK) != + (swrm->slave_status & SWRM_MCP_SLV_STATUS_MASK)) { + ret = (status & SWRM_MCP_SLV_STATUS_MASK); + *devnum = i; + break; + } + status >>= 2; + swrm->slave_status >>= 2; + } + swrm->slave_status = new_sts; + } + return ret; +} + +static irqreturn_t swr_mstr_interrupt(int irq, void *dev) +{ + struct swr_mstr_ctrl *swrm = dev; + u32 value, intr_sts; + int status, chg_sts, i; + u8 devnum = 0; + int ret = IRQ_HANDLED; + + pm_runtime_get_sync(&swrm->pdev->dev); + intr_sts = swrm->read(swrm->handle, SWRM_INTERRUPT_STATUS); + intr_sts &= SWRM_INTERRUPT_STATUS_RMSK; + for (i = 0; i < SWRM_INTERRUPT_MAX; i++) { + value = intr_sts & (1 << i); + if (!value) + continue; + + swrm->write(swrm->handle, SWRM_INTERRUPT_CLEAR, value); + switch (value) { + case SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ: + dev_dbg(swrm->dev, "SWR slave pend irq\n"); + break; + case SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED: + dev_dbg(swrm->dev, "SWR new slave attached\n"); + break; + case SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS: + status = swrm->read(swrm->handle, SWRM_MCP_SLV_STATUS); + if (status == swrm->slave_status) { + dev_dbg(swrm->dev, + "%s: No change in slave status: %d\n", + __func__, status); + break; + } + chg_sts = swrm_check_slave_change_status(swrm, status, + &devnum); + switch (chg_sts) { + case SWR_NOT_PRESENT: + dev_dbg(swrm->dev, "device %d got detached\n", + devnum); + break; + case SWR_ATTACHED_OK: + dev_dbg(swrm->dev, "device %d got attached\n", + devnum); + break; + case SWR_ALERT: + dev_dbg(swrm->dev, + "device %d has pending interrupt\n", + devnum); + break; + } + break; + case SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET: + dev_err_ratelimited(swrm->dev, "SWR bus clash detected\n"); + break; + case SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW: + dev_dbg(swrm->dev, "SWR read FIFO overflow\n"); + break; + case SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW: + dev_dbg(swrm->dev, "SWR read FIFO underflow\n"); + break; + case SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW: + dev_dbg(swrm->dev, "SWR write FIFO overflow\n"); + break; + case SWRM_INTERRUPT_STATUS_CMD_ERROR: + value = swrm->read(swrm->handle, SWRM_CMD_FIFO_STATUS); + dev_err_ratelimited(swrm->dev, + "SWR CMD error, fifo status 0x%x, flushing fifo\n", + value); + swrm->write(swrm->handle, SWRM_CMD_FIFO_CMD, 0x1); + break; + case SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION: + dev_dbg(swrm->dev, "SWR Port collision detected\n"); + break; + case SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH: + dev_dbg(swrm->dev, "SWR read enable valid mismatch\n"); + break; + case SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED: + complete(&swrm->broadcast); + dev_dbg(swrm->dev, "SWR cmd id finished\n"); + break; + case SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED: + break; + case SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED: + break; + case SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL: + break; + case SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED: + complete(&swrm->reset); + break; + case SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED: + break; + default: + dev_err_ratelimited(swrm->dev, "SWR unknown interrupt\n"); + ret = IRQ_NONE; + break; + } + } + pm_runtime_mark_last_busy(&swrm->pdev->dev); + pm_runtime_put_autosuspend(&swrm->pdev->dev); + return ret; +} + +static int swrm_get_device_status(struct swr_mstr_ctrl *swrm, u8 devnum) +{ + u32 val; + + swrm->slave_status = swrm->read(swrm->handle, SWRM_MCP_SLV_STATUS); + val = (swrm->slave_status >> (devnum * 2)); + val &= SWRM_MCP_SLV_STATUS_MASK; + return val; +} + +static int swrm_get_logical_dev_num(struct swr_master *mstr, u64 dev_id, + u8 *dev_num) +{ + int i; + u64 id = 0; + int ret = -EINVAL; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(mstr); + + if (!swrm) { + pr_err("%s: Invalid handle to swr controller\n", + __func__); + return ret; + } + + pm_runtime_get_sync(&swrm->pdev->dev); + for (i = 1; i < (mstr->num_dev + 1); i++) { + id = ((u64)(swrm->read(swrm->handle, + SWRM_ENUMERATOR_SLAVE_DEV_ID_2(i))) << 32); + id |= swrm->read(swrm->handle, + SWRM_ENUMERATOR_SLAVE_DEV_ID_1(i)); + if ((id & SWR_DEV_ID_MASK) == dev_id) { + if (swrm_get_device_status(swrm, i) == 0x01) { + *dev_num = i; + ret = 0; + } else { + dev_err(swrm->dev, "%s: device is not ready\n", + __func__); + } + goto found; + } + } + dev_err(swrm->dev, "%s: device id 0x%llx does not match with 0x%llx\n", + __func__, id, dev_id); +found: + pm_runtime_mark_last_busy(&swrm->pdev->dev); + pm_runtime_put_autosuspend(&swrm->pdev->dev); + return ret; +} +static int swrm_master_init(struct swr_mstr_ctrl *swrm) +{ + int ret = 0; + u32 val; + u8 row_ctrl = SWR_MAX_ROW; + u8 col_ctrl = SWR_MIN_COL; + u8 ssp_period = 1; + u8 retry_cmd_num = 3; + u32 reg[SWRM_MAX_INIT_REG]; + u32 value[SWRM_MAX_INIT_REG]; + int len = 0; + + /* Clear Rows and Cols */ + val = ((row_ctrl << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT) | + (col_ctrl << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT) | + (ssp_period << SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT)); + + reg[len] = SWRM_MCP_FRAME_CTRL_BANK_ADDR(0); + value[len++] = val; + + /* Set Auto enumeration flag */ + reg[len] = SWRM_ENUMERATOR_CFG_ADDR; + value[len++] = 1; + + /* Mask soundwire interrupts */ + reg[len] = SWRM_INTERRUPT_MASK_ADDR; + value[len++] = 0x1FFFD; + + /* Configure No pings */ + val = swrm->read(swrm->handle, SWRM_MCP_CFG_ADDR); + val &= ~SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK; + val |= (0x1f << SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT); + reg[len] = SWRM_MCP_CFG_ADDR; + value[len++] = val; + + /* Configure number of retries of a read/write cmd */ + val = (retry_cmd_num << SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT); + reg[len] = SWRM_CMD_FIFO_CFG_ADDR; + value[len++] = val; + + /* Set IRQ to PULSE */ + reg[len] = SWRM_COMP_CFG_ADDR; + value[len++] = 0x02; + + reg[len] = SWRM_COMP_CFG_ADDR; + value[len++] = 0x03; + + reg[len] = SWRM_INTERRUPT_CLEAR; + value[len++] = 0x08; + + swrm->bulk_write(swrm->handle, reg, value, len); + + return ret; +} + +static int swrm_probe(struct platform_device *pdev) +{ + struct swr_mstr_ctrl *swrm; + struct swr_ctrl_platform_data *pdata; + struct swr_device *swr_dev, *safe; + int ret; + + /* Allocate soundwire master driver structure */ + swrm = kzalloc(sizeof(struct swr_mstr_ctrl), GFP_KERNEL); + if (!swrm) { + ret = -ENOMEM; + goto err_memory_fail; + } + swrm->dev = &pdev->dev; + swrm->pdev = pdev; + platform_set_drvdata(pdev, swrm); + swr_set_ctrl_data(&swrm->master, swrm); + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "%s: pdata from parent is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->handle = (void *)pdata->handle; + if (!swrm->handle) { + dev_err(&pdev->dev, "%s: swrm->handle is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->read = pdata->read; + if (!swrm->read) { + dev_err(&pdev->dev, "%s: swrm->read is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->write = pdata->write; + if (!swrm->write) { + dev_err(&pdev->dev, "%s: swrm->write is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->bulk_write = pdata->bulk_write; + if (!swrm->bulk_write) { + dev_err(&pdev->dev, "%s: swrm->bulk_write is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->clk = pdata->clk; + if (!swrm->clk) { + dev_err(&pdev->dev, "%s: swrm->clk is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->reg_irq = pdata->reg_irq; + if (!swrm->reg_irq) { + dev_err(&pdev->dev, "%s: swrm->reg_irq is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->master.read = swrm_read; + swrm->master.write = swrm_write; + swrm->master.bulk_write = swrm_bulk_write; + swrm->master.get_logical_dev_num = swrm_get_logical_dev_num; + swrm->master.connect_port = swrm_connect_port; + swrm->master.disconnect_port = swrm_disconnect_port; + swrm->master.slvdev_datapath_control = swrm_slvdev_datapath_control; + swrm->master.remove_from_group = swrm_remove_from_group; + swrm->master.dev.parent = &pdev->dev; + swrm->master.dev.of_node = pdev->dev.of_node; + swrm->master.num_port = 0; + swrm->num_enum_slaves = 0; + swrm->rcmd_id = 0; + swrm->wcmd_id = 0; + swrm->slave_status = 0; + swrm->num_rx_chs = 0; + swrm->state = SWR_MSTR_RESUME; + init_completion(&swrm->reset); + init_completion(&swrm->broadcast); + mutex_init(&swrm->mlock); + INIT_LIST_HEAD(&swrm->mport_list); + mutex_init(&swrm->reslock); + + ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm, + SWR_IRQ_REGISTER); + if (ret) { + dev_err(&pdev->dev, "%s: IRQ register failed ret %d\n", + __func__, ret); + goto err_irq_fail; + } + + ret = swr_register_master(&swrm->master); + if (ret) { + dev_err(&pdev->dev, "%s: error adding swr master\n", __func__); + goto err_mstr_fail; + } + + if (pdev->dev.of_node) + of_register_swr_devices(&swrm->master); + + /* Add devices registered with board-info as the + * controller will be up now + */ + swr_master_add_boarddevices(&swrm->master); + mutex_lock(&swrm->mlock); + swrm_clk_request(swrm, true); + ret = swrm_master_init(swrm); + if (ret < 0) { + dev_err(&pdev->dev, + "%s: Error in master Initializaiton, err %d\n", + __func__, ret); + mutex_unlock(&swrm->mlock); + goto err_mstr_fail; + } + + /* Enumerate slave devices */ + list_for_each_entry_safe(swr_dev, safe, &swrm->master.devices, + dev_list) { + ret = swr_startup_devices(swr_dev); + if (ret) + list_del(&swr_dev->dev_list); + } + mutex_unlock(&swrm->mlock); + + dbgswrm = swrm; + debugfs_swrm_dent = debugfs_create_dir(dev_name(&pdev->dev), 0); + if (!IS_ERR(debugfs_swrm_dent)) { + debugfs_peek = debugfs_create_file("swrm_peek", + S_IFREG | 0444, debugfs_swrm_dent, + (void *) "swrm_peek", &swrm_debug_ops); + + debugfs_poke = debugfs_create_file("swrm_poke", + S_IFREG | 0444, debugfs_swrm_dent, + (void *) "swrm_poke", &swrm_debug_ops); + + debugfs_reg_dump = debugfs_create_file("swrm_reg_dump", + S_IFREG | 0444, debugfs_swrm_dent, + (void *) "swrm_reg_dump", + &swrm_debug_ops); + } + pm_runtime_set_autosuspend_delay(&pdev->dev, auto_suspend_timer); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_mark_last_busy(&pdev->dev); + + return 0; +err_mstr_fail: + swrm->reg_irq(swrm->handle, swr_mstr_interrupt, + swrm, SWR_IRQ_FREE); +err_irq_fail: +err_pdata_fail: + kfree(swrm); +err_memory_fail: + return ret; +} + +static int swrm_remove(struct platform_device *pdev) +{ + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + + swrm->reg_irq(swrm->handle, swr_mstr_interrupt, + swrm, SWR_IRQ_FREE); + if (swrm->mstr_port) { + kfree(swrm->mstr_port->port); + swrm->mstr_port->port = NULL; + kfree(swrm->mstr_port); + swrm->mstr_port = NULL; + } + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + swr_unregister_master(&swrm->master); + mutex_destroy(&swrm->mlock); + mutex_destroy(&swrm->reslock); + kfree(swrm); + return 0; +} + +static int swrm_clk_pause(struct swr_mstr_ctrl *swrm) +{ + u32 val; + + dev_dbg(swrm->dev, "%s: state: %d\n", __func__, swrm->state); + swrm->write(swrm->handle, SWRM_INTERRUPT_MASK_ADDR, 0x1FDFD); + val = swrm->read(swrm->handle, SWRM_MCP_CFG_ADDR); + val |= SWRM_MCP_CFG_BUS_CLK_PAUSE_BMSK; + swrm->write(swrm->handle, SWRM_MCP_CFG_ADDR, val); + swrm->state = SWR_MSTR_PAUSE; + + return 0; +} + +#ifdef CONFIG_PM +static int swrm_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + int ret = 0; + struct swr_master *mstr = &swrm->master; + struct swr_device *swr_dev; + + dev_dbg(dev, "%s: pm_runtime: resume, state:%d\n", + __func__, swrm->state); + mutex_lock(&swrm->reslock); + if ((swrm->state == SWR_MSTR_PAUSE) || + (swrm->state == SWR_MSTR_DOWN)) { + if (swrm->state == SWR_MSTR_DOWN) { + if (swrm_clk_request(swrm, true)) + goto exit; + } + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_device_up(swr_dev); + if (ret) { + dev_err(dev, + "%s: failed to wakeup swr dev %d\n", + __func__, swr_dev->dev_num); + swrm_clk_request(swrm, false); + goto exit; + } + } + swrm->write(swrm->handle, SWRM_COMP_SW_RESET, 0x01); + swrm->write(swrm->handle, SWRM_COMP_SW_RESET, 0x01); + swrm_master_init(swrm); + } +exit: + pm_runtime_set_autosuspend_delay(&pdev->dev, auto_suspend_timer); + mutex_unlock(&swrm->reslock); + return ret; +} + +static int swrm_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + int ret = 0; + struct swr_master *mstr = &swrm->master; + struct swr_device *swr_dev; + + dev_dbg(dev, "%s: pm_runtime: suspend state: %d\n", + __func__, swrm->state); + mutex_lock(&swrm->reslock); + if ((swrm->state == SWR_MSTR_RESUME) || + (swrm->state == SWR_MSTR_UP)) { + if (swrm_is_port_en(&swrm->master)) { + dev_dbg(dev, "%s ports are enabled\n", __func__); + ret = -EBUSY; + goto exit; + } + swrm_clk_pause(swrm); + swrm->write(swrm->handle, SWRM_COMP_CFG_ADDR, 0x00); + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_device_down(swr_dev); + if (ret) { + dev_err(dev, + "%s: failed to shutdown swr dev %d\n", + __func__, swr_dev->dev_num); + goto exit; + } + } + swrm_clk_request(swrm, false); + } +exit: + mutex_unlock(&swrm->reslock); + return ret; +} +#endif /* CONFIG_PM */ + +static int swrm_device_down(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + int ret = 0; + struct swr_master *mstr = &swrm->master; + struct swr_device *swr_dev; + + dev_dbg(dev, "%s: swrm state: %d\n", __func__, swrm->state); + mutex_lock(&swrm->reslock); + if ((swrm->state == SWR_MSTR_RESUME) || + (swrm->state == SWR_MSTR_UP)) { + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_device_down(swr_dev); + if (ret) + dev_err(dev, + "%s: failed to shutdown swr dev %d\n", + __func__, swr_dev->dev_num); + } + dev_dbg(dev, "%s: Shutting down SWRM\n", __func__); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + swrm_clk_request(swrm, false); + } + mutex_unlock(&swrm->reslock); + return ret; +} + +/** + * swrm_wcd_notify - parent device can notify to soundwire master through + * this function + * @pdev: pointer to platform device structure + * @id: command id from parent to the soundwire master + * @data: data from parent device to soundwire master + */ +int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data) +{ + struct swr_mstr_ctrl *swrm; + int ret = 0; + struct swr_master *mstr; + struct swr_device *swr_dev; + + if (!pdev) { + pr_err("%s: pdev is NULL\n", __func__); + return -EINVAL; + } + swrm = platform_get_drvdata(pdev); + if (!swrm) { + dev_err(&pdev->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + mstr = &swrm->master; + + switch (id) { + case SWR_CH_MAP: + if (!data) { + dev_err(swrm->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + } else { + ret = swrm_set_ch_map(swrm, data); + } + break; + case SWR_DEVICE_DOWN: + dev_dbg(swrm->dev, "%s: swr master down called\n", __func__); + mutex_lock(&swrm->mlock); + if ((swrm->state == SWR_MSTR_PAUSE) || + (swrm->state == SWR_MSTR_DOWN)) + dev_dbg(swrm->dev, "%s: SWR master is already Down: %d\n", + __func__, swrm->state); + else + swrm_device_down(&pdev->dev); + mutex_unlock(&swrm->mlock); + break; + case SWR_DEVICE_UP: + dev_dbg(swrm->dev, "%s: swr master up called\n", __func__); + mutex_lock(&swrm->mlock); + mutex_lock(&swrm->reslock); + if ((swrm->state == SWR_MSTR_RESUME) || + (swrm->state == SWR_MSTR_UP)) { + dev_dbg(swrm->dev, "%s: SWR master is already UP: %d\n", + __func__, swrm->state); + } else { + pm_runtime_mark_last_busy(&pdev->dev); + mutex_unlock(&swrm->reslock); + pm_runtime_get_sync(&pdev->dev); + mutex_lock(&swrm->reslock); + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_reset_device(swr_dev); + if (ret) { + dev_err(swrm->dev, + "%s: failed to reset swr device %d\n", + __func__, swr_dev->dev_num); + swrm_clk_request(swrm, false); + } + } + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + } + mutex_unlock(&swrm->reslock); + mutex_unlock(&swrm->mlock); + break; + case SWR_SET_NUM_RX_CH: + if (!data) { + dev_err(swrm->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + } else { + mutex_lock(&swrm->mlock); + swrm->num_rx_chs = *(int *)data; + if ((swrm->num_rx_chs > 1) && !swrm->num_cfg_devs) { + list_for_each_entry(swr_dev, &mstr->devices, + dev_list) { + ret = swr_set_device_group(swr_dev, + SWR_BROADCAST); + if (ret) + dev_err(swrm->dev, + "%s: set num ch failed\n", + __func__); + } + } else { + list_for_each_entry(swr_dev, &mstr->devices, + dev_list) { + ret = swr_set_device_group(swr_dev, + SWR_GROUP_NONE); + if (ret) + dev_err(swrm->dev, + "%s: set num ch failed\n", + __func__); + } + } + mutex_unlock(&swrm->mlock); + } + break; + default: + dev_err(swrm->dev, "%s: swr master unknown id %d\n", + __func__, id); + break; + } + return ret; +} +EXPORT_SYMBOL(swrm_wcd_notify); + +#ifdef CONFIG_PM_SLEEP +static int swrm_suspend(struct device *dev) +{ + int ret = -EBUSY; + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s: system suspend, state: %d\n", __func__, swrm->state); + if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) { + ret = swrm_runtime_suspend(dev); + if (!ret) { + /* + * Synchronize runtime-pm and system-pm states: + * At this point, we are already suspended. If + * runtime-pm still thinks its active, then + * make sure its status is in sync with HW + * status. The three below calls let the + * runtime-pm know that we are suspended + * already without re-invoking the suspend + * callback + */ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + } + } + if (ret == -EBUSY) { + /* + * There is a possibility that some audio stream is active + * during suspend. We dont want to return suspend failure in + * that case so that display and relevant components can still + * go to suspend. + * If there is some other error, then it should be passed-on + * to system level suspend + */ + ret = 0; + } + return ret; +} + +static int swrm_resume(struct device *dev) +{ + int ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s: system resume, state: %d\n", __func__, swrm->state); + if (!pm_runtime_enabled(dev) || !pm_runtime_suspend(dev)) { + ret = swrm_runtime_resume(dev); + if (!ret) { + pm_runtime_mark_last_busy(dev); + pm_request_autosuspend(dev); + } + } + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops swrm_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS( + swrm_suspend, + swrm_resume + ) + SET_RUNTIME_PM_OPS( + swrm_runtime_suspend, + swrm_runtime_resume, + NULL + ) +}; + +static const struct of_device_id swrm_dt_match[] = { + { + .compatible = "qcom,swr-wcd", + }, + {} +}; + +static struct platform_driver swr_mstr_driver = { + .probe = swrm_probe, + .remove = swrm_remove, + .driver = { + .name = SWR_WCD_NAME, + .owner = THIS_MODULE, + .pm = &swrm_dev_pm_ops, + .of_match_table = swrm_dt_match, + }, +}; + +static int __init swrm_init(void) +{ + return platform_driver_register(&swr_mstr_driver); +} +subsys_initcall(swrm_init); + +static void __exit swrm_exit(void) +{ + platform_driver_unregister(&swr_mstr_driver); +} +module_exit(swrm_exit); + + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("WCD SoundWire Controller"); +MODULE_ALIAS("platform:swr-wcd"); diff --git a/drivers/soundwire/swr-wcd-ctrl.h b/drivers/soundwire/swr-wcd-ctrl.h new file mode 100644 index 0000000000000000000000000000000000000000..8992318cdbd3577db1041a4c4a0fbbc6c0094ed3 --- /dev/null +++ b/drivers/soundwire/swr-wcd-ctrl.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _SWR_WCD_CTRL_H +#define _SWR_WCD_CTRL_H +#include +#include + +#define SWR_MAX_ROW 0 /* Rows = 48 */ +#define SWR_MAX_COL 7 /* Cols = 16 */ +#define SWR_MIN_COL 0 /* Cols = 2 */ + +#define SWR_WCD_NAME "swr-wcd" + +#define SWR_MSTR_PORT_LEN 8 /* Number of master ports */ + +enum { + SWR_MSTR_PAUSE, + SWR_MSTR_RESUME, + SWR_MSTR_UP, + SWR_MSTR_DOWN, +}; + +enum { + SWR_IRQ_FREE, + SWR_IRQ_REGISTER, +}; + +enum { + SWR_DAC_PORT, + SWR_COMP_PORT, + SWR_BOOST_PORT, + SWR_VISENSE_PORT, +}; + +struct usecase { + u8 num_port; + u8 num_ch; + u32 chrate; +}; + +struct port_params { + u8 si; + u8 off1; + u8 off2; +}; + +struct swrm_mports { + struct list_head list; + u8 id; +}; + +struct swr_ctrl_platform_data { + void *handle; /* holds priv data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq, + void *data), void *swr_handle, int type); +}; + +struct swr_mstr_ctrl { + struct swr_master master; + struct device *dev; + struct resource *supplies; + struct clk *mclk; + struct completion reset; + struct completion broadcast; + struct mutex mlock; + struct mutex reslock; + u8 rcmd_id; + u8 wcmd_id; + void *handle; /* SWR Master handle from client for read and writes */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq, + void *data), void *swr_handle, int type); + int irq; + int num_enum_slaves; + int slave_status; + struct swr_mstr_port *mstr_port; + struct list_head mport_list; + int state; + struct platform_device *pdev; + int num_rx_chs; + u8 num_cfg_devs; +}; + +#endif /* _SWR_WCD_CTRL_H */ diff --git a/drivers/soundwire/swrm_registers.h b/drivers/soundwire/swrm_registers.h new file mode 100644 index 0000000000000000000000000000000000000000..c6923f301f9f03543ec5d76a05717c433352e34b --- /dev/null +++ b/drivers/soundwire/swrm_registers.h @@ -0,0 +1,203 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _SWRM_REGISTERS_H +#define _SWRM_REGISTERS_H + +#define SWRM_BASE_ADDRESS 0x00 + +#define SWRM_COMP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000004) +#define SWRM_COMP_CFG_RMSK 0x3 +#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_BMSK 0x2 +#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_SHFT 0x1 +#define SWRM_COMP_CFG_ENABLE_BMSK 0x1 +#define SWRM_COMP_CFG_ENABLE_SHFT 0x0 + +#define SWRM_COMP_SW_RESET (SWRM_BASE_ADDRESS+0x00000008) + +#define SWRM_COMP_PARAMS (SWRM_BASE_ADDRESS+0x100) +#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK 0x0000001F +#define SWRM_COMP_PARAMS_DIN_PORTS_MASK 0x000003E0 +#define SWRM_COMP_PARAMS_WR_FIFO_DEPTH 0x00007C00 +#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH 0x000F8000 +#define SWRM_COMP_PARAMS_AUTO_ENUM_SLAVES 0x00F00000 +#define SWRM_COMP_PARAMS_DATA_LANES 0x07000000 + + +#define SWRM_INTERRUPT_STATUS (SWRM_BASE_ADDRESS+0x00000200) +#define SWRM_INTERRUPT_STATUS_RMSK 0x1FFFD + +#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ 0x1 +#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED 0x2 +#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS 0x4 +#define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET 0x8 +#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW 0x10 +#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW 0x20 +#define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW 0x40 +#define SWRM_INTERRUPT_STATUS_CMD_ERROR 0x80 +#define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION 0x100 +#define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH 0x200 +#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED 0x400 +#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED 0x800 +#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED 0x1000 +#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL 0x2000 +#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED 0x4000 +#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED 0x8000 +#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST 0x10000 + +#define SWRM_INTERRUPT_MASK_ADDR (SWRM_BASE_ADDRESS+0x00000204) +#define SWRM_INTERRUPT_MASK_RMSK 0x1FFFF + +#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_BMSK 0x1 +#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_SHFT 0x0 + +#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_BMSK 0x2 +#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_SHFT 0x1 + +#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_BMSK 0x4 +#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_SHFT 0x2 + +#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_BMSK 0x8 +#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_SHFT 0x3 + +#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_BMSK 0x10 +#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_SHFT 0x4 + +#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_BMSK 0x20 +#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_SHFT 0x5 + +#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_BMSK 0x40 +#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_SHFT 0x6 + +#define SWRM_INTERRUPT_MASK_CMD_ERROR_BMSK 0x80 +#define SWRM_INTERRUPT_MASK_CMD_ERROR_SHFT 0x7 + +#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_BMSK 0x100 +#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_SHFT 0x8 + +#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_BMSK 0x200 +#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_SHFT 0x9 + +#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_BMSK 0x400 +#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_SHFT 0xA + +#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_BMSK 0x800 +#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_SHFT 0xB + +#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_BMSK 0x1000 +#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_SHFT 0xC + +#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_BMSK 0x2000 +#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_SHFT 0xD + +#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_BMSK 0x4000 +#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_SHFT 0xE + +#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_BMSK 0x8000 +#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_SHFT 0xF + +#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_BMSK 0x10000 +#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_SHFT 0x10 + +#define SWRM_INTERRUPT_MAX 0x11 + +#define SWRM_INTERRUPT_CLEAR (SWRM_BASE_ADDRESS+0x00000208) + +#define SWRM_CMD_FIFO_WR_CMD (SWRM_BASE_ADDRESS + 0x00000300) +#define SWRM_CMD_FIFO_WR_CMD_MASK 0xFFFFFFFF +#define SWRM_CMD_FIFO_RD_CMD (SWRM_BASE_ADDRESS + 0x00000304) +#define SWRM_CMD_FIFO_RD_CMD_MASK 0xFFFFFFF +#define SWRM_CMD_FIFO_CMD (SWRM_BASE_ADDRESS + 0x00000308) +#define SWRM_CMD_FIFO_STATUS (SWRM_BASE_ADDRESS + 0x0000030C) + +#define SWRM_CMD_FIFO_STATUS_WR_CMD_FIFO_CNT_MASK 0x1F00 +#define SWRM_CMD_FIFO_STATUS_RD_CMD_FIFO_CNT_MASK 0x7C00000 + +#define SWRM_CMD_FIFO_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000314) +#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_BMSK 0x7 +#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT 0x0 + +#define SWRM_CMD_FIFO_RD_FIFO_ADDR (SWRM_BASE_ADDRESS + 0x00000318) + +#define SWRM_ENUMERATOR_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000500) +#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_BMSK 0x1 +#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_SHFT 0x0 + +#define SWRM_ENUMERATOR_SLAVE_DEV_ID_1(m) (SWRM_BASE_ADDRESS+0x530+0x8*m) +#define SWRM_ENUMERATOR_SLAVE_DEV_ID_2(m) (SWRM_BASE_ADDRESS+0x534+0x8*m) + +#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m) (SWRM_BASE_ADDRESS+0x101C+0x40*m) +#define SWRM_MCP_FRAME_CTRL_BANK_RMSK 0x00ff07ff +#define SWRM_MCP_FRAME_CTRL_BANK_SHFT 0 +#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_BMSK 0xff0000 +#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT 16 +#define SWRM_MCP_FRAME_CTRL_BANK_PHASE_BMSK 0xf800 +#define SWRM_MCP_FRAME_CTRL_BANK_PHASE_SHFT 11 +#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_BMSK 0x700 +#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_SHFT 8 +#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK 0xF8 +#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT 3 +#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK 0x7 +#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT 0 + +#define SWRM_MCP_BUS_CTRL_ADDR (SWRM_BASE_ADDRESS+0x00001044) +#define SWRM_MCP_BUS_CTRL_BUS_RESET_BMSK 0x1 +#define SWRM_MCP_BUS_CTRL_BUS_RESET_SHFT 0x0 +#define SWRM_MCP_BUS_CTRL_CLK_START_BMSK 0x2 +#define SWRM_MCP_BUS_CTRL_CLK_START_SHFT 0x1 + +#define SWRM_MCP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00001048) +#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK 0x3E0000 +#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT 0x11 +#define SWRM_MCP_CFG_BUS_CLK_PAUSE_BMSK 0x02 + +#define SWRM_MCP_STATUS (SWRM_BASE_ADDRESS+0x104C) +#define SWRM_MCP_STATUS_BANK_NUM_MASK 0x01 + +#define SWRM_MCP_SLV_STATUS (SWRM_BASE_ADDRESS+0x1090) +#define SWRM_MCP_SLV_STATUS_MASK 0x03 + +#define SWRM_DP_PORT_CTRL_BANK(n, m) (SWRM_BASE_ADDRESS + \ + 0x00001124 + \ + 0x100*(n-1) + \ + 0x40*m) +#define SWRM_DP_PORT_CTRL_BANK_MASK 0xFFFFFFFF +#define SWRM_DP_PORT_CTRL_EN_CHAN_MASK 0xFF000000 +#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_DP_PORT_CTRL_SAMPLE_INTERVAL 0x00 + +/* Soundwire Slave Register definition */ + +#define SWRS_BASE_ADDRESS 0x00 + +#define SWRS_DP_REG_OFFSET(port, bank) ((0x100*port)+(0x10*bank)) + +#define SWRS_DP_CHANNEL_ENABLE_BANK(n, m) (SWRS_BASE_ADDRESS + 0x120 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_SAMPLE_CONTROL_1_BANK(n, m) (SWRS_BASE_ADDRESS + 0x122 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_OFFSET_CONTROL_1_BANK(n, m) (SWRS_BASE_ADDRESS + 0x124 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_OFFSET_CONTROL_2_BANK(n, m) (SWRS_BASE_ADDRESS + 0x125 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_HCONTROL_BANK(n, m) (SWRS_BASE_ADDRESS + 0x126 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_BLOCK_CONTROL_3_BANK(n, m) (SWRS_BASE_ADDRESS + 0x127 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_SCP_FRAME_CTRL_BANK(m) (SWRS_BASE_ADDRESS + 0x60 + \ + 0x10*m) +#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (SWRS_BASE_ADDRESS + 0xE0 + \ + 0x10*m) + +#endif /* _SWRM_REGISTERS_H */ diff --git a/include/linux/mfd/msm-cdc-pinctrl.h b/include/linux/mfd/msm-cdc-pinctrl.h new file mode 100644 index 0000000000000000000000000000000000000000..14b18fe46cc42892b24c5354fd1f2a7f870449b1 --- /dev/null +++ b/include/linux/mfd/msm-cdc-pinctrl.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MFD_CDC_PINCTRL_H_ +#define __MFD_CDC_PINCTRL_H_ + +#include +#include + +#ifdef CONFIG_MSM_CDC_PINCTRL +extern int msm_cdc_pinctrl_select_sleep_state(struct device_node *np); +extern int msm_cdc_pinctrl_select_active_state(struct device_node *np); +extern bool msm_cdc_pinctrl_get_state(struct device_node *np); +extern int msm_cdc_get_gpio_state(struct device_node *np); + +#else +int msm_cdc_pinctrl_select_sleep_state(struct device_node *np) +{ + return 0; +} +int msm_cdc_pinctrl_select_active_state(struct device_node *np) +{ + return 0; +} +int msm_cdc_get_gpio_state(struct device_node *np) +{ + return 0; +} +# +#endif + +#endif diff --git a/include/linux/mfd/msm-cdc-supply.h b/include/linux/mfd/msm-cdc-supply.h new file mode 100644 index 0000000000000000000000000000000000000000..b40f44b1f12f4cbecd82b3a266d2bbbebd3609e6 --- /dev/null +++ b/include/linux/mfd/msm-cdc-supply.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __CODEC_POWER_SUPPLY_H__ +#define __CODEC_POWER_SUPPLY_H__ + +#include +#include +#include + +struct cdc_regulator { + const char *name; + int min_uV; + int max_uV; + int optimum_uA; + bool ondemand; + struct regulator *regulator; +}; + +extern int msm_cdc_get_power_supplies(struct device *dev, + struct cdc_regulator **cdc_vreg, + int *total_num_supplies); +extern int msm_cdc_disable_static_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies); +extern int msm_cdc_release_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies); +extern int msm_cdc_enable_static_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies); +extern int msm_cdc_init_supplies(struct device *dev, + struct regulator_bulk_data **supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies); +#endif diff --git a/include/linux/mfd/wcd9335/registers.h b/include/linux/mfd/wcd9335/registers.h new file mode 100644 index 0000000000000000000000000000000000000000..c50430d4278f83d6246a959aa16655c13b22ab13 --- /dev/null +++ b/include/linux/mfd/wcd9335/registers.h @@ -0,0 +1,1348 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _WCD9335_REGISTERS_H +#define _WCD9335_REGISTERS_H + +#define WCD9335_PAGE_SIZE 256 +#define WCD9335_NUM_PAGES 256 + +extern const u8 *wcd9335_reg[WCD9335_NUM_PAGES]; + +enum { + PAGE_0 = 0, + PAGE_1, + PAGE_2, + PAGE_6 = 6, + PAGE_10 = 0xA, + PAGE_11, + PAGE_12, + PAGE_13, + PAGE_0X80, +}; + +/* Page-0 Registers */ +#define WCD9335_PAGE0_PAGE_REGISTER 0x0000 +#define WCD9335_CODEC_RPM_CLK_BYPASS 0x0001 +#define WCD9335_CODEC_RPM_CLK_GATE 0x0002 +#define WCD9335_CODEC_RPM_CLK_MCLK_CFG 0x0003 +#define WCD9335_CODEC_RPM_RST_CTL 0x0009 +#define WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL 0x0011 +#define WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_1 0x0012 +#define WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_2 0x0013 +#define WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_3 0x0014 +#define WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN 0x0015 +#define WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN 0x0016 +#define WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1 0x0017 +#define WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2 0x0018 +#define WCD9335_CODEC_RPM_INT_MASK 0x001d +#define WCD9335_CODEC_RPM_INT_STATUS 0x001e +#define WCD9335_CODEC_RPM_INT_CLEAR 0x001f +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0 0x0021 +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE1 0x0022 +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2 0x0023 +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE3 0x0024 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_CTL 0x0025 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_TEST0 0x0026 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_TEST1 0x0027 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0 0x0029 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 0x002a +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 0x002b +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT3 0x002c +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT4 0x002d +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT5 0x002e +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT6 0x002f +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT7 0x0030 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT8 0x0031 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT9 0x0032 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10 0x0033 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11 0x0034 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12 0x0035 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT13 0x0036 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT14 0x0037 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT15 0x0038 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS 0x0039 +#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO 0x003a +#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_1 0x003b +#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_2 0x003c +#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_3 0x003d +#define WCD9335_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL 0x003e +#define WCD9335_CHIP_TIER_CTRL_I2C_ACTIVE 0x003f +#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_CTL 0x0041 +#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_STATUS 0x0042 +#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_MSB 0x0043 +#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_LSB 0x0044 +#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_CTL 0x0045 +#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_STATUS 0x0046 +#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_MSB 0x0047 +#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_LSB 0x0048 +#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_CTL 0x0049 +#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_STATUS 0x004a +#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_MSB 0x004b +#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_LSB 0x004c +#define WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL 0x0051 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL 0x0052 +#define WCD9335_DATA_HUB_DATA_HUB_I2S_CLK 0x0053 +#define WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG 0x0054 +#define WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG 0x0055 +#define WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG 0x0056 +#define WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG 0x0057 +#define WCD9335_DATA_HUB_DATA_HUB_RX4_INP_CFG 0x0058 +#define WCD9335_DATA_HUB_DATA_HUB_RX5_INP_CFG 0x0059 +#define WCD9335_DATA_HUB_DATA_HUB_RX6_INP_CFG 0x005a +#define WCD9335_DATA_HUB_DATA_HUB_RX7_INP_CFG 0x005b +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX0_INP_CFG 0x0061 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX1_INP_CFG 0x0062 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX2_INP_CFG 0x0063 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX3_INP_CFG 0x0064 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX4_INP_CFG 0x0065 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX5_INP_CFG 0x0066 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX6_INP_CFG 0x0067 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX7_INP_CFG 0x0068 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX8_INP_CFG 0x0069 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX9_INP_CFG 0x006a +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX10_INP_CFG 0x006b +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG 0x006c +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG 0x006e +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX14_INP_CFG 0x006f +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX15_INP_CFG 0x0070 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG 0x0071 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG 0x0072 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG 0x0073 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG 0x0074 +#define WCD9335_DATA_HUB_NATIVE_FIFO_SYNC 0x0075 +#define WCD9335_DATA_HUB_NATIVE_FIFO_STATUS 0x007D +#define WCD9335_INTR_CFG 0x0081 +#define WCD9335_INTR_CLR_COMMIT 0x0082 +#define WCD9335_INTR_PIN1_MASK0 0x0089 +#define WCD9335_INTR_PIN1_MASK1 0x008a +#define WCD9335_INTR_PIN1_MASK2 0x008b +#define WCD9335_INTR_PIN1_MASK3 0x008c +#define WCD9335_INTR_PIN1_STATUS0 0x0091 +#define WCD9335_INTR_PIN1_STATUS1 0x0092 +#define WCD9335_INTR_PIN1_STATUS2 0x0093 +#define WCD9335_INTR_PIN1_STATUS3 0x0094 +#define WCD9335_INTR_PIN1_CLEAR0 0x0099 +#define WCD9335_INTR_PIN1_CLEAR1 0x009a +#define WCD9335_INTR_PIN1_CLEAR2 0x009b +#define WCD9335_INTR_PIN1_CLEAR3 0x009c +#define WCD9335_INTR_PIN2_MASK0 0x00a1 +#define WCD9335_INTR_PIN2_MASK1 0x00a2 +#define WCD9335_INTR_PIN2_MASK2 0x00a3 +#define WCD9335_INTR_PIN2_MASK3 0x00a4 +#define WCD9335_INTR_PIN2_STATUS0 0x00a9 +#define WCD9335_INTR_PIN2_STATUS1 0x00aa +#define WCD9335_INTR_PIN2_STATUS2 0x00ab +#define WCD9335_INTR_PIN2_STATUS3 0x00ac +#define WCD9335_INTR_PIN2_CLEAR0 0x00b1 +#define WCD9335_INTR_PIN2_CLEAR1 0x00b2 +#define WCD9335_INTR_PIN2_CLEAR2 0x00b3 +#define WCD9335_INTR_PIN2_CLEAR3 0x00b4 +#define WCD9335_INTR_LEVEL0 0x00e1 +#define WCD9335_INTR_LEVEL1 0x00e2 +#define WCD9335_INTR_LEVEL2 0x00e3 +#define WCD9335_INTR_LEVEL3 0x00e4 +#define WCD9335_INTR_BYPASS0 0x00e9 +#define WCD9335_INTR_BYPASS1 0x00ea +#define WCD9335_INTR_BYPASS2 0x00eb +#define WCD9335_INTR_BYPASS3 0x00ec +#define WCD9335_INTR_SET0 0x00f1 +#define WCD9335_INTR_SET1 0x00f2 +#define WCD9335_INTR_SET2 0x00f3 +#define WCD9335_INTR_SET3 0x00f4 + +/* Page-1 Registers */ +#define WCD9335_PAGE1_PAGE_REGISTER 0x0100 +#define WCD9335_CPE_FLL_USER_CTL_0 0x0101 +#define WCD9335_CPE_FLL_USER_CTL_1 0x0102 +#define WCD9335_CPE_FLL_USER_CTL_2 0x0103 +#define WCD9335_CPE_FLL_USER_CTL_3 0x0104 +#define WCD9335_CPE_FLL_USER_CTL_4 0x0105 +#define WCD9335_CPE_FLL_USER_CTL_5 0x0106 +#define WCD9335_CPE_FLL_USER_CTL_6 0x0107 +#define WCD9335_CPE_FLL_USER_CTL_7 0x0108 +#define WCD9335_CPE_FLL_USER_CTL_8 0x0109 +#define WCD9335_CPE_FLL_USER_CTL_9 0x010a +#define WCD9335_CPE_FLL_L_VAL_CTL_0 0x010b +#define WCD9335_CPE_FLL_L_VAL_CTL_1 0x010c +#define WCD9335_CPE_FLL_DSM_FRAC_CTL_0 0x010d +#define WCD9335_CPE_FLL_DSM_FRAC_CTL_1 0x010e +#define WCD9335_CPE_FLL_CONFIG_CTL_0 0x010f +#define WCD9335_CPE_FLL_CONFIG_CTL_1 0x0110 +#define WCD9335_CPE_FLL_CONFIG_CTL_2 0x0111 +#define WCD9335_CPE_FLL_CONFIG_CTL_3 0x0112 +#define WCD9335_CPE_FLL_CONFIG_CTL_4 0x0113 +#define WCD9335_CPE_FLL_TEST_CTL_0 0x0114 +#define WCD9335_CPE_FLL_TEST_CTL_1 0x0115 +#define WCD9335_CPE_FLL_TEST_CTL_2 0x0116 +#define WCD9335_CPE_FLL_TEST_CTL_3 0x0117 +#define WCD9335_CPE_FLL_TEST_CTL_4 0x0118 +#define WCD9335_CPE_FLL_TEST_CTL_5 0x0119 +#define WCD9335_CPE_FLL_TEST_CTL_6 0x011a +#define WCD9335_CPE_FLL_TEST_CTL_7 0x011b +#define WCD9335_CPE_FLL_FREQ_CTL_0 0x011c +#define WCD9335_CPE_FLL_FREQ_CTL_1 0x011d +#define WCD9335_CPE_FLL_FREQ_CTL_2 0x011e +#define WCD9335_CPE_FLL_FREQ_CTL_3 0x011f +#define WCD9335_CPE_FLL_SSC_CTL_0 0x0120 +#define WCD9335_CPE_FLL_SSC_CTL_1 0x0121 +#define WCD9335_CPE_FLL_SSC_CTL_2 0x0122 +#define WCD9335_CPE_FLL_SSC_CTL_3 0x0123 +#define WCD9335_CPE_FLL_FLL_MODE 0x0124 +#define WCD9335_CPE_FLL_STATUS_0 0x0125 +#define WCD9335_CPE_FLL_STATUS_1 0x0126 +#define WCD9335_CPE_FLL_STATUS_2 0x0127 +#define WCD9335_CPE_FLL_STATUS_3 0x0128 +#define WCD9335_I2S_FLL_USER_CTL_0 0x0141 +#define WCD9335_I2S_FLL_USER_CTL_1 0x0142 +#define WCD9335_I2S_FLL_USER_CTL_2 0x0143 +#define WCD9335_I2S_FLL_USER_CTL_3 0x0144 +#define WCD9335_I2S_FLL_USER_CTL_4 0x0145 +#define WCD9335_I2S_FLL_USER_CTL_5 0x0146 +#define WCD9335_I2S_FLL_USER_CTL_6 0x0147 +#define WCD9335_I2S_FLL_USER_CTL_7 0x0148 +#define WCD9335_I2S_FLL_USER_CTL_8 0x0149 +#define WCD9335_I2S_FLL_USER_CTL_9 0x014a +#define WCD9335_I2S_FLL_L_VAL_CTL_0 0x014b +#define WCD9335_I2S_FLL_L_VAL_CTL_1 0x014c +#define WCD9335_I2S_FLL_DSM_FRAC_CTL_0 0x014d +#define WCD9335_I2S_FLL_DSM_FRAC_CTL_1 0x014e +#define WCD9335_I2S_FLL_CONFIG_CTL_0 0x014f +#define WCD9335_I2S_FLL_CONFIG_CTL_1 0x0150 +#define WCD9335_I2S_FLL_CONFIG_CTL_2 0x0151 +#define WCD9335_I2S_FLL_CONFIG_CTL_3 0x0152 +#define WCD9335_I2S_FLL_CONFIG_CTL_4 0x0153 +#define WCD9335_I2S_FLL_TEST_CTL_0 0x0154 +#define WCD9335_I2S_FLL_TEST_CTL_1 0x0155 +#define WCD9335_I2S_FLL_TEST_CTL_2 0x0156 +#define WCD9335_I2S_FLL_TEST_CTL_3 0x0157 +#define WCD9335_I2S_FLL_TEST_CTL_4 0x0158 +#define WCD9335_I2S_FLL_TEST_CTL_5 0x0159 +#define WCD9335_I2S_FLL_TEST_CTL_6 0x015a +#define WCD9335_I2S_FLL_TEST_CTL_7 0x015b +#define WCD9335_I2S_FLL_FREQ_CTL_0 0x015c +#define WCD9335_I2S_FLL_FREQ_CTL_1 0x015d +#define WCD9335_I2S_FLL_FREQ_CTL_2 0x015e +#define WCD9335_I2S_FLL_FREQ_CTL_3 0x015f +#define WCD9335_I2S_FLL_SSC_CTL_0 0x0160 +#define WCD9335_I2S_FLL_SSC_CTL_1 0x0161 +#define WCD9335_I2S_FLL_SSC_CTL_2 0x0162 +#define WCD9335_I2S_FLL_SSC_CTL_3 0x0163 +#define WCD9335_I2S_FLL_FLL_MODE 0x0164 +#define WCD9335_I2S_FLL_STATUS_0 0x0165 +#define WCD9335_I2S_FLL_STATUS_1 0x0166 +#define WCD9335_I2S_FLL_STATUS_2 0x0167 +#define WCD9335_I2S_FLL_STATUS_3 0x0168 +#define WCD9335_SB_FLL_USER_CTL_0 0x0181 +#define WCD9335_SB_FLL_USER_CTL_1 0x0182 +#define WCD9335_SB_FLL_USER_CTL_2 0x0183 +#define WCD9335_SB_FLL_USER_CTL_3 0x0184 +#define WCD9335_SB_FLL_USER_CTL_4 0x0185 +#define WCD9335_SB_FLL_USER_CTL_5 0x0186 +#define WCD9335_SB_FLL_USER_CTL_6 0x0187 +#define WCD9335_SB_FLL_USER_CTL_7 0x0188 +#define WCD9335_SB_FLL_USER_CTL_8 0x0189 +#define WCD9335_SB_FLL_USER_CTL_9 0x018a +#define WCD9335_SB_FLL_L_VAL_CTL_0 0x018b +#define WCD9335_SB_FLL_L_VAL_CTL_1 0x018c +#define WCD9335_SB_FLL_DSM_FRAC_CTL_0 0x018d +#define WCD9335_SB_FLL_DSM_FRAC_CTL_1 0x018e +#define WCD9335_SB_FLL_CONFIG_CTL_0 0x018f +#define WCD9335_SB_FLL_CONFIG_CTL_1 0x0190 +#define WCD9335_SB_FLL_CONFIG_CTL_2 0x0191 +#define WCD9335_SB_FLL_CONFIG_CTL_3 0x0192 +#define WCD9335_SB_FLL_CONFIG_CTL_4 0x0193 +#define WCD9335_SB_FLL_TEST_CTL_0 0x0194 +#define WCD9335_SB_FLL_TEST_CTL_1 0x0195 +#define WCD9335_SB_FLL_TEST_CTL_2 0x0196 +#define WCD9335_SB_FLL_TEST_CTL_3 0x0197 +#define WCD9335_SB_FLL_TEST_CTL_4 0x0198 +#define WCD9335_SB_FLL_TEST_CTL_5 0x0199 +#define WCD9335_SB_FLL_TEST_CTL_6 0x019a +#define WCD9335_SB_FLL_TEST_CTL_7 0x019b +#define WCD9335_SB_FLL_FREQ_CTL_0 0x019c +#define WCD9335_SB_FLL_FREQ_CTL_1 0x019d +#define WCD9335_SB_FLL_FREQ_CTL_2 0x019e +#define WCD9335_SB_FLL_FREQ_CTL_3 0x019f +#define WCD9335_SB_FLL_SSC_CTL_0 0x01a0 +#define WCD9335_SB_FLL_SSC_CTL_1 0x01a1 +#define WCD9335_SB_FLL_SSC_CTL_2 0x01a2 +#define WCD9335_SB_FLL_SSC_CTL_3 0x01a3 +#define WCD9335_SB_FLL_FLL_MODE 0x01a4 +#define WCD9335_SB_FLL_STATUS_0 0x01a5 +#define WCD9335_SB_FLL_STATUS_1 0x01a6 +#define WCD9335_SB_FLL_STATUS_2 0x01a7 +#define WCD9335_SB_FLL_STATUS_3 0x01a8 + +/* Page-2 Registers */ +#define WCD9335_PAGE2_PAGE_REGISTER 0x0200 +#define WCD9335_CPE_SS_MEM_PTR_0 0x0201 +#define WCD9335_CPE_SS_MEM_PTR_1 0x0202 +#define WCD9335_CPE_SS_MEM_PTR_2 0x0203 +#define WCD9335_CPE_SS_MEM_CTRL 0x0205 +#define WCD9335_CPE_SS_MEM_BANK_0 0x0206 +#define WCD9335_CPE_SS_MEM_BANK_1 0x0207 +#define WCD9335_CPE_SS_MEM_BANK_2 0x0208 +#define WCD9335_CPE_SS_MEM_BANK_3 0x0209 +#define WCD9335_CPE_SS_MEM_BANK_4 0x020a +#define WCD9335_CPE_SS_MEM_BANK_5 0x020b +#define WCD9335_CPE_SS_MEM_BANK_6 0x020c +#define WCD9335_CPE_SS_MEM_BANK_7 0x020d +#define WCD9335_CPE_SS_MEM_BANK_8 0x020e +#define WCD9335_CPE_SS_MEM_BANK_9 0x020f +#define WCD9335_CPE_SS_MEM_BANK_10 0x0210 +#define WCD9335_CPE_SS_MEM_BANK_11 0x0211 +#define WCD9335_CPE_SS_MEM_BANK_12 0x0212 +#define WCD9335_CPE_SS_MEM_BANK_13 0x0213 +#define WCD9335_CPE_SS_MEM_BANK_14 0x0214 +#define WCD9335_CPE_SS_MEM_BANK_15 0x0215 +#define WCD9335_CPE_SS_INBOX1_TRG 0x0216 +#define WCD9335_CPE_SS_INBOX2_TRG 0x0217 +#define WCD9335_CPE_SS_INBOX1_0 0x0218 +#define WCD9335_CPE_SS_INBOX1_1 0x0219 +#define WCD9335_CPE_SS_INBOX1_2 0x021a +#define WCD9335_CPE_SS_INBOX1_3 0x021b +#define WCD9335_CPE_SS_INBOX1_4 0x021c +#define WCD9335_CPE_SS_INBOX1_5 0x021d +#define WCD9335_CPE_SS_INBOX1_6 0x021e +#define WCD9335_CPE_SS_INBOX1_7 0x021f +#define WCD9335_CPE_SS_INBOX1_8 0x0220 +#define WCD9335_CPE_SS_INBOX1_9 0x0221 +#define WCD9335_CPE_SS_INBOX1_10 0x0222 +#define WCD9335_CPE_SS_INBOX1_11 0x0223 +#define WCD9335_CPE_SS_INBOX1_12 0x0224 +#define WCD9335_CPE_SS_INBOX1_13 0x0225 +#define WCD9335_CPE_SS_INBOX1_14 0x0226 +#define WCD9335_CPE_SS_INBOX1_15 0x0227 +#define WCD9335_CPE_SS_OUTBOX1_0 0x0228 +#define WCD9335_CPE_SS_OUTBOX1_1 0x0229 +#define WCD9335_CPE_SS_OUTBOX1_2 0x022a +#define WCD9335_CPE_SS_OUTBOX1_3 0x022b +#define WCD9335_CPE_SS_OUTBOX1_4 0x022c +#define WCD9335_CPE_SS_OUTBOX1_5 0x022d +#define WCD9335_CPE_SS_OUTBOX1_6 0x022e +#define WCD9335_CPE_SS_OUTBOX1_7 0x022f +#define WCD9335_CPE_SS_OUTBOX1_8 0x0230 +#define WCD9335_CPE_SS_OUTBOX1_9 0x0231 +#define WCD9335_CPE_SS_OUTBOX1_10 0x0232 +#define WCD9335_CPE_SS_OUTBOX1_11 0x0233 +#define WCD9335_CPE_SS_OUTBOX1_12 0x0234 +#define WCD9335_CPE_SS_OUTBOX1_13 0x0235 +#define WCD9335_CPE_SS_OUTBOX1_14 0x0236 +#define WCD9335_CPE_SS_OUTBOX1_15 0x0237 +#define WCD9335_CPE_SS_INBOX2_0 0x0238 +#define WCD9335_CPE_SS_INBOX2_1 0x0239 +#define WCD9335_CPE_SS_INBOX2_2 0x023a +#define WCD9335_CPE_SS_INBOX2_3 0x023b +#define WCD9335_CPE_SS_INBOX2_4 0x023c +#define WCD9335_CPE_SS_INBOX2_5 0x023d +#define WCD9335_CPE_SS_INBOX2_6 0x023e +#define WCD9335_CPE_SS_INBOX2_7 0x023f +#define WCD9335_CPE_SS_INBOX2_8 0x0240 +#define WCD9335_CPE_SS_INBOX2_9 0x0241 +#define WCD9335_CPE_SS_INBOX2_10 0x0242 +#define WCD9335_CPE_SS_INBOX2_11 0x0243 +#define WCD9335_CPE_SS_INBOX2_12 0x0244 +#define WCD9335_CPE_SS_INBOX2_13 0x0245 +#define WCD9335_CPE_SS_INBOX2_14 0x0246 +#define WCD9335_CPE_SS_INBOX2_15 0x0247 +#define WCD9335_CPE_SS_OUTBOX2_0 0x0248 +#define WCD9335_CPE_SS_OUTBOX2_1 0x0249 +#define WCD9335_CPE_SS_OUTBOX2_2 0x024a +#define WCD9335_CPE_SS_OUTBOX2_3 0x024b +#define WCD9335_CPE_SS_OUTBOX2_4 0x024c +#define WCD9335_CPE_SS_OUTBOX2_5 0x024d +#define WCD9335_CPE_SS_OUTBOX2_6 0x024e +#define WCD9335_CPE_SS_OUTBOX2_7 0x024f +#define WCD9335_CPE_SS_OUTBOX2_8 0x0250 +#define WCD9335_CPE_SS_OUTBOX2_9 0x0251 +#define WCD9335_CPE_SS_OUTBOX2_10 0x0252 +#define WCD9335_CPE_SS_OUTBOX2_11 0x0253 +#define WCD9335_CPE_SS_OUTBOX2_12 0x0254 +#define WCD9335_CPE_SS_OUTBOX2_13 0x0255 +#define WCD9335_CPE_SS_OUTBOX2_14 0x0256 +#define WCD9335_CPE_SS_OUTBOX2_15 0x0257 +#define WCD9335_CPE_SS_OUTBOX1_ACK 0x0258 +#define WCD9335_CPE_SS_OUTBOX2_ACK 0x0259 +#define WCD9335_CPE_SS_EC_BUF_INT_PERIOD 0x025a +#define WCD9335_CPE_SS_US_BUF_INT_PERIOD 0x025b +#define WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD 0x025c +#define WCD9335_CPE_SS_CFG 0x025d +#define WCD9335_CPE_SS_US_EC_MUX_CFG 0x025e +#define WCD9335_CPE_SS_MAD_CTL 0x025f +#define WCD9335_CPE_SS_CPAR_CTL 0x0260 +#define WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD 0x0261 +#define WCD9335_CPE_SS_TX_PP_CFG 0x0262 +#define WCD9335_CPE_SS_DMIC0_CTL 0x0263 +#define WCD9335_CPE_SS_DMIC1_CTL 0x0264 +#define WCD9335_CPE_SS_DMIC2_CTL 0x0265 +#define WCD9335_CPE_SS_DMIC_CFG 0x0266 +#define WCD9335_CPE_SS_SVA_CFG 0x0267 +#define WCD9335_CPE_SS_CPAR_CFG 0x0271 +#define WCD9335_CPE_SS_WDOG_CFG 0x0272 +#define WCD9335_CPE_SS_BACKUP_INT 0x0273 +#define WCD9335_CPE_SS_STATUS 0x0274 +#define WCD9335_CPE_SS_CPE_OCD_CFG 0x0275 +#define WCD9335_CPE_SS_SS_ERROR_INT_MASK 0x0276 +#define WCD9335_CPE_SS_SS_ERROR_INT_STATUS 0x0277 +#define WCD9335_CPE_SS_SS_ERROR_INT_CLEAR 0x0278 +#define WCD9335_SOC_MAD_MAIN_CTL_1 0x0281 +#define WCD9335_SOC_MAD_MAIN_CTL_2 0x0282 +#define WCD9335_SOC_MAD_AUDIO_CTL_1 0x0283 +#define WCD9335_SOC_MAD_AUDIO_CTL_2 0x0284 +#define WCD9335_SOC_MAD_AUDIO_CTL_3 0x0285 +#define WCD9335_SOC_MAD_AUDIO_CTL_4 0x0286 +#define WCD9335_SOC_MAD_AUDIO_CTL_5 0x0287 +#define WCD9335_SOC_MAD_AUDIO_CTL_6 0x0288 +#define WCD9335_SOC_MAD_AUDIO_CTL_7 0x0289 +#define WCD9335_SOC_MAD_AUDIO_CTL_8 0x028a +#define WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR 0x028b +#define WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL 0x028c +#define WCD9335_SOC_MAD_ULTR_CTL_1 0x028d +#define WCD9335_SOC_MAD_ULTR_CTL_2 0x028e +#define WCD9335_SOC_MAD_ULTR_CTL_3 0x028f +#define WCD9335_SOC_MAD_ULTR_CTL_4 0x0290 +#define WCD9335_SOC_MAD_ULTR_CTL_5 0x0291 +#define WCD9335_SOC_MAD_ULTR_CTL_6 0x0292 +#define WCD9335_SOC_MAD_ULTR_CTL_7 0x0293 +#define WCD9335_SOC_MAD_BEACON_CTL_1 0x0294 +#define WCD9335_SOC_MAD_BEACON_CTL_2 0x0295 +#define WCD9335_SOC_MAD_BEACON_CTL_3 0x0296 +#define WCD9335_SOC_MAD_BEACON_CTL_4 0x0297 +#define WCD9335_SOC_MAD_BEACON_CTL_5 0x0298 +#define WCD9335_SOC_MAD_BEACON_CTL_6 0x0299 +#define WCD9335_SOC_MAD_BEACON_CTL_7 0x029a +#define WCD9335_SOC_MAD_BEACON_CTL_8 0x029b +#define WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR 0x029c +#define WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL 0x029d +#define WCD9335_SOC_MAD_INP_SEL 0x029e + +/* Page-6 Registers */ +#define WCD9335_PAGE6_PAGE_REGISTER 0x0600 +#define WCD9335_ANA_BIAS 0x0601 +#define WCD9335_ANA_CLK_TOP 0x0602 +#define WCD9335_ANA_RCO 0x0603 +#define WCD9335_ANA_BUCK_VOUT_A 0x0604 +#define WCD9335_ANA_BUCK_VOUT_D 0x0605 +#define WCD9335_ANA_BUCK_CTL 0x0606 +#define WCD9335_ANA_BUCK_STATUS 0x0607 +#define WCD9335_ANA_RX_SUPPLIES 0x0608 +#define WCD9335_ANA_HPH 0x0609 +#define WCD9335_ANA_EAR 0x060a +#define WCD9335_ANA_LO_1_2 0x060b +#define WCD9335_ANA_LO_3_4 0x060c +#define WCD9335_ANA_MAD_SETUP 0x060d +#define WCD9335_ANA_AMIC1 0x060e +#define WCD9335_ANA_AMIC2 0x060f +#define WCD9335_ANA_AMIC3 0x0610 +#define WCD9335_ANA_AMIC4 0x0611 +#define WCD9335_ANA_AMIC5 0x0612 +#define WCD9335_ANA_AMIC6 0x0613 +#define WCD9335_ANA_MBHC_MECH 0x0614 +#define WCD9335_ANA_MBHC_ELECT 0x0615 +#define WCD9335_ANA_MBHC_ZDET 0x0616 +#define WCD9335_ANA_MBHC_RESULT_1 0x0617 +#define WCD9335_ANA_MBHC_RESULT_2 0x0618 +#define WCD9335_ANA_MBHC_RESULT_3 0x0619 +#define WCD9335_ANA_MBHC_BTN0 0x061a +#define WCD9335_ANA_MBHC_BTN1 0x061b +#define WCD9335_ANA_MBHC_BTN2 0x061c +#define WCD9335_ANA_MBHC_BTN3 0x061d +#define WCD9335_ANA_MBHC_BTN4 0x061e +#define WCD9335_ANA_MBHC_BTN5 0x061f +#define WCD9335_ANA_MBHC_BTN6 0x0620 +#define WCD9335_ANA_MBHC_BTN7 0x0621 +#define WCD9335_ANA_MICB1 0x0622 +#define WCD9335_ANA_MICB2 0x0623 +#define WCD9335_ANA_MICB2_RAMP 0x0624 +#define WCD9335_ANA_MICB3 0x0625 +#define WCD9335_ANA_MICB4 0x0626 +#define WCD9335_ANA_VBADC 0x0627 +#define WCD9335_BIAS_CTL 0x0628 +#define WCD9335_BIAS_VBG_FINE_ADJ 0x0629 +#define WCD9335_CLOCK_TEST_CTL 0x062d +#define WCD9335_RCO_CTRL_1 0x062e +#define WCD9335_RCO_CTRL_2 0x062f +#define WCD9335_RCO_CAL 0x0630 +#define WCD9335_RCO_CAL_1 0x0631 +#define WCD9335_RCO_CAL_2 0x0632 +#define WCD9335_RCO_TEST_CTRL 0x0633 +#define WCD9335_RCO_CAL_OUT_1 0x0634 +#define WCD9335_RCO_CAL_OUT_2 0x0635 +#define WCD9335_RCO_CAL_OUT_3 0x0636 +#define WCD9335_RCO_CAL_OUT_4 0x0637 +#define WCD9335_RCO_CAL_OUT_5 0x0638 +#define WCD9335_SIDO_SIDO_MODE_1 0x063a +#define WCD9335_SIDO_SIDO_MODE_2 0x063b +#define WCD9335_SIDO_SIDO_MODE_3 0x063c +#define WCD9335_SIDO_SIDO_MODE_4 0x063d +#define WCD9335_SIDO_SIDO_VCL_1 0x063e +#define WCD9335_SIDO_SIDO_VCL_2 0x063f +#define WCD9335_SIDO_SIDO_VCL_3 0x0640 +#define WCD9335_SIDO_SIDO_CCL_1 0x0641 +#define WCD9335_SIDO_SIDO_CCL_2 0x0642 +#define WCD9335_SIDO_SIDO_CCL_3 0x0643 +#define WCD9335_SIDO_SIDO_CCL_4 0x0644 +#define WCD9335_SIDO_SIDO_CCL_5 0x0645 +#define WCD9335_SIDO_SIDO_CCL_6 0x0646 +#define WCD9335_SIDO_SIDO_CCL_7 0x0647 +#define WCD9335_SIDO_SIDO_CCL_8 0x0648 +#define WCD9335_SIDO_SIDO_CCL_9 0x0649 +#define WCD9335_SIDO_SIDO_CCL_10 0x064a +#define WCD9335_SIDO_SIDO_FILTER_1 0x064b +#define WCD9335_SIDO_SIDO_FILTER_2 0x064c +#define WCD9335_SIDO_SIDO_DRIVER_1 0x064d +#define WCD9335_SIDO_SIDO_DRIVER_2 0x064e +#define WCD9335_SIDO_SIDO_DRIVER_3 0x064f +#define WCD9335_SIDO_SIDO_CAL_CODE_EXT_1 0x0650 +#define WCD9335_SIDO_SIDO_CAL_CODE_EXT_2 0x0651 +#define WCD9335_SIDO_SIDO_CAL_CODE_OUT_1 0x0652 +#define WCD9335_SIDO_SIDO_CAL_CODE_OUT_2 0x0653 +#define WCD9335_SIDO_SIDO_TEST_1 0x0654 +#define WCD9335_SIDO_SIDO_TEST_2 0x0655 +#define WCD9335_MBHC_CTL_1 0x0656 +#define WCD9335_MBHC_CTL_2 0x0657 +#define WCD9335_MBHC_PLUG_DETECT_CTL 0x0658 +#define WCD9335_MBHC_ZDET_ANA_CTL 0x0659 +#define WCD9335_MBHC_ZDET_RAMP_CTL 0x065a +#define WCD9335_MBHC_FSM_DEBUG 0x065b /* v1.x */ +#define WCD9335_MBHC_FSM_STATUS 0x065b /* v2.0 */ +#define WCD9335_MBHC_TEST_CTL 0x065c +#define WCD9335_VBADC_SUBBLOCK_EN 0x065d +#define WCD9335_VBADC_IBIAS_FE 0x065e +#define WCD9335_VBADC_BIAS_ADC 0x065f +#define WCD9335_VBADC_FE_CTRL 0x0660 +#define WCD9335_VBADC_ADC_REF 0x0661 +#define WCD9335_VBADC_ADC_IO 0x0662 +#define WCD9335_VBADC_ADC_SAR 0x0663 +#define WCD9335_VBADC_DEBUG 0x0664 +#define WCD9335_VBADC_ADC_DOUTMSB 0x0665 +#define WCD9335_VBADC_ADC_DOUTLSB 0x0666 +#define WCD9335_LDOH_MODE 0x0667 +#define WCD9335_LDOH_BIAS 0x0668 +#define WCD9335_LDOH_STB_LOADS 0x0669 +#define WCD9335_LDOH_SLOWRAMP 0x066a +#define WCD9335_MICB1_TEST_CTL_1 0x066b +#define WCD9335_MICB1_TEST_CTL_2 0x066c +#define WCD9335_MICB1_TEST_CTL_3 0x066d +#define WCD9335_MICB2_TEST_CTL_1 0x066e +#define WCD9335_MICB2_TEST_CTL_2 0x066f +#define WCD9335_MICB2_TEST_CTL_3 0x0670 +#define WCD9335_MICB3_TEST_CTL_1 0x0671 +#define WCD9335_MICB3_TEST_CTL_2 0x0672 +#define WCD9335_MICB3_TEST_CTL_3 0x0673 +#define WCD9335_MICB4_TEST_CTL_1 0x0674 +#define WCD9335_MICB4_TEST_CTL_2 0x0675 +#define WCD9335_MICB4_TEST_CTL_3 0x0676 +#define WCD9335_TX_COM_ADC_VCM 0x0677 +#define WCD9335_TX_COM_BIAS_ATEST 0x0678 +#define WCD9335_TX_COM_ADC_INT1_IB 0x0679 +#define WCD9335_TX_COM_ADC_INT2_IB 0x067a +#define WCD9335_TX_COM_TXFE_DIV_CTL 0x067b +#define WCD9335_TX_COM_TXFE_DIV_START 0x067c +#define WCD9335_TX_COM_TXFE_DIV_STOP_9P6M 0x067d +#define WCD9335_TX_COM_TXFE_DIV_STOP_12P288M 0x067e +#define WCD9335_TX_1_2_TEST_EN 0x067f +#define WCD9335_TX_1_2_ADC_IB 0x0680 +#define WCD9335_TX_1_2_ATEST_REFCTL 0x0681 +#define WCD9335_TX_1_2_TEST_CTL 0x0682 +#define WCD9335_TX_1_2_TEST_BLK_EN 0x0683 +#define WCD9335_TX_1_2_TXFE_CLKDIV 0x0684 +#define WCD9335_TX_1_2_SAR1_ERR 0x0685 +#define WCD9335_TX_1_2_SAR2_ERR 0x0686 +#define WCD9335_TX_3_4_TEST_EN 0x0687 +#define WCD9335_TX_3_4_ADC_IB 0x0688 +#define WCD9335_TX_3_4_ATEST_REFCTL 0x0689 +#define WCD9335_TX_3_4_TEST_CTL 0x068a +#define WCD9335_TX_3_4_TEST_BLK_EN 0x068b +#define WCD9335_TX_3_4_TXFE_CLKDIV 0x068c +#define WCD9335_TX_3_4_SAR1_ERR 0x068d +#define WCD9335_TX_3_4_SAR2_ERR 0x068e +#define WCD9335_TX_5_6_TEST_EN 0x068f +#define WCD9335_TX_5_6_ADC_IB 0x0690 +#define WCD9335_TX_5_6_ATEST_REFCTL 0x0691 +#define WCD9335_TX_5_6_TEST_CTL 0x0692 +#define WCD9335_TX_5_6_TEST_BLK_EN 0x0693 +#define WCD9335_TX_5_6_TXFE_CLKDIV 0x0694 +#define WCD9335_TX_5_6_SAR1_ERR 0x0695 +#define WCD9335_TX_5_6_SAR2_ERR 0x0696 +#define WCD9335_CLASSH_MODE_1 0x0697 +#define WCD9335_CLASSH_MODE_2 0x0698 +#define WCD9335_CLASSH_MODE_3 0x0699 +#define WCD9335_CLASSH_CTRL_VCL_1 0x069a +#define WCD9335_CLASSH_CTRL_VCL_2 0x069b +#define WCD9335_CLASSH_CTRL_CCL_1 0x069c +#define WCD9335_CLASSH_CTRL_CCL_2 0x069d +#define WCD9335_CLASSH_CTRL_CCL_3 0x069e +#define WCD9335_CLASSH_CTRL_CCL_4 0x069f +#define WCD9335_CLASSH_CTRL_CCL_5 0x06a0 +#define WCD9335_CLASSH_BUCK_TMUX_A_D 0x06a1 +#define WCD9335_CLASSH_BUCK_SW_DRV_CNTL 0x06a2 +#define WCD9335_CLASSH_SPARE 0x06a3 +#define WCD9335_FLYBACK_EN 0x06a4 +#define WCD9335_FLYBACK_VNEG_CTRL_1 0x06a5 +#define WCD9335_FLYBACK_VNEG_CTRL_2 0x06a6 +#define WCD9335_FLYBACK_VNEG_CTRL_3 0x06a7 +#define WCD9335_FLYBACK_VNEG_CTRL_4 0x06a8 +#define WCD9335_FLYBACK_VNEG_CTRL_5 0x06a9 +#define WCD9335_FLYBACK_VNEG_CTRL_6 0x06aa +#define WCD9335_FLYBACK_VNEG_CTRL_7 0x06ab +#define WCD9335_FLYBACK_VNEG_CTRL_8 0x06ac +#define WCD9335_FLYBACK_VNEG_CTRL_9 0x06ad +#define WCD9335_FLYBACK_VNEG_DAC_CTRL_1 0x06ae +#define WCD9335_FLYBACK_VNEG_DAC_CTRL_2 0x06af +#define WCD9335_FLYBACK_VNEG_DAC_CTRL_3 0x06b0 +#define WCD9335_FLYBACK_VNEG_DAC_CTRL_4 0x06b1 /* v1.x */ +#define WCD9335_FLYBACK_CTRL_1 0x06b1 /* v2.0 */ +#define WCD9335_FLYBACK_TEST_CTL 0x06b2 +#define WCD9335_RX_AUX_SW_CTL 0x06b3 +#define WCD9335_RX_PA_AUX_IN_CONN 0x06b4 +#define WCD9335_RX_TIMER_DIV 0x06b5 +#define WCD9335_RX_OCP_CTL 0x06b6 +#define WCD9335_RX_OCP_COUNT 0x06b7 +#define WCD9335_RX_BIAS_EAR_DAC 0x06b8 +#define WCD9335_RX_BIAS_EAR_AMP 0x06b9 +#define WCD9335_RX_BIAS_HPH_LDO 0x06ba +#define WCD9335_RX_BIAS_HPH_PA 0x06bb +#define WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2 0x06bc +#define WCD9335_RX_BIAS_HPH_RDAC_LDO 0x06bd +#define WCD9335_RX_BIAS_HPH_CNP1 0x06be +#define WCD9335_RX_BIAS_HPH_LOWPOWER 0x06bf +#define WCD9335_RX_BIAS_DIFFLO_PA 0x06c0 +#define WCD9335_RX_BIAS_DIFFLO_REF 0x06c1 +#define WCD9335_RX_BIAS_DIFFLO_LDO 0x06c2 +#define WCD9335_RX_BIAS_SELO_DAC_PA 0x06c3 +#define WCD9335_RX_BIAS_BUCK_RST 0x06c4 +#define WCD9335_RX_BIAS_BUCK_VREF_ERRAMP 0x06c5 +#define WCD9335_RX_BIAS_FLYB_ERRAMP 0x06c6 +#define WCD9335_RX_BIAS_FLYB_BUFF 0x06c7 +#define WCD9335_RX_BIAS_FLYB_MID_RST 0x06c8 +#define WCD9335_HPH_L_STATUS 0x06c9 +#define WCD9335_HPH_R_STATUS 0x06ca +#define WCD9335_HPH_CNP_EN 0x06cb +#define WCD9335_HPH_CNP_WG_CTL 0x06cc +#define WCD9335_HPH_CNP_WG_TIME 0x06cd +#define WCD9335_HPH_OCP_CTL 0x06ce +#define WCD9335_HPH_AUTO_CHOP 0x06cf +#define WCD9335_HPH_CHOP_CTL 0x06d0 +#define WCD9335_HPH_PA_CTL1 0x06d1 +#define WCD9335_HPH_PA_CTL2 0x06d2 +#define WCD9335_HPH_L_EN 0x06d3 +#define WCD9335_HPH_L_TEST 0x06d4 +#define WCD9335_HPH_L_ATEST 0x06d5 +#define WCD9335_HPH_R_EN 0x06d6 +#define WCD9335_HPH_R_TEST 0x06d7 +#define WCD9335_HPH_R_ATEST 0x06d8 +#define WCD9335_HPH_RDAC_CLK_CTL1 0x06d9 +#define WCD9335_HPH_RDAC_CLK_CTL2 0x06da +#define WCD9335_HPH_RDAC_LDO_CTL 0x06db +#define WCD9335_HPH_RDAC_CHOP_CLK_LP_CTL 0x06dc +#define WCD9335_HPH_REFBUFF_UHQA_CTL 0x06dd +#define WCD9335_HPH_REFBUFF_LP_CTL 0x06de +#define WCD9335_HPH_L_DAC_CTL 0x06df +#define WCD9335_HPH_R_DAC_CTL 0x06e0 +#define WCD9335_EAR_EN_REG 0x06e1 +#define WCD9335_EAR_CMBUFF 0x06e2 +#define WCD9335_EAR_ICTL 0x06e3 +#define WCD9335_EAR_EN_DBG_CTL 0x06e4 +#define WCD9335_EAR_CNP 0x06e5 +#define WCD9335_EAR_DAC_CTL_ATEST 0x06e6 +#define WCD9335_EAR_STATUS_REG 0x06e7 +#define WCD9335_EAR_OUT_SHORT 0x06e8 +#define WCD9335_DIFF_LO_MISC 0x06e9 +#define WCD9335_DIFF_LO_LO2_COMPANDER 0x06ea +#define WCD9335_DIFF_LO_LO1_COMPANDER 0x06eb +#define WCD9335_DIFF_LO_COMMON 0x06ec +#define WCD9335_DIFF_LO_BYPASS_EN 0x06ed +#define WCD9335_DIFF_LO_CNP 0x06ee +#define WCD9335_DIFF_LO_CORE_OUT_PROG 0x06ef +#define WCD9335_DIFF_LO_LDO_OUT_PROG 0x06f0 +#define WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ 0x06f1 +#define WCD9335_DIFF_LO_COM_PA_FREQ 0x06f2 +#define WCD9335_DIFF_LO_RESERVED_REG 0x06f3 +#define WCD9335_DIFF_LO_LO1_STATUS_1 0x06f4 +#define WCD9335_DIFF_LO_LO1_STATUS_2 0x06f5 +#define WCD9335_SE_LO_COM1 0x06f6 +#define WCD9335_SE_LO_COM2 0x06f7 +#define WCD9335_SE_LO_LO3_GAIN 0x06f8 +#define WCD9335_SE_LO_LO3_CTRL 0x06f9 +#define WCD9335_SE_LO_LO4_GAIN 0x06fa +#define WCD9335_SE_LO_LO4_CTRL 0x06fb +#define WCD9335_SE_LO_LO3_STATUS 0x06fe +#define WCD9335_SE_LO_LO4_STATUS 0x06ff + +/* Page-10 Registers */ +#define WCD9335_PAGE10_PAGE_REGISTER 0x0a00 +#define WCD9335_CDC_ANC0_CLK_RESET_CTL 0x0a01 +#define WCD9335_CDC_ANC0_MODE_1_CTL 0x0a02 +#define WCD9335_CDC_ANC0_MODE_2_CTL 0x0a03 +#define WCD9335_CDC_ANC0_FF_SHIFT 0x0a04 +#define WCD9335_CDC_ANC0_FB_SHIFT 0x0a05 +#define WCD9335_CDC_ANC0_LPF_FF_A_CTL 0x0a06 +#define WCD9335_CDC_ANC0_LPF_FF_B_CTL 0x0a07 +#define WCD9335_CDC_ANC0_LPF_FB_CTL 0x0a08 +#define WCD9335_CDC_ANC0_SMLPF_CTL 0x0a09 +#define WCD9335_CDC_ANC0_DCFLT_SHIFT_CTL 0x0a0a +#define WCD9335_CDC_ANC0_IIR_ADAPT_CTL 0x0a0b +#define WCD9335_CDC_ANC0_IIR_COEFF_1_CTL 0x0a0c +#define WCD9335_CDC_ANC0_IIR_COEFF_2_CTL 0x0a0d +#define WCD9335_CDC_ANC0_FF_A_GAIN_CTL 0x0a0e +#define WCD9335_CDC_ANC0_FF_B_GAIN_CTL 0x0a0f +#define WCD9335_CDC_ANC0_FB_GAIN_CTL 0x0a10 +#define WCD9335_CDC_ANC1_CLK_RESET_CTL 0x0a19 +#define WCD9335_CDC_ANC1_MODE_1_CTL 0x0a1a +#define WCD9335_CDC_ANC1_MODE_2_CTL 0x0a1b +#define WCD9335_CDC_ANC1_FF_SHIFT 0x0a1c +#define WCD9335_CDC_ANC1_FB_SHIFT 0x0a1d +#define WCD9335_CDC_ANC1_LPF_FF_A_CTL 0x0a1e +#define WCD9335_CDC_ANC1_LPF_FF_B_CTL 0x0a1f +#define WCD9335_CDC_ANC1_LPF_FB_CTL 0x0a20 +#define WCD9335_CDC_ANC1_SMLPF_CTL 0x0a21 +#define WCD9335_CDC_ANC1_DCFLT_SHIFT_CTL 0x0a22 +#define WCD9335_CDC_ANC1_IIR_ADAPT_CTL 0x0a23 +#define WCD9335_CDC_ANC1_IIR_COEFF_1_CTL 0x0a24 +#define WCD9335_CDC_ANC1_IIR_COEFF_2_CTL 0x0a25 +#define WCD9335_CDC_ANC1_FF_A_GAIN_CTL 0x0a26 +#define WCD9335_CDC_ANC1_FF_B_GAIN_CTL 0x0a27 +#define WCD9335_CDC_ANC1_FB_GAIN_CTL 0x0a28 +#define WCD9335_CDC_TX0_TX_PATH_CTL 0x0a31 +#define WCD9335_CDC_TX0_TX_PATH_CFG0 0x0a32 +#define WCD9335_CDC_TX0_TX_PATH_CFG1 0x0a33 +#define WCD9335_CDC_TX0_TX_VOL_CTL 0x0a34 +#define WCD9335_CDC_TX0_TX_PATH_192_CTL 0x0a35 +#define WCD9335_CDC_TX0_TX_PATH_192_CFG 0x0a36 +#define WCD9335_CDC_TX0_TX_PATH_SEC0 0x0a37 +#define WCD9335_CDC_TX0_TX_PATH_SEC1 0x0a38 +#define WCD9335_CDC_TX0_TX_PATH_SEC2 0x0a39 +#define WCD9335_CDC_TX0_TX_PATH_SEC3 0x0a3a +#define WCD9335_CDC_TX0_TX_PATH_SEC4 0x0a3b +#define WCD9335_CDC_TX0_TX_PATH_SEC5 0x0a3c +#define WCD9335_CDC_TX0_TX_PATH_SEC6 0x0a3d +#define WCD9335_CDC_TX0_TX_PATH_SEC7 0x0a3e +#define WCD9335_CDC_TX1_TX_PATH_CTL 0x0a41 +#define WCD9335_CDC_TX1_TX_PATH_CFG0 0x0a42 +#define WCD9335_CDC_TX1_TX_PATH_CFG1 0x0a43 +#define WCD9335_CDC_TX1_TX_VOL_CTL 0x0a44 +#define WCD9335_CDC_TX1_TX_PATH_192_CTL 0x0a45 +#define WCD9335_CDC_TX1_TX_PATH_192_CFG 0x0a46 +#define WCD9335_CDC_TX1_TX_PATH_SEC0 0x0a47 +#define WCD9335_CDC_TX1_TX_PATH_SEC1 0x0a48 +#define WCD9335_CDC_TX1_TX_PATH_SEC2 0x0a49 +#define WCD9335_CDC_TX1_TX_PATH_SEC3 0x0a4a +#define WCD9335_CDC_TX1_TX_PATH_SEC4 0x0a4b +#define WCD9335_CDC_TX1_TX_PATH_SEC5 0x0a4c +#define WCD9335_CDC_TX1_TX_PATH_SEC6 0x0a4d +#define WCD9335_CDC_TX2_TX_PATH_CTL 0x0a51 +#define WCD9335_CDC_TX2_TX_PATH_CFG0 0x0a52 +#define WCD9335_CDC_TX2_TX_PATH_CFG1 0x0a53 +#define WCD9335_CDC_TX2_TX_VOL_CTL 0x0a54 +#define WCD9335_CDC_TX2_TX_PATH_192_CTL 0x0a55 +#define WCD9335_CDC_TX2_TX_PATH_192_CFG 0x0a56 +#define WCD9335_CDC_TX2_TX_PATH_SEC0 0x0a57 +#define WCD9335_CDC_TX2_TX_PATH_SEC1 0x0a58 +#define WCD9335_CDC_TX2_TX_PATH_SEC2 0x0a59 +#define WCD9335_CDC_TX2_TX_PATH_SEC3 0x0a5a +#define WCD9335_CDC_TX2_TX_PATH_SEC4 0x0a5b +#define WCD9335_CDC_TX2_TX_PATH_SEC5 0x0a5c +#define WCD9335_CDC_TX2_TX_PATH_SEC6 0x0a5d +#define WCD9335_CDC_TX3_TX_PATH_CTL 0x0a61 +#define WCD9335_CDC_TX3_TX_PATH_CFG0 0x0a62 +#define WCD9335_CDC_TX3_TX_PATH_CFG1 0x0a63 +#define WCD9335_CDC_TX3_TX_VOL_CTL 0x0a64 +#define WCD9335_CDC_TX3_TX_PATH_192_CTL 0x0a65 +#define WCD9335_CDC_TX3_TX_PATH_192_CFG 0x0a66 +#define WCD9335_CDC_TX3_TX_PATH_SEC0 0x0a67 +#define WCD9335_CDC_TX3_TX_PATH_SEC1 0x0a68 +#define WCD9335_CDC_TX3_TX_PATH_SEC2 0x0a69 +#define WCD9335_CDC_TX3_TX_PATH_SEC3 0x0a6a +#define WCD9335_CDC_TX3_TX_PATH_SEC4 0x0a6b +#define WCD9335_CDC_TX3_TX_PATH_SEC5 0x0a6c +#define WCD9335_CDC_TX3_TX_PATH_SEC6 0x0a6d +#define WCD9335_CDC_TX4_TX_PATH_CTL 0x0a71 +#define WCD9335_CDC_TX4_TX_PATH_CFG0 0x0a72 +#define WCD9335_CDC_TX4_TX_PATH_CFG1 0x0a73 +#define WCD9335_CDC_TX4_TX_VOL_CTL 0x0a74 +#define WCD9335_CDC_TX4_TX_PATH_192_CTL 0x0a75 +#define WCD9335_CDC_TX4_TX_PATH_192_CFG 0x0a76 +#define WCD9335_CDC_TX4_TX_PATH_SEC0 0x0a77 +#define WCD9335_CDC_TX4_TX_PATH_SEC1 0x0a78 +#define WCD9335_CDC_TX4_TX_PATH_SEC2 0x0a79 +#define WCD9335_CDC_TX4_TX_PATH_SEC3 0x0a7a +#define WCD9335_CDC_TX4_TX_PATH_SEC4 0x0a7b +#define WCD9335_CDC_TX4_TX_PATH_SEC5 0x0a7c +#define WCD9335_CDC_TX4_TX_PATH_SEC6 0x0a7d +#define WCD9335_CDC_TX5_TX_PATH_CTL 0x0a81 +#define WCD9335_CDC_TX5_TX_PATH_CFG0 0x0a82 +#define WCD9335_CDC_TX5_TX_PATH_CFG1 0x0a83 +#define WCD9335_CDC_TX5_TX_VOL_CTL 0x0a84 +#define WCD9335_CDC_TX5_TX_PATH_192_CTL 0x0a85 +#define WCD9335_CDC_TX5_TX_PATH_192_CFG 0x0a86 +#define WCD9335_CDC_TX5_TX_PATH_SEC0 0x0a87 +#define WCD9335_CDC_TX5_TX_PATH_SEC1 0x0a88 +#define WCD9335_CDC_TX5_TX_PATH_SEC2 0x0a89 +#define WCD9335_CDC_TX5_TX_PATH_SEC3 0x0a8a +#define WCD9335_CDC_TX5_TX_PATH_SEC4 0x0a8b +#define WCD9335_CDC_TX5_TX_PATH_SEC5 0x0a8c +#define WCD9335_CDC_TX5_TX_PATH_SEC6 0x0a8d +#define WCD9335_CDC_TX6_TX_PATH_CTL 0x0a91 +#define WCD9335_CDC_TX6_TX_PATH_CFG0 0x0a92 +#define WCD9335_CDC_TX6_TX_PATH_CFG1 0x0a93 +#define WCD9335_CDC_TX6_TX_VOL_CTL 0x0a94 +#define WCD9335_CDC_TX6_TX_PATH_192_CTL 0x0a95 +#define WCD9335_CDC_TX6_TX_PATH_192_CFG 0x0a96 +#define WCD9335_CDC_TX6_TX_PATH_SEC0 0x0a97 +#define WCD9335_CDC_TX6_TX_PATH_SEC1 0x0a98 +#define WCD9335_CDC_TX6_TX_PATH_SEC2 0x0a99 +#define WCD9335_CDC_TX6_TX_PATH_SEC3 0x0a9a +#define WCD9335_CDC_TX6_TX_PATH_SEC4 0x0a9b +#define WCD9335_CDC_TX6_TX_PATH_SEC5 0x0a9c +#define WCD9335_CDC_TX6_TX_PATH_SEC6 0x0a9d +#define WCD9335_CDC_TX7_TX_PATH_CTL 0x0aa1 +#define WCD9335_CDC_TX7_TX_PATH_CFG0 0x0aa2 +#define WCD9335_CDC_TX7_TX_PATH_CFG1 0x0aa3 +#define WCD9335_CDC_TX7_TX_VOL_CTL 0x0aa4 +#define WCD9335_CDC_TX7_TX_PATH_192_CTL 0x0aa5 +#define WCD9335_CDC_TX7_TX_PATH_192_CFG 0x0aa6 +#define WCD9335_CDC_TX7_TX_PATH_SEC0 0x0aa7 +#define WCD9335_CDC_TX7_TX_PATH_SEC1 0x0aa8 +#define WCD9335_CDC_TX7_TX_PATH_SEC2 0x0aa9 +#define WCD9335_CDC_TX7_TX_PATH_SEC3 0x0aaa +#define WCD9335_CDC_TX7_TX_PATH_SEC4 0x0aab +#define WCD9335_CDC_TX7_TX_PATH_SEC5 0x0aac +#define WCD9335_CDC_TX7_TX_PATH_SEC6 0x0aad +#define WCD9335_CDC_TX8_TX_PATH_CTL 0x0ab1 +#define WCD9335_CDC_TX8_TX_PATH_CFG0 0x0ab2 +#define WCD9335_CDC_TX8_TX_PATH_CFG1 0x0ab3 +#define WCD9335_CDC_TX8_TX_VOL_CTL 0x0ab4 +#define WCD9335_CDC_TX8_TX_PATH_192_CTL 0x0ab5 +#define WCD9335_CDC_TX8_TX_PATH_192_CFG 0x0ab6 +#define WCD9335_CDC_TX8_TX_PATH_SEC0 0x0ab7 +#define WCD9335_CDC_TX8_TX_PATH_SEC1 0x0ab8 +#define WCD9335_CDC_TX8_TX_PATH_SEC2 0x0ab9 +#define WCD9335_CDC_TX8_TX_PATH_SEC3 0x0aba +#define WCD9335_CDC_TX8_TX_PATH_SEC4 0x0abb +#define WCD9335_CDC_TX8_TX_PATH_SEC5 0x0abc +#define WCD9335_CDC_TX8_TX_PATH_SEC6 0x0abd +#define WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL 0x0ac2 +#define WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0 0x0ac3 +#define WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL 0x0ac6 +#define WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0 0x0ac7 +#define WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL 0x0aca +#define WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0 0x0acb +#define WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL 0x0ace +#define WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0 0x0acf + +/* Page-11 Registers */ +#define WCD9335_PAGE11_PAGE_REGISTER 0x0b00 +#define WCD9335_CDC_COMPANDER1_CTL0 0x0b01 +#define WCD9335_CDC_COMPANDER1_CTL1 0x0b02 +#define WCD9335_CDC_COMPANDER1_CTL2 0x0b03 +#define WCD9335_CDC_COMPANDER1_CTL3 0x0b04 +#define WCD9335_CDC_COMPANDER1_CTL4 0x0b05 +#define WCD9335_CDC_COMPANDER1_CTL5 0x0b06 +#define WCD9335_CDC_COMPANDER1_CTL6 0x0b07 +#define WCD9335_CDC_COMPANDER1_CTL7 0x0b08 +#define WCD9335_CDC_COMPANDER2_CTL0 0x0b09 +#define WCD9335_CDC_COMPANDER2_CTL1 0x0b0a +#define WCD9335_CDC_COMPANDER2_CTL2 0x0b0b +#define WCD9335_CDC_COMPANDER2_CTL3 0x0b0c +#define WCD9335_CDC_COMPANDER2_CTL4 0x0b0d +#define WCD9335_CDC_COMPANDER2_CTL5 0x0b0e +#define WCD9335_CDC_COMPANDER2_CTL6 0x0b0f +#define WCD9335_CDC_COMPANDER2_CTL7 0x0b10 +#define WCD9335_CDC_COMPANDER3_CTL0 0x0b11 +#define WCD9335_CDC_COMPANDER3_CTL1 0x0b12 +#define WCD9335_CDC_COMPANDER3_CTL2 0x0b13 +#define WCD9335_CDC_COMPANDER3_CTL3 0x0b14 +#define WCD9335_CDC_COMPANDER3_CTL4 0x0b15 +#define WCD9335_CDC_COMPANDER3_CTL5 0x0b16 +#define WCD9335_CDC_COMPANDER3_CTL6 0x0b17 +#define WCD9335_CDC_COMPANDER3_CTL7 0x0b18 +#define WCD9335_CDC_COMPANDER4_CTL0 0x0b19 +#define WCD9335_CDC_COMPANDER4_CTL1 0x0b1a +#define WCD9335_CDC_COMPANDER4_CTL2 0x0b1b +#define WCD9335_CDC_COMPANDER4_CTL3 0x0b1c +#define WCD9335_CDC_COMPANDER4_CTL4 0x0b1d +#define WCD9335_CDC_COMPANDER4_CTL5 0x0b1e +#define WCD9335_CDC_COMPANDER4_CTL6 0x0b1f +#define WCD9335_CDC_COMPANDER4_CTL7 0x0b20 +#define WCD9335_CDC_COMPANDER5_CTL0 0x0b21 +#define WCD9335_CDC_COMPANDER5_CTL1 0x0b22 +#define WCD9335_CDC_COMPANDER5_CTL2 0x0b23 +#define WCD9335_CDC_COMPANDER5_CTL3 0x0b24 +#define WCD9335_CDC_COMPANDER5_CTL4 0x0b25 +#define WCD9335_CDC_COMPANDER5_CTL5 0x0b26 +#define WCD9335_CDC_COMPANDER5_CTL6 0x0b27 +#define WCD9335_CDC_COMPANDER5_CTL7 0x0b28 +#define WCD9335_CDC_COMPANDER6_CTL0 0x0b29 +#define WCD9335_CDC_COMPANDER6_CTL1 0x0b2a +#define WCD9335_CDC_COMPANDER6_CTL2 0x0b2b +#define WCD9335_CDC_COMPANDER6_CTL3 0x0b2c +#define WCD9335_CDC_COMPANDER6_CTL4 0x0b2d +#define WCD9335_CDC_COMPANDER6_CTL5 0x0b2e +#define WCD9335_CDC_COMPANDER6_CTL6 0x0b2f +#define WCD9335_CDC_COMPANDER6_CTL7 0x0b30 +#define WCD9335_CDC_COMPANDER7_CTL0 0x0b31 +#define WCD9335_CDC_COMPANDER7_CTL1 0x0b32 +#define WCD9335_CDC_COMPANDER7_CTL2 0x0b33 +#define WCD9335_CDC_COMPANDER7_CTL3 0x0b34 +#define WCD9335_CDC_COMPANDER7_CTL4 0x0b35 +#define WCD9335_CDC_COMPANDER7_CTL5 0x0b36 +#define WCD9335_CDC_COMPANDER7_CTL6 0x0b37 +#define WCD9335_CDC_COMPANDER7_CTL7 0x0b38 +#define WCD9335_CDC_COMPANDER8_CTL0 0x0b39 +#define WCD9335_CDC_COMPANDER8_CTL1 0x0b3a +#define WCD9335_CDC_COMPANDER8_CTL2 0x0b3b +#define WCD9335_CDC_COMPANDER8_CTL3 0x0b3c +#define WCD9335_CDC_COMPANDER8_CTL4 0x0b3d +#define WCD9335_CDC_COMPANDER8_CTL5 0x0b3e +#define WCD9335_CDC_COMPANDER8_CTL6 0x0b3f +#define WCD9335_CDC_COMPANDER8_CTL7 0x0b40 +#define WCD9335_CDC_RX0_RX_PATH_CTL 0x0b41 +#define WCD9335_CDC_RX0_RX_PATH_CFG0 0x0b42 +#define WCD9335_CDC_RX0_RX_PATH_CFG1 0x0b43 +#define WCD9335_CDC_RX0_RX_PATH_CFG2 0x0b44 +#define WCD9335_CDC_RX0_RX_VOL_CTL 0x0b45 +#define WCD9335_CDC_RX0_RX_PATH_MIX_CTL 0x0b46 +#define WCD9335_CDC_RX0_RX_PATH_MIX_CFG 0x0b47 +#define WCD9335_CDC_RX0_RX_VOL_MIX_CTL 0x0b48 +#define WCD9335_CDC_RX0_RX_PATH_SEC0 0x0b49 +#define WCD9335_CDC_RX0_RX_PATH_SEC1 0x0b4a +#define WCD9335_CDC_RX0_RX_PATH_SEC2 0x0b4b +#define WCD9335_CDC_RX0_RX_PATH_SEC3 0x0b4c +#define WCD9335_CDC_RX0_RX_PATH_SEC5 0x0b4e +#define WCD9335_CDC_RX0_RX_PATH_SEC6 0x0b4f +#define WCD9335_CDC_RX0_RX_PATH_SEC7 0x0b50 +#define WCD9335_CDC_RX0_RX_PATH_MIX_SEC0 0x0b51 +#define WCD9335_CDC_RX0_RX_PATH_MIX_SEC1 0x0b52 +#define WCD9335_CDC_RX1_RX_PATH_CTL 0x0b55 +#define WCD9335_CDC_RX1_RX_PATH_CFG0 0x0b56 +#define WCD9335_CDC_RX1_RX_PATH_CFG1 0x0b57 +#define WCD9335_CDC_RX1_RX_PATH_CFG2 0x0b58 +#define WCD9335_CDC_RX1_RX_VOL_CTL 0x0b59 +#define WCD9335_CDC_RX1_RX_PATH_MIX_CTL 0x0b5a +#define WCD9335_CDC_RX1_RX_PATH_MIX_CFG 0x0b5b +#define WCD9335_CDC_RX1_RX_VOL_MIX_CTL 0x0b5c +#define WCD9335_CDC_RX1_RX_PATH_SEC0 0x0b5d +#define WCD9335_CDC_RX1_RX_PATH_SEC1 0x0b5e +#define WCD9335_CDC_RX1_RX_PATH_SEC2 0x0b5f +#define WCD9335_CDC_RX1_RX_PATH_SEC3 0x0b60 +#define WCD9335_CDC_RX1_RX_PATH_SEC4 0x0b61 +#define WCD9335_CDC_RX1_RX_PATH_SEC5 0x0b62 +#define WCD9335_CDC_RX1_RX_PATH_SEC6 0x0b63 +#define WCD9335_CDC_RX1_RX_PATH_SEC7 0x0b64 +#define WCD9335_CDC_RX1_RX_PATH_MIX_SEC0 0x0b65 +#define WCD9335_CDC_RX1_RX_PATH_MIX_SEC1 0x0b66 +#define WCD9335_CDC_RX2_RX_PATH_CTL 0x0b69 +#define WCD9335_CDC_RX2_RX_PATH_CFG0 0x0b6a +#define WCD9335_CDC_RX2_RX_PATH_CFG1 0x0b6b +#define WCD9335_CDC_RX2_RX_PATH_CFG2 0x0b6c +#define WCD9335_CDC_RX2_RX_VOL_CTL 0x0b6d +#define WCD9335_CDC_RX2_RX_PATH_MIX_CTL 0x0b6e +#define WCD9335_CDC_RX2_RX_PATH_MIX_CFG 0x0b6f +#define WCD9335_CDC_RX2_RX_VOL_MIX_CTL 0x0b70 +#define WCD9335_CDC_RX2_RX_PATH_SEC0 0x0b71 +#define WCD9335_CDC_RX2_RX_PATH_SEC1 0x0b72 +#define WCD9335_CDC_RX2_RX_PATH_SEC2 0x0b73 +#define WCD9335_CDC_RX2_RX_PATH_SEC3 0x0b74 +#define WCD9335_CDC_RX2_RX_PATH_SEC4 0x0b75 +#define WCD9335_CDC_RX2_RX_PATH_SEC5 0x0b76 +#define WCD9335_CDC_RX2_RX_PATH_SEC6 0x0b77 +#define WCD9335_CDC_RX2_RX_PATH_SEC7 0x0b78 +#define WCD9335_CDC_RX2_RX_PATH_MIX_SEC0 0x0b79 +#define WCD9335_CDC_RX2_RX_PATH_MIX_SEC1 0x0b7a +#define WCD9335_CDC_RX3_RX_PATH_CTL 0x0b7d +#define WCD9335_CDC_RX3_RX_PATH_CFG0 0x0b7e +#define WCD9335_CDC_RX3_RX_PATH_CFG1 0x0b7f +#define WCD9335_CDC_RX3_RX_PATH_CFG2 0x0b80 +#define WCD9335_CDC_RX3_RX_VOL_CTL 0x0b81 +#define WCD9335_CDC_RX3_RX_PATH_MIX_CTL 0x0b82 +#define WCD9335_CDC_RX3_RX_PATH_MIX_CFG 0x0b83 +#define WCD9335_CDC_RX3_RX_VOL_MIX_CTL 0x0b84 +#define WCD9335_CDC_RX3_RX_PATH_SEC0 0x0b85 +#define WCD9335_CDC_RX3_RX_PATH_SEC1 0x0b86 +#define WCD9335_CDC_RX3_RX_PATH_SEC2 0x0b87 +#define WCD9335_CDC_RX3_RX_PATH_SEC3 0x0b88 +#define WCD9335_CDC_RX3_RX_PATH_SEC5 0x0b8a +#define WCD9335_CDC_RX3_RX_PATH_SEC6 0x0b8b +#define WCD9335_CDC_RX3_RX_PATH_SEC7 0x0b8c +#define WCD9335_CDC_RX3_RX_PATH_MIX_SEC0 0x0b8d +#define WCD9335_CDC_RX3_RX_PATH_MIX_SEC1 0x0b8e +#define WCD9335_CDC_RX4_RX_PATH_CTL 0x0b91 +#define WCD9335_CDC_RX4_RX_PATH_CFG0 0x0b92 +#define WCD9335_CDC_RX4_RX_PATH_CFG1 0x0b93 +#define WCD9335_CDC_RX4_RX_PATH_CFG2 0x0b94 +#define WCD9335_CDC_RX4_RX_VOL_CTL 0x0b95 +#define WCD9335_CDC_RX4_RX_PATH_MIX_CTL 0x0b96 +#define WCD9335_CDC_RX4_RX_PATH_MIX_CFG 0x0b97 +#define WCD9335_CDC_RX4_RX_VOL_MIX_CTL 0x0b98 +#define WCD9335_CDC_RX4_RX_PATH_SEC0 0x0b99 +#define WCD9335_CDC_RX4_RX_PATH_SEC1 0x0b9a +#define WCD9335_CDC_RX4_RX_PATH_SEC2 0x0b9b +#define WCD9335_CDC_RX4_RX_PATH_SEC3 0x0b9c +#define WCD9335_CDC_RX4_RX_PATH_SEC5 0x0b9e +#define WCD9335_CDC_RX4_RX_PATH_SEC6 0x0b9f +#define WCD9335_CDC_RX4_RX_PATH_SEC7 0x0ba0 +#define WCD9335_CDC_RX4_RX_PATH_MIX_SEC0 0x0ba1 +#define WCD9335_CDC_RX4_RX_PATH_MIX_SEC1 0x0ba2 +#define WCD9335_CDC_RX5_RX_PATH_CTL 0x0ba5 +#define WCD9335_CDC_RX5_RX_PATH_CFG0 0x0ba6 +#define WCD9335_CDC_RX5_RX_PATH_CFG1 0x0ba7 +#define WCD9335_CDC_RX5_RX_PATH_CFG2 0x0ba8 +#define WCD9335_CDC_RX5_RX_VOL_CTL 0x0ba9 +#define WCD9335_CDC_RX5_RX_PATH_MIX_CTL 0x0baa +#define WCD9335_CDC_RX5_RX_PATH_MIX_CFG 0x0bab +#define WCD9335_CDC_RX5_RX_VOL_MIX_CTL 0x0bac +#define WCD9335_CDC_RX5_RX_PATH_SEC0 0x0bad +#define WCD9335_CDC_RX5_RX_PATH_SEC1 0x0bae +#define WCD9335_CDC_RX5_RX_PATH_SEC2 0x0baf +#define WCD9335_CDC_RX5_RX_PATH_SEC3 0x0bb0 +#define WCD9335_CDC_RX5_RX_PATH_SEC5 0x0bb2 +#define WCD9335_CDC_RX5_RX_PATH_SEC6 0x0bb3 +#define WCD9335_CDC_RX5_RX_PATH_SEC7 0x0bb4 +#define WCD9335_CDC_RX5_RX_PATH_MIX_SEC0 0x0bb5 +#define WCD9335_CDC_RX5_RX_PATH_MIX_SEC1 0x0bb6 +#define WCD9335_CDC_RX6_RX_PATH_CTL 0x0bb9 +#define WCD9335_CDC_RX6_RX_PATH_CFG0 0x0bba +#define WCD9335_CDC_RX6_RX_PATH_CFG1 0x0bbb +#define WCD9335_CDC_RX6_RX_PATH_CFG2 0x0bbc +#define WCD9335_CDC_RX6_RX_VOL_CTL 0x0bbd +#define WCD9335_CDC_RX6_RX_PATH_MIX_CTL 0x0bbe +#define WCD9335_CDC_RX6_RX_PATH_MIX_CFG 0x0bbf +#define WCD9335_CDC_RX6_RX_VOL_MIX_CTL 0x0bc0 +#define WCD9335_CDC_RX6_RX_PATH_SEC0 0x0bc1 +#define WCD9335_CDC_RX6_RX_PATH_SEC1 0x0bc2 +#define WCD9335_CDC_RX6_RX_PATH_SEC2 0x0bc3 +#define WCD9335_CDC_RX6_RX_PATH_SEC3 0x0bc4 +#define WCD9335_CDC_RX6_RX_PATH_SEC5 0x0bc6 +#define WCD9335_CDC_RX6_RX_PATH_SEC6 0x0bc7 +#define WCD9335_CDC_RX6_RX_PATH_SEC7 0x0bc8 +#define WCD9335_CDC_RX6_RX_PATH_MIX_SEC0 0x0bc9 +#define WCD9335_CDC_RX6_RX_PATH_MIX_SEC1 0x0bca +#define WCD9335_CDC_RX7_RX_PATH_CTL 0x0bcd +#define WCD9335_CDC_RX7_RX_PATH_CFG0 0x0bce +#define WCD9335_CDC_RX7_RX_PATH_CFG1 0x0bcf +#define WCD9335_CDC_RX7_RX_PATH_CFG2 0x0bd0 +#define WCD9335_CDC_RX7_RX_VOL_CTL 0x0bd1 +#define WCD9335_CDC_RX7_RX_PATH_MIX_CTL 0x0bd2 +#define WCD9335_CDC_RX7_RX_PATH_MIX_CFG 0x0bd3 +#define WCD9335_CDC_RX7_RX_VOL_MIX_CTL 0x0bd4 +#define WCD9335_CDC_RX7_RX_PATH_SEC0 0x0bd5 +#define WCD9335_CDC_RX7_RX_PATH_SEC1 0x0bd6 +#define WCD9335_CDC_RX7_RX_PATH_SEC2 0x0bd7 +#define WCD9335_CDC_RX7_RX_PATH_SEC3 0x0bd8 +#define WCD9335_CDC_RX7_RX_PATH_SEC5 0x0bda +#define WCD9335_CDC_RX7_RX_PATH_SEC6 0x0bdb +#define WCD9335_CDC_RX7_RX_PATH_SEC7 0x0bdc +#define WCD9335_CDC_RX7_RX_PATH_MIX_SEC0 0x0bdd +#define WCD9335_CDC_RX7_RX_PATH_MIX_SEC1 0x0bde +#define WCD9335_CDC_RX8_RX_PATH_CTL 0x0be1 +#define WCD9335_CDC_RX8_RX_PATH_CFG0 0x0be2 +#define WCD9335_CDC_RX8_RX_PATH_CFG1 0x0be3 +#define WCD9335_CDC_RX8_RX_PATH_CFG2 0x0be4 +#define WCD9335_CDC_RX8_RX_VOL_CTL 0x0be5 +#define WCD9335_CDC_RX8_RX_PATH_MIX_CTL 0x0be6 +#define WCD9335_CDC_RX8_RX_PATH_MIX_CFG 0x0be7 +#define WCD9335_CDC_RX8_RX_VOL_MIX_CTL 0x0be8 +#define WCD9335_CDC_RX8_RX_PATH_SEC0 0x0be9 +#define WCD9335_CDC_RX8_RX_PATH_SEC1 0x0bea +#define WCD9335_CDC_RX8_RX_PATH_SEC2 0x0beb +#define WCD9335_CDC_RX8_RX_PATH_SEC3 0x0bec +#define WCD9335_CDC_RX8_RX_PATH_SEC5 0x0bee +#define WCD9335_CDC_RX8_RX_PATH_SEC6 0x0bef +#define WCD9335_CDC_RX8_RX_PATH_SEC7 0x0bf0 +#define WCD9335_CDC_RX8_RX_PATH_MIX_SEC0 0x0bf1 +#define WCD9335_CDC_RX8_RX_PATH_MIX_SEC1 0x0bf2 + +/* Page-12 Registers */ +#define WCD9335_PAGE12_PAGE_REGISTER 0x0c00 +#define WCD9335_CDC_CLSH_CRC 0x0c01 +#define WCD9335_CDC_CLSH_DLY_CTRL 0x0c02 +#define WCD9335_CDC_CLSH_DECAY_CTRL 0x0c03 +#define WCD9335_CDC_CLSH_HPH_V_PA 0x0c04 +#define WCD9335_CDC_CLSH_EAR_V_PA 0x0c05 +#define WCD9335_CDC_CLSH_HPH_V_HD 0x0c06 +#define WCD9335_CDC_CLSH_EAR_V_HD 0x0c07 +#define WCD9335_CDC_CLSH_K1_MSB 0x0c08 +#define WCD9335_CDC_CLSH_K1_LSB 0x0c09 +#define WCD9335_CDC_CLSH_K2_MSB 0x0c0a +#define WCD9335_CDC_CLSH_K2_LSB 0x0c0b +#define WCD9335_CDC_CLSH_IDLE_CTRL 0x0c0c +#define WCD9335_CDC_CLSH_IDLE_HPH 0x0c0d +#define WCD9335_CDC_CLSH_IDLE_EAR 0x0c0e +#define WCD9335_CDC_CLSH_TEST0 0x0c0f +#define WCD9335_CDC_CLSH_TEST1 0x0c10 +#define WCD9335_CDC_CLSH_OVR_VREF 0x0c11 +#define WCD9335_CDC_BOOST0_BOOST_PATH_CTL 0x0c19 +#define WCD9335_CDC_BOOST0_BOOST_CTL 0x0c1a +#define WCD9335_CDC_BOOST0_BOOST_CFG1 0x0c1b +#define WCD9335_CDC_BOOST0_BOOST_CFG2 0x0c1c +#define WCD9335_CDC_BOOST1_BOOST_PATH_CTL 0x0c21 +#define WCD9335_CDC_BOOST1_BOOST_CTL 0x0c22 +#define WCD9335_CDC_BOOST1_BOOST_CFG1 0x0c23 +#define WCD9335_CDC_BOOST1_BOOST_CFG2 0x0c24 +#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_0 0x0c29 +#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_1 0x0c2a +#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_2 0x0c2b +#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_3 0x0c2c +#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0 0x0c2d +#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1 0x0c2e +#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2 0x0c2f +#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3 0x0c30 +#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0 0x0c31 +#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1 0x0c32 +#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2 0x0c33 +#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3 0x0c34 +#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_0 0x0c35 +#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_1 0x0c36 +#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_2 0x0c37 +#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_3 0x0c38 +#define WCD9335_SWR_AHB_BRIDGE_ACCESS_CFG 0x0c39 +#define WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS 0x0c3a +#define WCD9335_CDC_VBAT_VBAT_PATH_CTL 0x0c3d +#define WCD9335_CDC_VBAT_VBAT_CFG 0x0c3e +#define WCD9335_CDC_VBAT_VBAT_ADC_CAL1 0x0c3f +#define WCD9335_CDC_VBAT_VBAT_ADC_CAL2 0x0c40 +#define WCD9335_CDC_VBAT_VBAT_ADC_CAL3 0x0c41 +#define WCD9335_CDC_VBAT_VBAT_PK_EST1 0x0c42 +#define WCD9335_CDC_VBAT_VBAT_PK_EST2 0x0c43 +#define WCD9335_CDC_VBAT_VBAT_PK_EST3 0x0c44 +#define WCD9335_CDC_VBAT_VBAT_RF_PROC1 0x0c45 +#define WCD9335_CDC_VBAT_VBAT_RF_PROC2 0x0c46 +#define WCD9335_CDC_VBAT_VBAT_TAC1 0x0c47 +#define WCD9335_CDC_VBAT_VBAT_TAC2 0x0c48 +#define WCD9335_CDC_VBAT_VBAT_TAC3 0x0c49 +#define WCD9335_CDC_VBAT_VBAT_TAC4 0x0c4a +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD1 0x0c4b +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD2 0x0c4c +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD3 0x0c4d +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD4 0x0c4e +#define WCD9335_CDC_VBAT_VBAT_DEBUG1 0x0c4f +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD_MON 0x0c50 +#define WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL 0x0c51 +#define WCD9335_SPLINE_SRC0_CLK_RST_CTL_0 0x0c55 +#define WCD9335_SPLINE_SRC0_STATUS 0x0c56 +#define WCD9335_SPLINE_SRC1_CLK_RST_CTL_0 0x0c6d +#define WCD9335_SPLINE_SRC1_STATUS 0x0c6e +#define WCD9335_SPLINE_SRC2_CLK_RST_CTL_0 0x0c85 +#define WCD9335_SPLINE_SRC2_STATUS 0x0c86 +#define WCD9335_SPLINE_SRC3_CLK_RST_CTL_0 0x0c9d +#define WCD9335_SPLINE_SRC3_STATUS 0x0c9e +#define WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL 0x0cb5 +#define WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1 0x0cb6 +#define WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL 0x0cb9 +#define WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1 0x0cba + +/* Page-13 Registers */ +#define WCD9335_PAGE13_PAGE_REGISTER 0x0d00 +#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0 0x0d01 +#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1 0x0d02 +#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0 0x0d03 +#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1 0x0d04 +#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0 0x0d05 +#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1 0x0d06 +#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0 0x0d07 +#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1 0x0d08 +#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0 0x0d09 +#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1 0x0d0a +#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0 0x0d0b +#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1 0x0d0c +#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0 0x0d0d +#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1 0x0d0e +#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0 0x0d0f +#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1 0x0d10 +#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0 0x0d11 +#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1 0x0d12 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0 0x0d13 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1 0x0d14 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2 0x0d15 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3 0x0d16 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4 0x0d17 +#define WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0 0x0d18 +#define WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1 0x0d19 +#define WCD9335_CDC_RX_INP_MUX_ANC_CFG0 0x0d1a +#define WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0 0x0d1b +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 0x0d1d +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 0x0d1e +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0 0x0d1f +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1 0x0d20 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0 0x0d21 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1 0x0d22 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0 0x0d23 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1 0x0d24 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 0x0d25 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0 0x0d26 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0 0x0d27 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0 0x0d28 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0 0x0d29 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0 0x0d2b +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0 0x0d2c +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0 0x0d2d +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0 0x0d2e +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0 0x0d31 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1 0x0d32 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2 0x0d33 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3 0x0d34 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0 0x0d35 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1 0x0d36 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2 0x0d37 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3 0x0d38 +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0 0x0d3a +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1 0x0d3b +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2 0x0d3c +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3 0x0d3d +#define WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41 +#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42 +#define WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL 0x0d43 +#define WCD9335_CDC_PROX_DETECT_PROX_CTL 0x0d49 +#define WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD0 0x0d4a +#define WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD1 0x0d4b +#define WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB 0x0d4c +#define WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB 0x0d4d +#define WCD9335_CDC_PROX_DETECT_PROX_STATUS 0x0d4e +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_CTRL 0x0d4f +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB 0x0d50 +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB 0x0d51 +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD 0x0d52 +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD 0x0d53 +#define WCD9335_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT 0x0d54 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL 0x0d55 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL 0x0d56 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL 0x0d57 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL 0x0d58 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL 0x0d59 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL 0x0d5a +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL 0x0d5b +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL 0x0d5c +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL 0x0d5d +#define WCD9335_CDC_SIDETONE_IIR0_IIR_CTL 0x0d5e +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL 0x0d5f +#define WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL 0x0d60 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL 0x0d61 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL 0x0d65 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL 0x0d66 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL 0x0d67 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL 0x0d68 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL 0x0d69 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL 0x0d6a +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL 0x0d6b +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL 0x0d6c +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL 0x0d6d +#define WCD9335_CDC_SIDETONE_IIR1_IIR_CTL 0x0d6e +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL 0x0d6f +#define WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL 0x0d70 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL 0x0d71 +#define WCD9335_CDC_TOP_TOP_CFG0 0x0d81 +#define WCD9335_CDC_TOP_TOP_CFG1 0x0d82 +#define WCD9335_CDC_TOP_TOP_CFG2 0x0d83 +#define WCD9335_CDC_TOP_TOP_CFG3 0x0d84 +#define WCD9335_CDC_TOP_TOP_CFG4 0x0d85 +#define WCD9335_CDC_TOP_TOP_CFG5 0x0d86 +#define WCD9335_CDC_TOP_TOP_CFG6 0x0d87 +#define WCD9335_CDC_TOP_TOP_CFG7 0x0d88 +#define WCD9335_CDC_TOP_HPHL_COMP_WR_LSB 0x0d89 +#define WCD9335_CDC_TOP_HPHL_COMP_WR_MSB 0x0d8a +#define WCD9335_CDC_TOP_HPHL_COMP_LUT 0x0d8b +#define WCD9335_CDC_TOP_HPHL_COMP_RD_LSB 0x0d8c +#define WCD9335_CDC_TOP_HPHL_COMP_RD_MSB 0x0d8d +#define WCD9335_CDC_TOP_HPHR_COMP_WR_LSB 0x0d8e +#define WCD9335_CDC_TOP_HPHR_COMP_WR_MSB 0x0d8f +#define WCD9335_CDC_TOP_HPHR_COMP_LUT 0x0d90 +#define WCD9335_CDC_TOP_HPHR_COMP_RD_LSB 0x0d91 +#define WCD9335_CDC_TOP_HPHR_COMP_RD_MSB 0x0d92 +#define WCD9335_CDC_TOP_DIFFL_COMP_WR_LSB 0x0d93 +#define WCD9335_CDC_TOP_DIFFL_COMP_WR_MSB 0x0d94 +#define WCD9335_CDC_TOP_DIFFL_COMP_LUT 0x0d95 +#define WCD9335_CDC_TOP_DIFFL_COMP_RD_LSB 0x0d96 +#define WCD9335_CDC_TOP_DIFFL_COMP_RD_MSB 0x0d97 +#define WCD9335_CDC_TOP_DIFFR_COMP_WR_LSB 0x0d98 +#define WCD9335_CDC_TOP_DIFFR_COMP_WR_MSB 0x0d99 +#define WCD9335_CDC_TOP_DIFFR_COMP_LUT 0x0d9a +#define WCD9335_CDC_TOP_DIFFR_COMP_RD_LSB 0x0d9b +#define WCD9335_CDC_TOP_DIFFR_COMP_RD_MSB 0x0d9c + +/* Page-0x80 Registers */ +#define WCD9335_PAGE80_PAGE_REGISTER 0x8000 +#define WCD9335_TLMM_BIST_MODE_PINCFG 0x8001 +#define WCD9335_TLMM_RF_PA_ON_PINCFG 0x8002 +#define WCD9335_TLMM_INTR1_PINCFG 0x8003 +#define WCD9335_TLMM_INTR2_PINCFG 0x8004 +#define WCD9335_TLMM_SWR_DATA_PINCFG 0x8005 +#define WCD9335_TLMM_SWR_CLK_PINCFG 0x8006 +#define WCD9335_TLMM_SLIMBUS_DATA2_PINCFG 0x8007 +#define WCD9335_TLMM_I2C_CLK_PINCFG 0x8008 +#define WCD9335_TLMM_I2C_DATA_PINCFG 0x8009 +#define WCD9335_TLMM_I2S_RX_SD0_PINCFG 0x800a +#define WCD9335_TLMM_I2S_RX_SD1_PINCFG 0x800b +#define WCD9335_TLMM_I2S_RX_SCK_PINCFG 0x800c +#define WCD9335_TLMM_I2S_RX_WS_PINCFG 0x800d +#define WCD9335_TLMM_I2S_TX_SD0_PINCFG 0x800e +#define WCD9335_TLMM_I2S_TX_SD1_PINCFG 0x800f +#define WCD9335_TLMM_I2S_TX_SCK_PINCFG 0x8010 +#define WCD9335_TLMM_I2S_TX_WS_PINCFG 0x8011 +#define WCD9335_TLMM_DMIC1_CLK_PINCFG 0x8012 +#define WCD9335_TLMM_DMIC1_DATA_PINCFG 0x8013 +#define WCD9335_TLMM_DMIC2_CLK_PINCFG 0x8014 +#define WCD9335_TLMM_DMIC2_DATA_PINCFG 0x8015 +#define WCD9335_TLMM_DMIC3_CLK_PINCFG 0x8016 +#define WCD9335_TLMM_DMIC3_DATA_PINCFG 0x8017 +#define WCD9335_TLMM_JTDI_PINCFG 0x8018 +#define WCD9335_TLMM_JTDO_PINCFG 0x8019 +#define WCD9335_TLMM_JTMS_PINCFG 0x801a +#define WCD9335_TLMM_JTCK_PINCFG 0x801b +#define WCD9335_TLMM_JTRST_PINCFG 0x801c +#define WCD9335_TEST_DEBUG_PIN_CTL_OE_0 0x8031 +#define WCD9335_TEST_DEBUG_PIN_CTL_OE_1 0x8032 +#define WCD9335_TEST_DEBUG_PIN_CTL_OE_2 0x8033 +#define WCD9335_TEST_DEBUG_PIN_CTL_OE_3 0x8034 +#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_0 0x8035 +#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_1 0x8036 +#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_2 0x8037 +#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_3 0x8038 +#define WCD9335_TEST_DEBUG_PAD_DRVCTL 0x8039 +#define WCD9335_TEST_DEBUG_PIN_STATUS 0x803a +#define WCD9335_TEST_DEBUG_NPL_DLY_TEST_1 0x803b +#define WCD9335_TEST_DEBUG_NPL_DLY_TEST_2 0x803c +#define WCD9335_TEST_DEBUG_MEM_CTRL 0x803d +#define WCD9335_TEST_DEBUG_DEBUG_BUS_SEL 0x8041 +#define WCD9335_TEST_DEBUG_DEBUG_JTAG 0x8042 +#define WCD9335_TEST_DEBUG_DEBUG_EN_1 0x8043 +#define WCD9335_TEST_DEBUG_DEBUG_EN_2 0x8044 +#define WCD9335_TEST_DEBUG_DEBUG_EN_3 0x8045 +#define WCD9335_MAX_REGISTER 0x80FF + +/* SLIMBUS Slave Registers */ +#define TASHA_SLIM_PGD_PORT_INT_EN0 (0x30) +#define TASHA_SLIM_PGD_PORT_INT_STATUS_RX_0 (0x34) +#define TASHA_SLIM_PGD_PORT_INT_STATUS_RX_1 (0x35) +#define TASHA_SLIM_PGD_PORT_INT_STATUS_TX_0 (0x36) +#define TASHA_SLIM_PGD_PORT_INT_STATUS_TX_1 (0x37) +#define TASHA_SLIM_PGD_PORT_INT_CLR_RX_0 (0x38) +#define TASHA_SLIM_PGD_PORT_INT_CLR_RX_1 (0x39) +#define TASHA_SLIM_PGD_PORT_INT_CLR_TX_0 (0x3A) +#define TASHA_SLIM_PGD_PORT_INT_CLR_TX_1 (0x3B) +#define TASHA_SLIM_PGD_PORT_INT_RX_SOURCE0 (0x60) +#define TASHA_SLIM_PGD_PORT_INT_TX_SOURCE0 (0x70) + +/* Macros for Packing Register Writes into a U32 */ +#define TASHA_PACKED_REG_SIZE sizeof(u32) + +#define TASHA_CODEC_PACK_ENTRY(reg, mask, val) ((val & 0xff)|\ + ((mask & 0xff) << 8)|((reg & 0xffff) << 16)) +#define TASHA_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xffff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0) +#endif diff --git a/include/linux/mfd/wcd934x/registers.h b/include/linux/mfd/wcd934x/registers.h new file mode 100644 index 0000000000000000000000000000000000000000..a824875096e46d810853b0299c748830a30f1c84 --- /dev/null +++ b/include/linux/mfd/wcd934x/registers.h @@ -0,0 +1,1848 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _WCD934X_REGISTERS_H +#define _WCD934X_REGISTERS_H + +#define WCD934X_PAGE_SIZE 256 +#define WCD934X_NUM_PAGES 256 + +extern const u8 * const wcd934x_reg[WCD934X_NUM_PAGES]; + +enum { + WCD934X_PAGE_0 = 0, + WCD934X_PAGE_1, + WCD934X_PAGE_2, + WCD934X_PAGE_4 = 4, + WCD934X_PAGE_5, + WCD934X_PAGE_6, + WCD934X_PAGE_7, + WCD934X_PAGE_10 = 0xA, + WCD934X_PAGE_11, + WCD934X_PAGE_12, + WCD934X_PAGE_13, + WCD934X_PAGE_14, + WCD934X_PAGE_15, + WCD934X_PAGE_0x50, + WCD934X_PAGE_0X80, +}; + +enum { + WCD934X_WRITE = 0, + WCD934X_READ, + WCD934X_READ_WRITE, +}; + +/* Page-0 Registers */ +#define WCD934X_PAGE0_PAGE_REGISTER 0x0000 +#define WCD934X_CODEC_RPM_CLK_BYPASS 0x0001 +#define WCD934X_CODEC_RPM_CLK_GATE 0x0002 +#define WCD934X_CODEC_RPM_CLK_MCLK_CFG 0x0003 +#define WCD934X_CODEC_RPM_CLK_MCLK2_CFG 0x0004 +#define WCD934X_CODEC_RPM_I2S_DSD_CLK_SEL 0x0005 +#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_BYTE1 0x0022 +#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2 0x0023 +#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE3 0x0024 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_CTL 0x0025 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_TEST0 0x0026 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_TEST1 0x0027 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0 0x0029 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 0x002a +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 0x002b +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT3 0x002c +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT4 0x002d +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT5 0x002e +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT6 0x002f +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT7 0x0030 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT8 0x0031 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9 0x0032 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT10 0x0033 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT11 0x0034 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT12 0x0035 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT13 0x0036 +#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_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO 0x003a +#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_1 0x003b +#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_2 0x003c +#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_3 0x003d +#define WCD934X_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL 0x003e +#define WCD934X_CHIP_TIER_CTRL_SLNQ_WAIT_STATE_CTL 0x003f +#define WCD934X_CHIP_TIER_CTRL_I2C_ACTIVE 0x0040 +#define WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN 0x0041 +#define WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE 0x0042 +#define WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA 0x0043 +#define WCD934X_DATA_HUB_RX0_CFG 0x0051 +#define WCD934X_DATA_HUB_RX1_CFG 0x0052 +#define WCD934X_DATA_HUB_RX2_CFG 0x0053 +#define WCD934X_DATA_HUB_RX3_CFG 0x0054 +#define WCD934X_DATA_HUB_RX4_CFG 0x0055 +#define WCD934X_DATA_HUB_RX5_CFG 0x0056 +#define WCD934X_DATA_HUB_RX6_CFG 0x0057 +#define WCD934X_DATA_HUB_RX7_CFG 0x0058 +#define WCD934X_DATA_HUB_SB_TX0_INP_CFG 0x0061 +#define WCD934X_DATA_HUB_SB_TX1_INP_CFG 0x0062 +#define WCD934X_DATA_HUB_SB_TX2_INP_CFG 0x0063 +#define WCD934X_DATA_HUB_SB_TX3_INP_CFG 0x0064 +#define WCD934X_DATA_HUB_SB_TX4_INP_CFG 0x0065 +#define WCD934X_DATA_HUB_SB_TX5_INP_CFG 0x0066 +#define WCD934X_DATA_HUB_SB_TX6_INP_CFG 0x0067 +#define WCD934X_DATA_HUB_SB_TX7_INP_CFG 0x0068 +#define WCD934X_DATA_HUB_SB_TX8_INP_CFG 0x0069 +#define WCD934X_DATA_HUB_SB_TX9_INP_CFG 0x006a +#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_DATA_HUB_SB_TX14_INP_CFG 0x006f +#define WCD934X_DATA_HUB_SB_TX15_INP_CFG 0x0070 +#define WCD934X_DATA_HUB_I2S_TX0_CFG 0x0071 +#define WCD934X_DATA_HUB_I2S_TX1_0_CFG 0x0073 +#define WCD934X_DATA_HUB_I2S_TX1_1_CFG 0x0074 +#define WCD934X_DATA_HUB_I2S_0_CTL 0x0081 +#define WCD934X_DATA_HUB_I2S_1_CTL 0x0082 +#define WCD934X_DATA_HUB_I2S_2_CTL 0x0083 +#define WCD934X_DATA_HUB_I2S_3_CTL 0x0084 +#define WCD934X_DATA_HUB_I2S_CLKSRC_CTL 0x0085 +#define WCD934X_DATA_HUB_I2S_COMMON_CTL 0x0086 +#define WCD934X_DATA_HUB_I2S_0_TDM_CTL 0x0087 +#define WCD934X_DATA_HUB_I2S_STATUS 0x0088 +#define WCD934X_DMA_RDMA_CTL_0 0x0091 +#define WCD934X_DMA_CH_2_3_CFG_RDMA_0 0x0092 +#define WCD934X_DMA_CH_0_1_CFG_RDMA_0 0x0093 +#define WCD934X_DMA_RDMA_CTL_1 0x0094 +#define WCD934X_DMA_CH_2_3_CFG_RDMA_1 0x0095 +#define WCD934X_DMA_CH_0_1_CFG_RDMA_1 0x0096 +#define WCD934X_DMA_RDMA_CTL_2 0x0097 +#define WCD934X_DMA_CH_2_3_CFG_RDMA_2 0x0098 +#define WCD934X_DMA_CH_0_1_CFG_RDMA_2 0x0099 +#define WCD934X_DMA_RDMA_CTL_3 0x009A +#define WCD934X_DMA_CH_2_3_CFG_RDMA_3 0x009B +#define WCD934X_DMA_CH_0_1_CFG_RDMA_3 0x009C +#define WCD934X_DMA_RDMA_CTL_4 0x009D +#define WCD934X_DMA_CH_2_3_CFG_RDMA_4 0x009E +#define WCD934X_DMA_CH_0_1_CFG_RDMA_4 0x009F +#define WCD934X_DMA_RDMA4_PRT_CFG 0x00b1 +#define WCD934X_DMA_RDMA_SBTX0_7_CFG 0x00b9 +#define WCD934X_DMA_RDMA_SBTX8_11_CFG 0x00ba +#define WCD934X_DMA_WDMA_CTL_0 0x00c1 +#define WCD934X_DMA_CH_4_5_CFG_WDMA_0 0x00c2 +#define WCD934X_DMA_CH_2_3_CFG_WDMA_0 0x00c3 +#define WCD934X_DMA_CH_0_1_CFG_WDMA_0 0x00c4 +#define WCD934X_DMA_WDMA_CTL_1 0x00C6 +#define WCD934X_DMA_CH_4_5_CFG_WDMA_1 0x00C7 +#define WCD934X_DMA_CH_2_3_CFG_WDMA_1 0x00C8 +#define WCD934X_DMA_CH_0_1_CFG_WDMA_1 0x00C9 +#define WCD934X_DMA_WDMA_CTL_2 0x00CB +#define WCD934X_DMA_CH_4_5_CFG_WDMA_2 0x00CC +#define WCD934X_DMA_CH_2_3_CFG_WDMA_2 0x00CD +#define WCD934X_DMA_CH_0_1_CFG_WDMA_2 0x00CE +#define WCD934X_DMA_WDMA_CTL_3 0x00D0 +#define WCD934X_DMA_CH_4_5_CFG_WDMA_3 0x00D1 +#define WCD934X_DMA_CH_2_3_CFG_WDMA_3 0x00D2 +#define WCD934X_DMA_CH_0_1_CFG_WDMA_3 0x00D3 +#define WCD934X_DMA_WDMA_CTL_4 0x00D5 +#define WCD934X_DMA_CH_4_5_CFG_WDMA_4 0x00D6 +#define WCD934X_DMA_CH_2_3_CFG_WDMA_4 0x00D7 +#define WCD934X_DMA_CH_0_1_CFG_WDMA_4 0x00D8 +#define WCD934X_DMA_WDMA0_PRT_CFG 0x00E1 +#define WCD934X_DMA_WDMA3_PRT_CFG 0x00E2 +#define WCD934X_DMA_WDMA4_PRT0_3_CFG 0x00E3 +#define WCD934X_DMA_WDMA4_PRT4_7_CFG 0x00E4 +#define WCD934X_PAGE1_PAGE_REGISTER 0x0100 +#define WCD934X_CPE_FLL_USER_CTL_0 0x0101 +#define WCD934X_CPE_FLL_USER_CTL_1 0x0102 +#define WCD934X_CPE_FLL_USER_CTL_2 0x0103 +#define WCD934X_CPE_FLL_USER_CTL_3 0x0104 +#define WCD934X_CPE_FLL_USER_CTL_4 0x0105 +#define WCD934X_CPE_FLL_USER_CTL_5 0x0106 +#define WCD934X_CPE_FLL_USER_CTL_6 0x0107 +#define WCD934X_CPE_FLL_USER_CTL_7 0x0108 +#define WCD934X_CPE_FLL_USER_CTL_8 0x0109 +#define WCD934X_CPE_FLL_USER_CTL_9 0x010a +#define WCD934X_CPE_FLL_L_VAL_CTL_0 0x010b +#define WCD934X_CPE_FLL_L_VAL_CTL_1 0x010c +#define WCD934X_CPE_FLL_DSM_FRAC_CTL_0 0x010d +#define WCD934X_CPE_FLL_DSM_FRAC_CTL_1 0x010e +#define WCD934X_CPE_FLL_CONFIG_CTL_0 0x010f +#define WCD934X_CPE_FLL_CONFIG_CTL_1 0x0110 +#define WCD934X_CPE_FLL_CONFIG_CTL_2 0x0111 +#define WCD934X_CPE_FLL_CONFIG_CTL_3 0x0112 +#define WCD934X_CPE_FLL_CONFIG_CTL_4 0x0113 +#define WCD934X_CPE_FLL_TEST_CTL_0 0x0114 +#define WCD934X_CPE_FLL_TEST_CTL_1 0x0115 +#define WCD934X_CPE_FLL_TEST_CTL_2 0x0116 +#define WCD934X_CPE_FLL_TEST_CTL_3 0x0117 +#define WCD934X_CPE_FLL_TEST_CTL_4 0x0118 +#define WCD934X_CPE_FLL_TEST_CTL_5 0x0119 +#define WCD934X_CPE_FLL_TEST_CTL_6 0x011a +#define WCD934X_CPE_FLL_TEST_CTL_7 0x011b +#define WCD934X_CPE_FLL_FREQ_CTL_0 0x011c +#define WCD934X_CPE_FLL_FREQ_CTL_1 0x011d +#define WCD934X_CPE_FLL_FREQ_CTL_2 0x011e +#define WCD934X_CPE_FLL_FREQ_CTL_3 0x011f +#define WCD934X_CPE_FLL_SSC_CTL_0 0x0120 +#define WCD934X_CPE_FLL_SSC_CTL_1 0x0121 +#define WCD934X_CPE_FLL_SSC_CTL_2 0x0122 +#define WCD934X_CPE_FLL_SSC_CTL_3 0x0123 +#define WCD934X_CPE_FLL_FLL_MODE 0x0124 +#define WCD934X_CPE_FLL_STATUS_0 0x0125 +#define WCD934X_CPE_FLL_STATUS_1 0x0126 +#define WCD934X_CPE_FLL_STATUS_2 0x0127 +#define WCD934X_CPE_FLL_STATUS_3 0x0128 +#define WCD934X_I2S_FLL_USER_CTL_0 0x0141 +#define WCD934X_I2S_FLL_USER_CTL_1 0x0142 +#define WCD934X_I2S_FLL_USER_CTL_2 0x0143 +#define WCD934X_I2S_FLL_USER_CTL_3 0x0144 +#define WCD934X_I2S_FLL_USER_CTL_4 0x0145 +#define WCD934X_I2S_FLL_USER_CTL_5 0x0146 +#define WCD934X_I2S_FLL_USER_CTL_6 0x0147 +#define WCD934X_I2S_FLL_USER_CTL_7 0x0148 +#define WCD934X_I2S_FLL_USER_CTL_8 0x0149 +#define WCD934X_I2S_FLL_USER_CTL_9 0x014a +#define WCD934X_I2S_FLL_L_VAL_CTL_0 0x014b +#define WCD934X_I2S_FLL_L_VAL_CTL_1 0x014c +#define WCD934X_I2S_FLL_DSM_FRAC_CTL_0 0x014d +#define WCD934X_I2S_FLL_DSM_FRAC_CTL_1 0x014e +#define WCD934X_I2S_FLL_CONFIG_CTL_0 0x014f +#define WCD934X_I2S_FLL_CONFIG_CTL_1 0x0150 +#define WCD934X_I2S_FLL_CONFIG_CTL_2 0x0151 +#define WCD934X_I2S_FLL_CONFIG_CTL_3 0x0152 +#define WCD934X_I2S_FLL_CONFIG_CTL_4 0x0153 +#define WCD934X_I2S_FLL_TEST_CTL_0 0x0154 +#define WCD934X_I2S_FLL_TEST_CTL_1 0x0155 +#define WCD934X_I2S_FLL_TEST_CTL_2 0x0156 +#define WCD934X_I2S_FLL_TEST_CTL_3 0x0157 +#define WCD934X_I2S_FLL_TEST_CTL_4 0x0158 +#define WCD934X_I2S_FLL_TEST_CTL_5 0x0159 +#define WCD934X_I2S_FLL_TEST_CTL_6 0x015a +#define WCD934X_I2S_FLL_TEST_CTL_7 0x015b +#define WCD934X_I2S_FLL_FREQ_CTL_0 0x015c +#define WCD934X_I2S_FLL_FREQ_CTL_1 0x015d +#define WCD934X_I2S_FLL_FREQ_CTL_2 0x015e +#define WCD934X_I2S_FLL_FREQ_CTL_3 0x015f +#define WCD934X_I2S_FLL_SSC_CTL_0 0x0160 +#define WCD934X_I2S_FLL_SSC_CTL_1 0x0161 +#define WCD934X_I2S_FLL_SSC_CTL_2 0x0162 +#define WCD934X_I2S_FLL_SSC_CTL_3 0x0163 +#define WCD934X_I2S_FLL_FLL_MODE 0x0164 +#define WCD934X_I2S_FLL_STATUS_0 0x0165 +#define WCD934X_I2S_FLL_STATUS_1 0x0166 +#define WCD934X_I2S_FLL_STATUS_2 0x0167 +#define WCD934X_I2S_FLL_STATUS_3 0x0168 +#define WCD934X_SB_FLL_USER_CTL_0 0x0181 +#define WCD934X_SB_FLL_USER_CTL_1 0x0182 +#define WCD934X_SB_FLL_USER_CTL_2 0x0183 +#define WCD934X_SB_FLL_USER_CTL_3 0x0184 +#define WCD934X_SB_FLL_USER_CTL_4 0x0185 +#define WCD934X_SB_FLL_USER_CTL_5 0x0186 +#define WCD934X_SB_FLL_USER_CTL_6 0x0187 +#define WCD934X_SB_FLL_USER_CTL_7 0x0188 +#define WCD934X_SB_FLL_USER_CTL_8 0x0189 +#define WCD934X_SB_FLL_USER_CTL_9 0x018a +#define WCD934X_SB_FLL_L_VAL_CTL_0 0x018b +#define WCD934X_SB_FLL_L_VAL_CTL_1 0x018c +#define WCD934X_SB_FLL_DSM_FRAC_CTL_0 0x018d +#define WCD934X_SB_FLL_DSM_FRAC_CTL_1 0x018e +#define WCD934X_SB_FLL_CONFIG_CTL_0 0x018f +#define WCD934X_SB_FLL_CONFIG_CTL_1 0x0190 +#define WCD934X_SB_FLL_CONFIG_CTL_2 0x0191 +#define WCD934X_SB_FLL_CONFIG_CTL_3 0x0192 +#define WCD934X_SB_FLL_CONFIG_CTL_4 0x0193 +#define WCD934X_SB_FLL_TEST_CTL_0 0x0194 +#define WCD934X_SB_FLL_TEST_CTL_1 0x0195 +#define WCD934X_SB_FLL_TEST_CTL_2 0x0196 +#define WCD934X_SB_FLL_TEST_CTL_3 0x0197 +#define WCD934X_SB_FLL_TEST_CTL_4 0x0198 +#define WCD934X_SB_FLL_TEST_CTL_5 0x0199 +#define WCD934X_SB_FLL_TEST_CTL_6 0x019a +#define WCD934X_SB_FLL_TEST_CTL_7 0x019b +#define WCD934X_SB_FLL_FREQ_CTL_0 0x019c +#define WCD934X_SB_FLL_FREQ_CTL_1 0x019d +#define WCD934X_SB_FLL_FREQ_CTL_2 0x019e +#define WCD934X_SB_FLL_FREQ_CTL_3 0x019f +#define WCD934X_SB_FLL_SSC_CTL_0 0x01a0 +#define WCD934X_SB_FLL_SSC_CTL_1 0x01a1 +#define WCD934X_SB_FLL_SSC_CTL_2 0x01a2 +#define WCD934X_SB_FLL_SSC_CTL_3 0x01a3 +#define WCD934X_SB_FLL_FLL_MODE 0x01a4 +#define WCD934X_SB_FLL_STATUS_0 0x01a5 +#define WCD934X_SB_FLL_STATUS_1 0x01a6 +#define WCD934X_SB_FLL_STATUS_2 0x01a7 +#define WCD934X_SB_FLL_STATUS_3 0x01a8 +#define WCD934X_PAGE2_PAGE_REGISTER 0x0200 +#define WCD934X_CPE_SS_CPE_CTL 0x0201 +#define WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0 0x0202 +#define WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1 0x0203 +#define WCD934X_CPE_SS_PWR_CPEFLL_CTL 0x0204 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0 0x0205 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1 0x0206 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE 0x0207 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0 0x0208 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1 0x0209 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2 0x020a +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3 0x020b +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4 0x020c +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5 0x020d +#define WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN 0x020e +#define WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL 0x020f +#define WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL 0x0210 +#define WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL1 0x0211 +#define WCD934X_CPE_SS_US_BUF_INT_PERIOD 0x0212 +#define WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD 0x0213 +#define WCD934X_CPE_SS_SVA_CFG 0x0214 +#define WCD934X_CPE_SS_US_CFG 0x0215 +#define WCD934X_CPE_SS_MAD_CTL 0x0216 +#define WCD934X_CPE_SS_CPAR_CTL 0x0217 +#define WCD934X_CPE_SS_DMIC0_CTL 0x0218 +#define WCD934X_CPE_SS_DMIC1_CTL 0x0219 +#define WCD934X_CPE_SS_DMIC2_CTL 0x021a +#define WCD934X_CPE_SS_DMIC_CFG 0x021b +#define WCD934X_CPE_SS_CPAR_CFG 0x021c +#define WCD934X_CPE_SS_WDOG_CFG 0x021d +#define WCD934X_CPE_SS_BACKUP_INT 0x021e +#define WCD934X_CPE_SS_STATUS 0x021f +#define WCD934X_CPE_SS_CPE_OCD_CFG 0x0220 +#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A 0x0221 +#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B 0x0222 +#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A 0x0223 +#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B 0x0224 +#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A 0x0225 +#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B 0x0226 +#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A 0x0227 +#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B 0x0228 +#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A 0x0229 +#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B 0x022a +#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1A 0x022b +#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1B 0x022c +#define WCD934X_SOC_MAD_MAIN_CTL_1 0x0281 +#define WCD934X_SOC_MAD_MAIN_CTL_2 0x0282 +#define WCD934X_SOC_MAD_AUDIO_CTL_1 0x0283 +#define WCD934X_SOC_MAD_AUDIO_CTL_2 0x0284 +#define WCD934X_SOC_MAD_AUDIO_CTL_3 0x0285 +#define WCD934X_SOC_MAD_AUDIO_CTL_4 0x0286 +#define WCD934X_SOC_MAD_AUDIO_CTL_5 0x0287 +#define WCD934X_SOC_MAD_AUDIO_CTL_6 0x0288 +#define WCD934X_SOC_MAD_AUDIO_CTL_7 0x0289 +#define WCD934X_SOC_MAD_AUDIO_CTL_8 0x028a +#define WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR 0x028b +#define WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL 0x028c +#define WCD934X_SOC_MAD_ULTR_CTL_1 0x028d +#define WCD934X_SOC_MAD_ULTR_CTL_2 0x028e +#define WCD934X_SOC_MAD_ULTR_CTL_3 0x028f +#define WCD934X_SOC_MAD_ULTR_CTL_4 0x0290 +#define WCD934X_SOC_MAD_ULTR_CTL_5 0x0291 +#define WCD934X_SOC_MAD_ULTR_CTL_6 0x0292 +#define WCD934X_SOC_MAD_ULTR_CTL_7 0x0293 +#define WCD934X_SOC_MAD_BEACON_CTL_1 0x0294 +#define WCD934X_SOC_MAD_BEACON_CTL_2 0x0295 +#define WCD934X_SOC_MAD_BEACON_CTL_3 0x0296 +#define WCD934X_SOC_MAD_BEACON_CTL_4 0x0297 +#define WCD934X_SOC_MAD_BEACON_CTL_5 0x0298 +#define WCD934X_SOC_MAD_BEACON_CTL_6 0x0299 +#define WCD934X_SOC_MAD_BEACON_CTL_7 0x029a +#define WCD934X_SOC_MAD_BEACON_CTL_8 0x029b +#define WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR 0x029c +#define WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL 0x029d +#define WCD934X_SOC_MAD_INP_SEL 0x029e +#define WCD934X_PAGE4_PAGE_REGISTER 0x0400 +#define WCD934X_INTR_CFG 0x0401 +#define WCD934X_INTR_CLR_COMMIT 0x0402 +#define WCD934X_INTR_PIN1_MASK0 0x0409 +#define WCD934X_INTR_PIN1_MASK1 0x040a +#define WCD934X_INTR_PIN1_MASK2 0x040b +#define WCD934X_INTR_PIN1_MASK3 0x040c +#define WCD934X_INTR_PIN1_STATUS0 0x0411 +#define WCD934X_INTR_PIN1_STATUS1 0x0412 +#define WCD934X_INTR_PIN1_STATUS2 0x0413 +#define WCD934X_INTR_PIN1_STATUS3 0x0414 +#define WCD934X_INTR_PIN1_CLEAR0 0x0419 +#define WCD934X_INTR_PIN1_CLEAR1 0x041a +#define WCD934X_INTR_PIN1_CLEAR2 0x041b +#define WCD934X_INTR_PIN1_CLEAR3 0x041c +#define WCD934X_INTR_PIN2_MASK3 0x0424 +#define WCD934X_INTR_PIN2_STATUS3 0x042c +#define WCD934X_INTR_PIN2_CLEAR3 0x0434 +#define WCD934X_INTR_CPESS_SUMRY_MASK2 0x043b +#define WCD934X_INTR_CPESS_SUMRY_MASK3 0x043c +#define WCD934X_INTR_CPESS_SUMRY_STATUS2 0x0443 +#define WCD934X_INTR_CPESS_SUMRY_STATUS3 0x0444 +#define WCD934X_INTR_CPESS_SUMRY_CLEAR2 0x044b +#define WCD934X_INTR_CPESS_SUMRY_CLEAR3 0x044c +#define WCD934X_INTR_LEVEL0 0x0461 +#define WCD934X_INTR_LEVEL1 0x0462 +#define WCD934X_INTR_LEVEL2 0x0463 +#define WCD934X_INTR_LEVEL3 0x0464 +#define WCD934X_INTR_BYPASS0 0x0469 +#define WCD934X_INTR_BYPASS1 0x046a +#define WCD934X_INTR_BYPASS2 0x046b +#define WCD934X_INTR_BYPASS3 0x046c +#define WCD934X_INTR_SET0 0x0471 +#define WCD934X_INTR_SET1 0x0472 +#define WCD934X_INTR_SET2 0x0473 +#define WCD934X_INTR_SET3 0x0474 +#define WCD934X_INTR_CODEC_MISC_MASK 0x04b1 +#define WCD934X_INTR_CODEC_MISC_STATUS 0x04b2 +#define WCD934X_INTR_CODEC_MISC_CLEAR 0x04b3 +#define WCD934X_PAGE5_PAGE_REGISTER 0x0500 +#define WCD934X_SLNQ_DIG_DEVICE 0x0501 +#define WCD934X_SLNQ_DIG_REVISION 0x0502 +#define WCD934X_SLNQ_DIG_H_COMMAND 0x0511 +#define WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_MSB 0x0512 +#define WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_LSB 0x0513 +#define WCD934X_SLNQ_DIG_MASTER_ADDRESS_MSB 0x0514 +#define WCD934X_SLNQ_DIG_MASTER_ADDRESS_LSB 0x0515 +#define WCD934X_SLNQ_DIG_SLAVE_ADDRESS_MSB 0x0516 +#define WCD934X_SLNQ_DIG_SLAVE_ADDRESS_LSB 0x0517 +#define WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_MSB 0x0518 +#define WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_LSB 0x0519 +#define WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_MSB 0x051a +#define WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_LSB 0x051b +#define WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_MSB 0x051c +#define WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_LSB 0x051d +#define WCD934X_SLNQ_DIG_COMM_CTL 0x0520 +#define WCD934X_SLNQ_DIG_FRAME_CTRL 0x0542 +#define WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH1_2 0x055c +#define WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH3_4 0x055d +#define WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH5 0x055e +#define WCD934X_SLNQ_DIG_SW_EVENT_RD 0x0561 +#define WCD934X_SLNQ_DIG_SW_EVENT_CTRL 0x0562 +#define WCD934X_SLNQ_DIG_PDM_SELECT_1 0x0563 +#define WCD934X_SLNQ_DIG_PDM_SELECT_2 0x0564 +#define WCD934X_SLNQ_DIG_PDM_SELECT_3 0x0565 +#define WCD934X_SLNQ_DIG_PDM_SAMPLING_FREQ 0x0566 +#define WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_CTL 0x0569 +#define WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_SEL 0x056a +#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_MSB 0x056b +#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_LSB 0x056c +#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_MSB 0x056d +#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_LSB 0x056e +#define WCD934X_SLNQ_DIG_RAM_CNTRL 0x0571 +#define WCD934X_SLNQ_DIG_SRAM_BANK 0x0572 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_0 0x0573 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1 0x0574 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2 0x0575 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3 0x0576 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_4 0x0577 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_5 0x0578 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_6 0x0579 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_7 0x057a +#define WCD934X_SLNQ_DIG_SRAM_BYTE_8 0x057b +#define WCD934X_SLNQ_DIG_SRAM_BYTE_9 0x057c +#define WCD934X_SLNQ_DIG_SRAM_BYTE_A 0x057d +#define WCD934X_SLNQ_DIG_SRAM_BYTE_B 0x057e +#define WCD934X_SLNQ_DIG_SRAM_BYTE_C 0x057f +#define WCD934X_SLNQ_DIG_SRAM_BYTE_D 0x0580 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_E 0x0581 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_F 0x0582 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_10 0x0583 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_11 0x0584 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_12 0x0585 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_13 0x0586 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_14 0x0587 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_15 0x0588 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_16 0x0589 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_17 0x058a +#define WCD934X_SLNQ_DIG_SRAM_BYTE_18 0x058b +#define WCD934X_SLNQ_DIG_SRAM_BYTE_19 0x058c +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1A 0x058d +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1B 0x058e +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1C 0x058f +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1D 0x0590 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1E 0x0591 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1F 0x0592 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_20 0x0593 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_21 0x0594 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_22 0x0595 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_23 0x0596 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_24 0x0597 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_25 0x0598 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_26 0x0599 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_27 0x059a +#define WCD934X_SLNQ_DIG_SRAM_BYTE_28 0x059b +#define WCD934X_SLNQ_DIG_SRAM_BYTE_29 0x059c +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2A 0x059d +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2B 0x059e +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2C 0x059f +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2D 0x05a0 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2E 0x05a1 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2F 0x05a2 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_30 0x05a3 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_31 0x05a4 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_32 0x05a5 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_33 0x05a6 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_34 0x05a7 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_35 0x05a8 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_36 0x05a9 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_37 0x05aa +#define WCD934X_SLNQ_DIG_SRAM_BYTE_38 0x05ab +#define WCD934X_SLNQ_DIG_SRAM_BYTE_39 0x05ac +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3A 0x05ad +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3B 0x05ae +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3C 0x05af +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3D 0x05b0 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3E 0x05b1 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3F 0x05b2 +#define WCD934X_SLNQ_DIG_TOP_CTRL1 0x05b3 +#define WCD934X_SLNQ_DIG_TOP_CTRL2 0x05b4 +#define WCD934X_SLNQ_DIG_PDM_CTRL 0x05b5 +#define WCD934X_SLNQ_DIG_PDM_MUTE_CTRL 0x05b6 +#define WCD934X_SLNQ_DIG_DEC_BYPASS_CTRL 0x05b7 +#define WCD934X_SLNQ_DIG_DEC_BYPASS_STATUS 0x05b8 +#define WCD934X_SLNQ_DIG_DEC_BYPASS_FS 0x05b9 +#define WCD934X_SLNQ_DIG_DEC_BYPASS_IN_SEL 0x05ba +#define WCD934X_SLNQ_DIG_GPOUT_ENABLE 0x05bb +#define WCD934X_SLNQ_DIG_GPOUT_VAL 0x05bc +#define WCD934X_SLNQ_DIG_ANA_INTERRUPT_MASK 0x05be +#define WCD934X_SLNQ_DIG_ANA_INTERRUPT_STATUS 0x05bf +#define WCD934X_SLNQ_DIG_ANA_INTERRUPT_CLR 0x05c0 +#define WCD934X_SLNQ_DIG_IP_TESTING 0x05c1 +#define WCD934X_SLNQ_DIG_INTERRUPT_CNTRL 0x05e3 +#define WCD934X_SLNQ_DIG_INTERRUPT_CNT 0x05e9 +#define WCD934X_SLNQ_DIG_INTERRUPT_CNT_MSB 0x05eb +#define WCD934X_SLNQ_DIG_INTERRUPT_CNT_LSB 0x05ec +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK0 0x05f1 +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK1 0x05f2 +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK2 0x05f3 +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK3 0x05f4 +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK4 0x05f5 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS0 0x05f6 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS1 0x05f7 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS2 0x05f8 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS3 0x05f9 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS4 0x05fa +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR0 0x05fb +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR1 0x05fc +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR2 0x05fd +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR3 0x05fe +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR4 0x05ff +#define WCD934X_ANA_PAGE_REGISTER 0x0600 +#define WCD934X_ANA_BIAS 0x0601 +#define WCD934X_ANA_RCO 0x0603 +#define WCD934X_ANA_PAGE6_SPARE2 0x0604 +#define WCD934X_ANA_PAGE6_SPARE3 0x0605 +#define WCD934X_ANA_BUCK_CTL 0x0606 +#define WCD934X_ANA_BUCK_STATUS 0x0607 +#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_MAD_SETUP 0x060d +#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_MBHC_BTN0 0x061a +#define WCD934X_ANA_MBHC_BTN1 0x061b +#define WCD934X_ANA_MBHC_BTN2 0x061c +#define WCD934X_ANA_MBHC_BTN3 0x061d +#define WCD934X_ANA_MBHC_BTN4 0x061e +#define WCD934X_ANA_MBHC_BTN5 0x061f +#define WCD934X_ANA_MBHC_BTN6 0x0620 +#define WCD934X_ANA_MBHC_BTN7 0x0621 +#define WCD934X_ANA_MICB1 0x0622 +#define WCD934X_ANA_MICB2 0x0623 +#define WCD934X_ANA_MICB2_RAMP 0x0624 +#define WCD934X_ANA_MICB3 0x0625 +#define WCD934X_ANA_MICB4 0x0626 +#define WCD934X_ANA_VBADC 0x0627 +#define WCD934X_BIAS_CTL 0x0628 +#define WCD934X_BIAS_VBG_FINE_ADJ 0x0629 +#define WCD934X_RCO_CTRL_1 0x062e +#define WCD934X_RCO_CTRL_2 0x062f +#define WCD934X_RCO_CAL 0x0630 +#define WCD934X_RCO_CAL_1 0x0631 +#define WCD934X_RCO_CAL_2 0x0632 +#define WCD934X_RCO_TEST_CTRL 0x0633 +#define WCD934X_RCO_CAL_OUT_1 0x0634 +#define WCD934X_RCO_CAL_OUT_2 0x0635 +#define WCD934X_RCO_CAL_OUT_3 0x0636 +#define WCD934X_RCO_CAL_OUT_4 0x0637 +#define WCD934X_RCO_CAL_OUT_5 0x0638 +#define WCD934X_SIDO_MODE_1 0x063a +#define WCD934X_SIDO_MODE_2 0x063b +#define WCD934X_SIDO_MODE_3 0x063c +#define WCD934X_SIDO_MODE_4 0x063d +#define WCD934X_SIDO_VCL_1 0x063e +#define WCD934X_SIDO_VCL_2 0x063f +#define WCD934X_SIDO_VCL_3 0x0640 +#define WCD934X_SIDO_CCL_1 0x0641 +#define WCD934X_SIDO_CCL_2 0x0642 +#define WCD934X_SIDO_CCL_3 0x0643 +#define WCD934X_SIDO_CCL_4 0x0644 +#define WCD934X_SIDO_CCL_5 0x0645 +#define WCD934X_SIDO_CCL_6 0x0646 +#define WCD934X_SIDO_CCL_7 0x0647 +#define WCD934X_SIDO_CCL_8 0x0648 +#define WCD934X_SIDO_CCL_9 0x0649 +#define WCD934X_SIDO_CCL_10 0x064a +#define WCD934X_SIDO_FILTER_1 0x064b +#define WCD934X_SIDO_FILTER_2 0x064c +#define WCD934X_SIDO_DRIVER_1 0x064d +#define WCD934X_SIDO_DRIVER_2 0x064e +#define WCD934X_SIDO_DRIVER_3 0x064f +#define WCD934X_SIDO_CAL_CODE_EXT_1 0x0650 +#define WCD934X_SIDO_CAL_CODE_EXT_2 0x0651 +#define WCD934X_SIDO_CAL_CODE_OUT_1 0x0652 +#define WCD934X_SIDO_CAL_CODE_OUT_2 0x0653 +#define WCD934X_SIDO_TEST_1 0x0654 +#define WCD934X_SIDO_TEST_2 0x0655 +#define WCD934X_MBHC_CTL_CLK 0x0656 +#define WCD934X_MBHC_CTL_ANA 0x0657 +#define WCD934X_MBHC_CTL_SPARE_1 0x0658 +#define WCD934X_MBHC_CTL_SPARE_2 0x0659 +#define WCD934X_MBHC_CTL_BCS 0x065a +#define WCD934X_MBHC_STATUS_SPARE_1 0x065b +#define WCD934X_MBHC_TEST_CTL 0x065c +#define WCD934X_VBADC_SUBBLOCK_EN 0x065d +#define WCD934X_VBADC_IBIAS_FE 0x065e +#define WCD934X_VBADC_BIAS_ADC 0x065f +#define WCD934X_VBADC_FE_CTRL 0x0660 +#define WCD934X_VBADC_ADC_REF 0x0661 +#define WCD934X_VBADC_ADC_IO 0x0662 +#define WCD934X_VBADC_ADC_SAR 0x0663 +#define WCD934X_VBADC_DEBUG 0x0664 +#define WCD934X_LDOH_MODE 0x0667 +#define WCD934X_LDOH_BIAS 0x0668 +#define WCD934X_LDOH_STB_LOADS 0x0669 +#define WCD934X_LDOH_SLOWRAMP 0x066a +#define WCD934X_MICB1_TEST_CTL_1 0x066b +#define WCD934X_MICB1_TEST_CTL_2 0x066c +#define WCD934X_MICB1_TEST_CTL_3 0x066d +#define WCD934X_MICB2_TEST_CTL_1 0x066e +#define WCD934X_MICB2_TEST_CTL_2 0x066f +#define WCD934X_MICB2_TEST_CTL_3 0x0670 +#define WCD934X_MICB3_TEST_CTL_1 0x0671 +#define WCD934X_MICB3_TEST_CTL_2 0x0672 +#define WCD934X_MICB3_TEST_CTL_3 0x0673 +#define WCD934X_MICB4_TEST_CTL_1 0x0674 +#define WCD934X_MICB4_TEST_CTL_2 0x0675 +#define WCD934X_MICB4_TEST_CTL_3 0x0676 +#define WCD934X_TX_COM_ADC_VCM 0x0677 +#define WCD934X_TX_COM_BIAS_ATEST 0x0678 +#define WCD934X_TX_COM_ADC_INT1_IB 0x0679 +#define WCD934X_TX_COM_ADC_INT2_IB 0x067a +#define WCD934X_TX_COM_TXFE_DIV_CTL 0x067b +#define WCD934X_TX_COM_TXFE_DIV_START 0x067c +#define WCD934X_TX_COM_TXFE_DIV_STOP_9P6M 0x067d +#define WCD934X_TX_COM_TXFE_DIV_STOP_12P288M 0x067e +#define WCD934X_TX_1_2_TEST_EN 0x067f +#define WCD934X_TX_1_2_ADC_IB 0x0680 +#define WCD934X_TX_1_2_ATEST_REFCTL 0x0681 +#define WCD934X_TX_1_2_TEST_CTL 0x0682 +#define WCD934X_TX_1_2_TEST_BLK_EN 0x0683 +#define WCD934X_TX_1_2_TXFE_CLKDIV 0x0684 +#define WCD934X_TX_1_2_SAR1_ERR 0x0685 +#define WCD934X_TX_1_2_SAR2_ERR 0x0686 +#define WCD934X_TX_3_4_TEST_EN 0x0687 +#define WCD934X_TX_3_4_ADC_IB 0x0688 +#define WCD934X_TX_3_4_ATEST_REFCTL 0x0689 +#define WCD934X_TX_3_4_TEST_CTL 0x068a +#define WCD934X_TX_3_4_TEST_BLK_EN 0x068b +#define WCD934X_TX_3_4_TXFE_CLKDIV 0x068c +#define WCD934X_TX_3_4_SAR1_ERR 0x068d +#define WCD934X_TX_3_4_SAR2_ERR 0x068e +#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_CLASSH_SPARE 0x06a3 +#define WCD934X_FLYBACK_EN 0x06a4 +#define WCD934X_FLYBACK_VNEG_CTRL_1 0x06a5 +#define WCD934X_FLYBACK_VNEG_CTRL_2 0x06a6 +#define WCD934X_FLYBACK_VNEG_CTRL_3 0x06a7 +#define WCD934X_FLYBACK_VNEG_CTRL_4 0x06a8 +#define WCD934X_FLYBACK_VNEG_CTRL_5 0x06a9 +#define WCD934X_FLYBACK_VNEG_CTRL_6 0x06aa +#define WCD934X_FLYBACK_VNEG_CTRL_7 0x06ab +#define WCD934X_FLYBACK_VNEG_CTRL_8 0x06ac +#define WCD934X_FLYBACK_VNEG_CTRL_9 0x06ad +#define WCD934X_FLYBACK_VNEGDAC_CTRL_1 0x06ae +#define WCD934X_FLYBACK_VNEGDAC_CTRL_2 0x06af +#define WCD934X_FLYBACK_VNEGDAC_CTRL_3 0x06b0 +#define WCD934X_FLYBACK_CTRL_1 0x06b1 +#define WCD934X_FLYBACK_TEST_CTL 0x06b2 +#define WCD934X_RX_AUX_SW_CTL 0x06b3 +#define WCD934X_RX_PA_AUX_IN_CONN 0x06b4 +#define WCD934X_RX_TIMER_DIV 0x06b5 +#define WCD934X_RX_OCP_CTL 0x06b6 +#define WCD934X_RX_OCP_COUNT 0x06b7 +#define WCD934X_RX_BIAS_EAR_DAC 0x06b8 +#define WCD934X_RX_BIAS_EAR_AMP 0x06b9 +#define WCD934X_RX_BIAS_HPH_LDO 0x06ba +#define WCD934X_RX_BIAS_HPH_PA 0x06bb +#define WCD934X_RX_BIAS_HPH_RDACBUFF_CNP2 0x06bc +#define WCD934X_RX_BIAS_HPH_RDAC_LDO 0x06bd +#define WCD934X_RX_BIAS_HPH_CNP1 0x06be +#define WCD934X_RX_BIAS_HPH_LOWPOWER 0x06bf +#define WCD934X_RX_BIAS_DIFFLO_PA 0x06c0 +#define WCD934X_RX_BIAS_DIFFLO_REF 0x06c1 +#define WCD934X_RX_BIAS_DIFFLO_LDO 0x06c2 +#define WCD934X_RX_BIAS_SELO_DAC_PA 0x06c3 +#define WCD934X_RX_BIAS_BUCK_RST 0x06c4 +#define WCD934X_RX_BIAS_BUCK_VREF_ERRAMP 0x06c5 +#define WCD934X_RX_BIAS_FLYB_ERRAMP 0x06c6 +#define WCD934X_RX_BIAS_FLYB_BUFF 0x06c7 +#define WCD934X_RX_BIAS_FLYB_MID_RST 0x06c8 +#define WCD934X_HPH_L_STATUS 0x06c9 +#define WCD934X_HPH_R_STATUS 0x06ca +#define WCD934X_HPH_CNP_EN 0x06cb +#define WCD934X_HPH_CNP_WG_CTL 0x06cc +#define WCD934X_HPH_CNP_WG_TIME 0x06cd +#define WCD934X_HPH_OCP_CTL 0x06ce +#define WCD934X_HPH_AUTO_CHOP 0x06cf +#define WCD934X_HPH_CHOP_CTL 0x06d0 +#define WCD934X_HPH_PA_CTL1 0x06d1 +#define WCD934X_HPH_PA_CTL2 0x06d2 +#define WCD934X_HPH_L_EN 0x06d3 +#define WCD934X_HPH_L_TEST 0x06d4 +#define WCD934X_HPH_L_ATEST 0x06d5 +#define WCD934X_HPH_R_EN 0x06d6 +#define WCD934X_HPH_R_TEST 0x06d7 +#define WCD934X_HPH_R_ATEST 0x06d8 +#define WCD934X_HPH_RDAC_CLK_CTL1 0x06d9 +#define WCD934X_HPH_RDAC_CLK_CTL2 0x06da +#define WCD934X_HPH_RDAC_LDO_CTL 0x06db +#define WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL 0x06dc +#define WCD934X_HPH_REFBUFF_UHQA_CTL 0x06dd +#define WCD934X_HPH_REFBUFF_LP_CTL 0x06de +#define WCD934X_HPH_L_DAC_CTL 0x06df +#define WCD934X_HPH_R_DAC_CTL 0x06e0 +#define WCD934X_EAR_EN_REG 0x06e1 +#define WCD934X_EAR_CMBUFF 0x06e2 +#define WCD934X_EAR_ICTL 0x06e3 +#define WCD934X_EAR_EN_DBG_CTL 0x06e4 +#define WCD934X_EAR_CNP 0x06e5 +#define WCD934X_EAR_DAC_CTL_ATEST 0x06e6 +#define WCD934X_EAR_STATUS_REG 0x06e7 +#define WCD934X_EAR_EAR_MISC 0x06e8 +#define WCD934X_DIFF_LO_MISC 0x06e9 +#define WCD934X_DIFF_LO_LO2_COMPANDER 0x06ea +#define WCD934X_DIFF_LO_LO1_COMPANDER 0x06eb +#define WCD934X_DIFF_LO_COMMON 0x06ec +#define WCD934X_DIFF_LO_BYPASS_EN 0x06ed +#define WCD934X_DIFF_LO_CNP 0x06ee +#define WCD934X_DIFF_LO_CORE_OUT_PROG 0x06ef +#define WCD934X_DIFF_LO_LDO_OUT_PROG 0x06f0 +#define WCD934X_DIFF_LO_COM_SWCAP_REFBUF_FREQ 0x06f1 +#define WCD934X_DIFF_LO_COM_PA_FREQ 0x06f2 +#define WCD934X_DIFF_LO_RESERVED_REG 0x06f3 +#define WCD934X_DIFF_LO_LO1_STATUS_1 0x06f4 +#define WCD934X_DIFF_LO_LO1_STATUS_2 0x06f5 +#define WCD934X_ANA_NEW_PAGE_REGISTER 0x0700 +#define WCD934X_HPH_NEW_ANA_HPH2 0x0701 +#define WCD934X_HPH_NEW_ANA_HPH3 0x0702 +#define WCD934X_SLNQ_ANA_EN 0x0703 +#define WCD934X_SLNQ_ANA_STATUS 0x0704 +#define WCD934X_SLNQ_ANA_LDO_CONFIG 0x0705 +#define WCD934X_SLNQ_ANA_LDO_OCP_CONFIG 0x0706 +#define WCD934X_SLNQ_ANA_TX_LDO_CONFIG 0x0707 +#define WCD934X_SLNQ_ANA_TX_DRV_CONFIG 0x0708 +#define WCD934X_SLNQ_ANA_RX_CONFIG_1 0x0709 +#define WCD934X_SLNQ_ANA_RX_CONFIG_2 0x070a +#define WCD934X_SLNQ_ANA_PLL_ENABLES 0x070b +#define WCD934X_SLNQ_ANA_PLL_PRESET 0x070c +#define WCD934X_SLNQ_ANA_PLL_STATUS 0x070d +#define WCD934X_CLK_SYS_PLL_ENABLES 0x070e +#define WCD934X_CLK_SYS_PLL_PRESET 0x070f +#define WCD934X_CLK_SYS_PLL_STATUS 0x0710 +#define WCD934X_CLK_SYS_MCLK_PRG 0x0711 +#define WCD934X_CLK_SYS_MCLK2_PRG1 0x0712 +#define WCD934X_CLK_SYS_MCLK2_PRG2 0x0713 +#define WCD934X_CLK_SYS_XO_PRG 0x0714 +#define WCD934X_CLK_SYS_XO_CAP_XTP 0x0715 +#define WCD934X_CLK_SYS_XO_CAP_XTM 0x0716 +#define WCD934X_BOOST_BST_EN_DLY 0x0718 +#define WCD934X_BOOST_CTRL_ILIM 0x0719 +#define WCD934X_BOOST_VOUT_SETTING 0x071a +#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_MBHC_NEW_ELECT_REM_CLAMP_CTL 0x071f +#define WCD934X_MBHC_NEW_CTL_1 0x0720 +#define WCD934X_MBHC_NEW_CTL_2 0x0721 +#define WCD934X_MBHC_NEW_PLUG_DETECT_CTL 0x0722 +#define WCD934X_MBHC_NEW_ZDET_ANA_CTL 0x0723 +#define WCD934X_MBHC_NEW_ZDET_RAMP_CTL 0x0724 +#define WCD934X_MBHC_NEW_FSM_STATUS 0x0725 +#define WCD934X_MBHC_NEW_ADC_RESULT 0x0726 +#define WCD934X_TX_NEW_AMIC_4_5_SEL 0x0727 +#define WCD934X_VBADC_NEW_ADC_MODE 0x072f +#define WCD934X_VBADC_NEW_ADC_DOUTMSB 0x0730 +#define WCD934X_VBADC_NEW_ADC_DOUTLSB 0x0731 +#define WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL 0x0732 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL 0x0733 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L 0x0733 +#define WCD934X_HPH_NEW_INT_RDAC_VREF_CTL 0x0734 +#define WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL 0x0735 +#define WCD934X_HPH_NEW_INT_RDAC_MISC1 0x0736 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R 0x0736 +#define WCD934X_HPH_NEW_INT_PA_MISC1 0x0737 +#define WCD934X_HPH_NEW_INT_PA_MISC2 0x0738 +#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC 0x0739 +#define WCD934X_HPH_NEW_INT_HPH_TIMER1 0x073a +#define WCD934X_HPH_NEW_INT_HPH_TIMER2 0x073b +#define WCD934X_HPH_NEW_INT_HPH_TIMER3 0x073c +#define WCD934X_HPH_NEW_INT_HPH_TIMER4 0x073d +#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC2 0x073e +#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC3 0x073f +#define WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI 0x0745 +#define WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_ULP 0x0746 +#define WCD934X_RX_NEW_INT_HPH_RDAC_LDO_LP 0x0747 +#define WCD934X_SLNQ_INT_ANA_INT_LDO_TEST 0x074b +#define WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_1 0x074c +#define WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_2 0x074d +#define WCD934X_SLNQ_INT_ANA_INT_TX_LDO_TEST 0x074e +#define WCD934X_SLNQ_INT_ANA_INT_TX_DRV_TEST 0x074f +#define WCD934X_SLNQ_INT_ANA_INT_RX_TEST 0x0750 +#define WCD934X_SLNQ_INT_ANA_INT_RX_TEST_STATUS 0x0751 +#define WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_1 0x0752 +#define WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_2 0x0753 +#define WCD934X_SLNQ_INT_ANA_INT_CLK_CTRL 0x0754 +#define WCD934X_SLNQ_INT_ANA_INT_RESERVED_1 0x0755 +#define WCD934X_SLNQ_INT_ANA_INT_RESERVED_2 0x0756 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG0 0x0757 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG1 0x0758 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG0 0x0759 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG1 0x075a +#define WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG0 0x075b +#define WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG1 0x075c +#define WCD934X_SLNQ_INT_ANA_INT_PLL_L_VAL 0x075d +#define WCD934X_SLNQ_INT_ANA_INT_PLL_M_VAL 0x075e +#define WCD934X_SLNQ_INT_ANA_INT_PLL_N_VAL 0x075f +#define WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG0 0x0760 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_PFD_CP_DSM_PROG 0x0761 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_VCO_PROG 0x0762 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG1 0x0763 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_LDO_LOCK_CFG 0x0764 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_DIG_LOCK_DET_CFG 0x0765 +#define WCD934X_CLK_SYS_INT_POST_DIV_REG0 0x076c +#define WCD934X_CLK_SYS_INT_POST_DIV_REG1 0x076d +#define WCD934X_CLK_SYS_INT_REF_DIV_REG0 0x076e +#define WCD934X_CLK_SYS_INT_REF_DIV_REG1 0x076f +#define WCD934X_CLK_SYS_INT_FILTER_REG0 0x0770 +#define WCD934X_CLK_SYS_INT_FILTER_REG1 0x0771 +#define WCD934X_CLK_SYS_INT_PLL_L_VAL 0x0772 +#define WCD934X_CLK_SYS_INT_PLL_M_VAL 0x0773 +#define WCD934X_CLK_SYS_INT_PLL_N_VAL 0x0774 +#define WCD934X_CLK_SYS_INT_TEST_REG0 0x0775 +#define WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG 0x0776 +#define WCD934X_CLK_SYS_INT_VCO_PROG 0x0777 +#define WCD934X_CLK_SYS_INT_TEST_REG1 0x0778 +#define WCD934X_CLK_SYS_INT_LDO_LOCK_CFG 0x0779 +#define WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG 0x077a +#define WCD934X_CLK_SYS_INT_CLK_TEST1 0x077b +#define WCD934X_CLK_SYS_INT_CLK_TEST2 0x077c +#define WCD934X_CLK_SYS_INT_CLK_TEST3 0x077d +#define WCD934X_CLK_SYS_INT_XO_TEST1 0x077e +#define WCD934X_CLK_SYS_INT_XO_TEST2 0x077f +#define WCD934X_BOOST_INT_VCOMP_HYST 0x0787 +#define WCD934X_BOOST_INT_VLOOP_FILTER 0x0788 +#define WCD934X_BOOST_INT_CTRL_IDELTA 0x0789 +#define WCD934X_BOOST_INT_CTRL_ILIM_STARTUP 0x078a +#define WCD934X_BOOST_INT_CTRL_MIN_ONTIME 0x078b +#define WCD934X_BOOST_INT_CTRL_MAX_ONTIME 0x078c +#define WCD934X_BOOST_INT_CTRL_TIMING 0x078d +#define WCD934X_BOOST_INT_TMUX_A_D 0x078e +#define WCD934X_BOOST_INT_SW_DRV_CNTL 0x078f +#define WCD934X_BOOST_INT_SPARE1 0x0790 +#define WCD934X_BOOST_INT_SPARE2 0x0791 +#define WCD934X_SIDO_NEW_INT_RAMP_STATUS 0x0796 +#define WCD934X_SIDO_NEW_INT_SPARE_1 0x0797 +#define WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A 0x0798 +#define WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D 0x0799 +#define WCD934X_SIDO_NEW_INT_RAMP_INC_WAIT 0x079a +#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL 0x079b +#define WCD934X_SIDO_NEW_INT_RAMP_IBLEED_CTL 0x079c +#define WCD934X_SIDO_NEW_INT_DEBUG_CPROVR_TEST 0x079d +#define WCD934X_SIDO_NEW_INT_RAMP_CTL_A 0x079e +#define WCD934X_SIDO_NEW_INT_RAMP_CTL_D 0x079f +#define WCD934X_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD 0x07a0 +#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1 0x07a1 +#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2 0x07a2 +#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3 0x07a3 +#define WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1 0x07a4 +#define WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2 0x07a5 +#define WCD934X_MBHC_NEW_INT_SLNQ_HPF 0x07af +#define WCD934X_MBHC_NEW_INT_SLNQ_REF 0x07b0 +#define WCD934X_MBHC_NEW_INT_SLNQ_COMP 0x07b1 +#define WCD934X_MBHC_NEW_INT_SPARE_2 0x07b2 +#define WCD934X_PAGE10_PAGE_REGISTER 0x0a00 +#define WCD934X_CDC_ANC0_CLK_RESET_CTL 0x0a01 +#define WCD934X_CDC_ANC0_MODE_1_CTL 0x0a02 +#define WCD934X_CDC_ANC0_MODE_2_CTL 0x0a03 +#define WCD934X_CDC_ANC0_FF_SHIFT 0x0a04 +#define WCD934X_CDC_ANC0_FB_SHIFT 0x0a05 +#define WCD934X_CDC_ANC0_LPF_FF_A_CTL 0x0a06 +#define WCD934X_CDC_ANC0_LPF_FF_B_CTL 0x0a07 +#define WCD934X_CDC_ANC0_LPF_FB_CTL 0x0a08 +#define WCD934X_CDC_ANC0_SMLPF_CTL 0x0a09 +#define WCD934X_CDC_ANC0_DCFLT_SHIFT_CTL 0x0a0a +#define WCD934X_CDC_ANC0_IIR_ADAPT_CTL 0x0a0b +#define WCD934X_CDC_ANC0_IIR_COEFF_1_CTL 0x0a0c +#define WCD934X_CDC_ANC0_IIR_COEFF_2_CTL 0x0a0d +#define WCD934X_CDC_ANC0_FF_A_GAIN_CTL 0x0a0e +#define WCD934X_CDC_ANC0_FF_B_GAIN_CTL 0x0a0f +#define WCD934X_CDC_ANC0_FB_GAIN_CTL 0x0a10 +#define WCD934X_CDC_ANC0_RC_COMMON_CTL 0x0a11 +#define WCD934X_CDC_ANC0_FIFO_COMMON_CTL 0x0a13 +#define WCD934X_CDC_ANC0_RC0_STATUS_FMIN_CNTR 0x0a14 +#define WCD934X_CDC_ANC0_RC1_STATUS_FMIN_CNTR 0x0a15 +#define WCD934X_CDC_ANC0_RC0_STATUS_FMAX_CNTR 0x0a16 +#define WCD934X_CDC_ANC0_RC1_STATUS_FMAX_CNTR 0x0a17 +#define WCD934X_CDC_ANC0_STATUS_FIFO 0x0a18 +#define WCD934X_CDC_ANC1_CLK_RESET_CTL 0x0a19 +#define WCD934X_CDC_ANC1_MODE_1_CTL 0x0a1a +#define WCD934X_CDC_ANC1_MODE_2_CTL 0x0a1b +#define WCD934X_CDC_ANC1_FF_SHIFT 0x0a1c +#define WCD934X_CDC_ANC1_FB_SHIFT 0x0a1d +#define WCD934X_CDC_ANC1_LPF_FF_A_CTL 0x0a1e +#define WCD934X_CDC_ANC1_LPF_FF_B_CTL 0x0a1f +#define WCD934X_CDC_ANC1_LPF_FB_CTL 0x0a20 +#define WCD934X_CDC_ANC1_SMLPF_CTL 0x0a21 +#define WCD934X_CDC_ANC1_DCFLT_SHIFT_CTL 0x0a22 +#define WCD934X_CDC_ANC1_IIR_ADAPT_CTL 0x0a23 +#define WCD934X_CDC_ANC1_IIR_COEFF_1_CTL 0x0a24 +#define WCD934X_CDC_ANC1_IIR_COEFF_2_CTL 0x0a25 +#define WCD934X_CDC_ANC1_FF_A_GAIN_CTL 0x0a26 +#define WCD934X_CDC_ANC1_FF_B_GAIN_CTL 0x0a27 +#define WCD934X_CDC_ANC1_FB_GAIN_CTL 0x0a28 +#define WCD934X_CDC_ANC1_RC_COMMON_CTL 0x0a29 +#define WCD934X_CDC_ANC1_FIFO_COMMON_CTL 0x0a2b +#define WCD934X_CDC_ANC1_RC0_STATUS_FMIN_CNTR 0x0a2c +#define WCD934X_CDC_ANC1_RC1_STATUS_FMIN_CNTR 0x0a2d +#define WCD934X_CDC_ANC1_RC0_STATUS_FMAX_CNTR 0x0a2e +#define WCD934X_CDC_ANC1_RC1_STATUS_FMAX_CNTR 0x0a2f +#define WCD934X_CDC_ANC1_STATUS_FIFO 0x0a30 +#define WCD934X_CDC_TX0_TX_PATH_CTL 0x0a31 +#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_SEC0 0x0a37 +#define WCD934X_CDC_TX0_TX_PATH_SEC1 0x0a38 +#define WCD934X_CDC_TX0_TX_PATH_SEC2 0x0a39 +#define WCD934X_CDC_TX0_TX_PATH_SEC3 0x0a3a +#define WCD934X_CDC_TX0_TX_PATH_SEC4 0x0a3b +#define WCD934X_CDC_TX0_TX_PATH_SEC5 0x0a3c +#define WCD934X_CDC_TX0_TX_PATH_SEC6 0x0a3d +#define WCD934X_CDC_TX0_TX_PATH_SEC7 0x0a3e +#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_TX1_TX_PATH_192_CTL 0x0a45 +#define WCD934X_CDC_TX1_TX_PATH_192_CFG 0x0a46 +#define WCD934X_CDC_TX1_TX_PATH_SEC0 0x0a47 +#define WCD934X_CDC_TX1_TX_PATH_SEC1 0x0a48 +#define WCD934X_CDC_TX1_TX_PATH_SEC2 0x0a49 +#define WCD934X_CDC_TX1_TX_PATH_SEC3 0x0a4a +#define WCD934X_CDC_TX1_TX_PATH_SEC4 0x0a4b +#define WCD934X_CDC_TX1_TX_PATH_SEC5 0x0a4c +#define WCD934X_CDC_TX1_TX_PATH_SEC6 0x0a4d +#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_TX2_TX_PATH_192_CTL 0x0a55 +#define WCD934X_CDC_TX2_TX_PATH_192_CFG 0x0a56 +#define WCD934X_CDC_TX2_TX_PATH_SEC0 0x0a57 +#define WCD934X_CDC_TX2_TX_PATH_SEC1 0x0a58 +#define WCD934X_CDC_TX2_TX_PATH_SEC2 0x0a59 +#define WCD934X_CDC_TX2_TX_PATH_SEC3 0x0a5a +#define WCD934X_CDC_TX2_TX_PATH_SEC4 0x0a5b +#define WCD934X_CDC_TX2_TX_PATH_SEC5 0x0a5c +#define WCD934X_CDC_TX2_TX_PATH_SEC6 0x0a5d +#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_TX3_TX_PATH_SEC0 0x0a67 +#define WCD934X_CDC_TX3_TX_PATH_SEC1 0x0a68 +#define WCD934X_CDC_TX3_TX_PATH_SEC2 0x0a69 +#define WCD934X_CDC_TX3_TX_PATH_SEC3 0x0a6a +#define WCD934X_CDC_TX3_TX_PATH_SEC4 0x0a6b +#define WCD934X_CDC_TX3_TX_PATH_SEC5 0x0a6c +#define WCD934X_CDC_TX3_TX_PATH_SEC6 0x0a6d +#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_TX4_TX_PATH_SEC0 0x0a77 +#define WCD934X_CDC_TX4_TX_PATH_SEC1 0x0a78 +#define WCD934X_CDC_TX4_TX_PATH_SEC2 0x0a79 +#define WCD934X_CDC_TX4_TX_PATH_SEC3 0x0a7a +#define WCD934X_CDC_TX4_TX_PATH_SEC4 0x0a7b +#define WCD934X_CDC_TX4_TX_PATH_SEC5 0x0a7c +#define WCD934X_CDC_TX4_TX_PATH_SEC6 0x0a7d +#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_TX5_TX_PATH_SEC0 0x0a87 +#define WCD934X_CDC_TX5_TX_PATH_SEC1 0x0a88 +#define WCD934X_CDC_TX5_TX_PATH_SEC2 0x0a89 +#define WCD934X_CDC_TX5_TX_PATH_SEC3 0x0a8a +#define WCD934X_CDC_TX5_TX_PATH_SEC4 0x0a8b +#define WCD934X_CDC_TX5_TX_PATH_SEC5 0x0a8c +#define WCD934X_CDC_TX5_TX_PATH_SEC6 0x0a8d +#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_TX6_TX_PATH_SEC0 0x0a97 +#define WCD934X_CDC_TX6_TX_PATH_SEC1 0x0a98 +#define WCD934X_CDC_TX6_TX_PATH_SEC2 0x0a99 +#define WCD934X_CDC_TX6_TX_PATH_SEC3 0x0a9a +#define WCD934X_CDC_TX6_TX_PATH_SEC4 0x0a9b +#define WCD934X_CDC_TX6_TX_PATH_SEC5 0x0a9c +#define WCD934X_CDC_TX6_TX_PATH_SEC6 0x0a9d +#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_TX7_TX_PATH_SEC0 0x0aa7 +#define WCD934X_CDC_TX7_TX_PATH_SEC1 0x0aa8 +#define WCD934X_CDC_TX7_TX_PATH_SEC2 0x0aa9 +#define WCD934X_CDC_TX7_TX_PATH_SEC3 0x0aaa +#define WCD934X_CDC_TX7_TX_PATH_SEC4 0x0aab +#define WCD934X_CDC_TX7_TX_PATH_SEC5 0x0aac +#define WCD934X_CDC_TX7_TX_PATH_SEC6 0x0aad +#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_TX8_TX_PATH_SEC0 0x0ab7 +#define WCD934X_CDC_TX8_TX_PATH_SEC1 0x0ab8 +#define WCD934X_CDC_TX8_TX_PATH_SEC2 0x0ab9 +#define WCD934X_CDC_TX8_TX_PATH_SEC3 0x0aba +#define WCD934X_CDC_TX8_TX_PATH_SEC4 0x0abb +#define WCD934X_CDC_TX8_TX_PATH_SEC5 0x0abc +#define WCD934X_CDC_TX8_TX_PATH_SEC6 0x0abd +#define WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL 0x0ac2 +#define WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0 0x0ac3 +#define WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL 0x0ac6 +#define WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0 0x0ac7 +#define WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL 0x0aca +#define WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0 0x0acb +#define WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL 0x0ace +#define WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0 0x0acf +#define WCD934X_PAGE11_PAGE_REGISTER 0x0b00 +#define WCD934X_CDC_COMPANDER1_CTL0 0x0b01 +#define WCD934X_CDC_COMPANDER1_CTL1 0x0b02 +#define WCD934X_CDC_COMPANDER1_CTL2 0x0b03 +#define WCD934X_CDC_COMPANDER1_CTL3 0x0b04 +#define WCD934X_CDC_COMPANDER1_CTL4 0x0b05 +#define WCD934X_CDC_COMPANDER1_CTL5 0x0b06 +#define WCD934X_CDC_COMPANDER1_CTL6 0x0b07 +#define WCD934X_CDC_COMPANDER1_CTL7 0x0b08 +#define WCD934X_CDC_COMPANDER2_CTL0 0x0b09 +#define WCD934X_CDC_COMPANDER2_CTL1 0x0b0a +#define WCD934X_CDC_COMPANDER2_CTL2 0x0b0b +#define WCD934X_CDC_COMPANDER2_CTL3 0x0b0c +#define WCD934X_CDC_COMPANDER2_CTL4 0x0b0d +#define WCD934X_CDC_COMPANDER2_CTL5 0x0b0e +#define WCD934X_CDC_COMPANDER2_CTL6 0x0b0f +#define WCD934X_CDC_COMPANDER2_CTL7 0x0b10 +#define WCD934X_CDC_COMPANDER3_CTL0 0x0b11 +#define WCD934X_CDC_COMPANDER3_CTL1 0x0b12 +#define WCD934X_CDC_COMPANDER3_CTL2 0x0b13 +#define WCD934X_CDC_COMPANDER3_CTL3 0x0b14 +#define WCD934X_CDC_COMPANDER3_CTL4 0x0b15 +#define WCD934X_CDC_COMPANDER3_CTL5 0x0b16 +#define WCD934X_CDC_COMPANDER3_CTL6 0x0b17 +#define WCD934X_CDC_COMPANDER3_CTL7 0x0b18 +#define WCD934X_CDC_COMPANDER4_CTL0 0x0b19 +#define WCD934X_CDC_COMPANDER4_CTL1 0x0b1a +#define WCD934X_CDC_COMPANDER4_CTL2 0x0b1b +#define WCD934X_CDC_COMPANDER4_CTL3 0x0b1c +#define WCD934X_CDC_COMPANDER4_CTL4 0x0b1d +#define WCD934X_CDC_COMPANDER4_CTL5 0x0b1e +#define WCD934X_CDC_COMPANDER4_CTL6 0x0b1f +#define WCD934X_CDC_COMPANDER4_CTL7 0x0b20 +#define WCD934X_CDC_COMPANDER7_CTL0 0x0b31 +#define WCD934X_CDC_COMPANDER7_CTL1 0x0b32 +#define WCD934X_CDC_COMPANDER7_CTL2 0x0b33 +#define WCD934X_CDC_COMPANDER7_CTL3 0x0b34 +#define WCD934X_CDC_COMPANDER7_CTL4 0x0b35 +#define WCD934X_CDC_COMPANDER7_CTL5 0x0b36 +#define WCD934X_CDC_COMPANDER7_CTL6 0x0b37 +#define WCD934X_CDC_COMPANDER7_CTL7 0x0b38 +#define WCD934X_CDC_COMPANDER8_CTL0 0x0b39 +#define WCD934X_CDC_COMPANDER8_CTL1 0x0b3a +#define WCD934X_CDC_COMPANDER8_CTL2 0x0b3b +#define WCD934X_CDC_COMPANDER8_CTL3 0x0b3c +#define WCD934X_CDC_COMPANDER8_CTL4 0x0b3d +#define WCD934X_CDC_COMPANDER8_CTL5 0x0b3e +#define WCD934X_CDC_COMPANDER8_CTL6 0x0b3f +#define WCD934X_CDC_COMPANDER8_CTL7 0x0b40 +#define WCD934X_CDC_RX0_RX_PATH_CTL 0x0b41 +#define WCD934X_CDC_RX0_RX_PATH_CFG0 0x0b42 +#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_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_SEC1 0x0b4a +#define WCD934X_CDC_RX0_RX_PATH_SEC2 0x0b4b +#define WCD934X_CDC_RX0_RX_PATH_SEC3 0x0b4c +#define WCD934X_CDC_RX0_RX_PATH_SEC5 0x0b4e +#define WCD934X_CDC_RX0_RX_PATH_SEC6 0x0b4f +#define WCD934X_CDC_RX0_RX_PATH_SEC7 0x0b50 +#define WCD934X_CDC_RX0_RX_PATH_MIX_SEC0 0x0b51 +#define WCD934X_CDC_RX0_RX_PATH_MIX_SEC1 0x0b52 +#define WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL 0x0b53 +#define WCD934X_CDC_RX1_RX_PATH_CTL 0x0b55 +#define WCD934X_CDC_RX1_RX_PATH_CFG0 0x0b56 +#define WCD934X_CDC_RX1_RX_PATH_CFG1 0x0b57 +#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_SEC1 0x0b5e +#define WCD934X_CDC_RX1_RX_PATH_SEC2 0x0b5f +#define WCD934X_CDC_RX1_RX_PATH_SEC3 0x0b60 +#define WCD934X_CDC_RX1_RX_PATH_SEC4 0x0b61 +#define WCD934X_CDC_RX1_RX_PATH_SEC5 0x0b62 +#define WCD934X_CDC_RX1_RX_PATH_SEC6 0x0b63 +#define WCD934X_CDC_RX1_RX_PATH_SEC7 0x0b64 +#define WCD934X_CDC_RX1_RX_PATH_MIX_SEC0 0x0b65 +#define WCD934X_CDC_RX1_RX_PATH_MIX_SEC1 0x0b66 +#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_RX2_RX_PATH_CFG1 0x0b6b +#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_SEC1 0x0b72 +#define WCD934X_CDC_RX2_RX_PATH_SEC2 0x0b73 +#define WCD934X_CDC_RX2_RX_PATH_SEC3 0x0b74 +#define WCD934X_CDC_RX2_RX_PATH_SEC4 0x0b75 +#define WCD934X_CDC_RX2_RX_PATH_SEC5 0x0b76 +#define WCD934X_CDC_RX2_RX_PATH_SEC6 0x0b77 +#define WCD934X_CDC_RX2_RX_PATH_SEC7 0x0b78 +#define WCD934X_CDC_RX2_RX_PATH_MIX_SEC0 0x0b79 +#define WCD934X_CDC_RX2_RX_PATH_MIX_SEC1 0x0b7a +#define WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL 0x0b7b +#define WCD934X_CDC_RX3_RX_PATH_CTL 0x0b7d +#define WCD934X_CDC_RX3_RX_PATH_CFG0 0x0b7e +#define WCD934X_CDC_RX3_RX_PATH_CFG1 0x0b7f +#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_SEC1 0x0b86 +#define WCD934X_CDC_RX3_RX_PATH_SEC2 0x0b87 +#define WCD934X_CDC_RX3_RX_PATH_SEC3 0x0b88 +#define WCD934X_CDC_RX3_RX_PATH_SEC5 0x0b8a +#define WCD934X_CDC_RX3_RX_PATH_SEC6 0x0b8b +#define WCD934X_CDC_RX3_RX_PATH_SEC7 0x0b8c +#define WCD934X_CDC_RX3_RX_PATH_MIX_SEC0 0x0b8d +#define WCD934X_CDC_RX3_RX_PATH_MIX_SEC1 0x0b8e +#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_CFG1 0x0b93 +#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_SEC1 0x0b9a +#define WCD934X_CDC_RX4_RX_PATH_SEC2 0x0b9b +#define WCD934X_CDC_RX4_RX_PATH_SEC3 0x0b9c +#define WCD934X_CDC_RX4_RX_PATH_SEC5 0x0b9e +#define WCD934X_CDC_RX4_RX_PATH_SEC6 0x0b9f +#define WCD934X_CDC_RX4_RX_PATH_SEC7 0x0ba0 +#define WCD934X_CDC_RX4_RX_PATH_MIX_SEC0 0x0ba1 +#define WCD934X_CDC_RX4_RX_PATH_MIX_SEC1 0x0ba2 +#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_SEC0 0x0bd5 +#define WCD934X_CDC_RX7_RX_PATH_SEC1 0x0bd6 +#define WCD934X_CDC_RX7_RX_PATH_SEC2 0x0bd7 +#define WCD934X_CDC_RX7_RX_PATH_SEC3 0x0bd8 +#define WCD934X_CDC_RX7_RX_PATH_SEC5 0x0bda +#define WCD934X_CDC_RX7_RX_PATH_SEC6 0x0bdb +#define WCD934X_CDC_RX7_RX_PATH_SEC7 0x0bdc +#define WCD934X_CDC_RX7_RX_PATH_MIX_SEC0 0x0bdd +#define WCD934X_CDC_RX7_RX_PATH_MIX_SEC1 0x0bde +#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_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_SEC0 0x0be9 +#define WCD934X_CDC_RX8_RX_PATH_SEC1 0x0bea +#define WCD934X_CDC_RX8_RX_PATH_SEC2 0x0beb +#define WCD934X_CDC_RX8_RX_PATH_SEC3 0x0bec +#define WCD934X_CDC_RX8_RX_PATH_SEC5 0x0bee +#define WCD934X_CDC_RX8_RX_PATH_SEC6 0x0bef +#define WCD934X_CDC_RX8_RX_PATH_SEC7 0x0bf0 +#define WCD934X_CDC_RX8_RX_PATH_MIX_SEC0 0x0bf1 +#define WCD934X_CDC_RX8_RX_PATH_MIX_SEC1 0x0bf2 +#define WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL 0x0bf3 +#define WCD934X_PAGE12_PAGE_REGISTER 0x0c00 +#define WCD934X_CDC_CLSH_CRC 0x0c01 +#define WCD934X_CDC_CLSH_DLY_CTRL 0x0c02 +#define WCD934X_CDC_CLSH_DECAY_CTRL 0x0c03 +#define WCD934X_CDC_CLSH_HPH_V_PA 0x0c04 +#define WCD934X_CDC_CLSH_EAR_V_PA 0x0c05 +#define WCD934X_CDC_CLSH_HPH_V_HD 0x0c06 +#define WCD934X_CDC_CLSH_EAR_V_HD 0x0c07 +#define WCD934X_CDC_CLSH_K1_MSB 0x0c08 +#define WCD934X_CDC_CLSH_K1_LSB 0x0c09 +#define WCD934X_CDC_CLSH_K2_MSB 0x0c0a +#define WCD934X_CDC_CLSH_K2_LSB 0x0c0b +#define WCD934X_CDC_CLSH_IDLE_CTRL 0x0c0c +#define WCD934X_CDC_CLSH_IDLE_HPH 0x0c0d +#define WCD934X_CDC_CLSH_IDLE_EAR 0x0c0e +#define WCD934X_CDC_CLSH_TEST0 0x0c0f +#define WCD934X_CDC_CLSH_TEST1 0x0c10 +#define WCD934X_CDC_CLSH_OVR_VREF 0x0c11 +#define WCD934X_CDC_BOOST0_BOOST_PATH_CTL 0x0c19 +#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_CDC_VBAT_VBAT_PATH_CTL 0x0c3d +#define WCD934X_CDC_VBAT_VBAT_CFG 0x0c3e +#define WCD934X_CDC_VBAT_VBAT_ADC_CAL1 0x0c3f +#define WCD934X_CDC_VBAT_VBAT_ADC_CAL2 0x0c40 +#define WCD934X_CDC_VBAT_VBAT_ADC_CAL3 0x0c41 +#define WCD934X_CDC_VBAT_VBAT_PK_EST1 0x0c42 +#define WCD934X_CDC_VBAT_VBAT_PK_EST2 0x0c43 +#define WCD934X_CDC_VBAT_VBAT_PK_EST3 0x0c44 +#define WCD934X_CDC_VBAT_VBAT_RF_PROC1 0x0c45 +#define WCD934X_CDC_VBAT_VBAT_RF_PROC2 0x0c46 +#define WCD934X_CDC_VBAT_VBAT_TAC1 0x0c47 +#define WCD934X_CDC_VBAT_VBAT_TAC2 0x0c48 +#define WCD934X_CDC_VBAT_VBAT_TAC3 0x0c49 +#define WCD934X_CDC_VBAT_VBAT_TAC4 0x0c4a +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD1 0x0c4b +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD2 0x0c4c +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD3 0x0c4d +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD4 0x0c4e +#define WCD934X_CDC_VBAT_VBAT_DEBUG1 0x0c4f +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD_MON 0x0c50 +#define WCD934X_CDC_VBAT_VBAT_GAIN_MON_VAL 0x0c51 +#define WCD934X_CDC_VBAT_VBAT_BAN 0x0c52 +#define WCD934X_MIXING_ASRC0_CLK_RST_CTL 0x0c55 +#define WCD934X_MIXING_ASRC0_CTL0 0x0c56 +#define WCD934X_MIXING_ASRC0_CTL1 0x0c57 +#define WCD934X_MIXING_ASRC0_FIFO_CTL 0x0c58 +#define WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB 0x0c59 +#define WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB 0x0c5a +#define WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB 0x0c5b +#define WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB 0x0c5c +#define WCD934X_MIXING_ASRC0_STATUS_FIFO 0x0c5d +#define WCD934X_MIXING_ASRC1_CLK_RST_CTL 0x0c61 +#define WCD934X_MIXING_ASRC1_CTL0 0x0c62 +#define WCD934X_MIXING_ASRC1_CTL1 0x0c63 +#define WCD934X_MIXING_ASRC1_FIFO_CTL 0x0c64 +#define WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB 0x0c65 +#define WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB 0x0c66 +#define WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB 0x0c67 +#define WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB 0x0c68 +#define WCD934X_MIXING_ASRC1_STATUS_FIFO 0x0c69 +#define WCD934X_MIXING_ASRC2_CLK_RST_CTL 0x0c6d +#define WCD934X_MIXING_ASRC2_CTL0 0x0c6e +#define WCD934X_MIXING_ASRC2_CTL1 0x0c6f +#define WCD934X_MIXING_ASRC2_FIFO_CTL 0x0c70 +#define WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB 0x0c71 +#define WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB 0x0c72 +#define WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB 0x0c73 +#define WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB 0x0c74 +#define WCD934X_MIXING_ASRC2_STATUS_FIFO 0x0c75 +#define WCD934X_MIXING_ASRC3_CLK_RST_CTL 0x0c79 +#define WCD934X_MIXING_ASRC3_CTL0 0x0c7a +#define WCD934X_MIXING_ASRC3_CTL1 0x0c7b +#define WCD934X_MIXING_ASRC3_FIFO_CTL 0x0c7c +#define WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB 0x0c7d +#define WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB 0x0c7e +#define WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB 0x0c7f +#define WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB 0x0c80 +#define WCD934X_MIXING_ASRC3_STATUS_FIFO 0x0c81 +#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_0 0x0c85 +#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_1 0x0c86 +#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_2 0x0c87 +#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_3 0x0c88 +#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0 0x0c89 +#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_1 0x0c8a +#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_2 0x0c8b +#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_3 0x0c8c +#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0 0x0c8d +#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_1 0x0c8e +#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_2 0x0c8f +#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_3 0x0c90 +#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_CFG 0x0c95 +#define WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS 0x0c96 +#define WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL 0x0cb5 +#define WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1 0x0cb6 +#define WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL 0x0cb9 +#define WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1 0x0cba +#define WCD934X_SIDETONE_ASRC0_CLK_RST_CTL 0x0cbd +#define WCD934X_SIDETONE_ASRC0_CTL0 0x0cbe +#define WCD934X_SIDETONE_ASRC0_CTL1 0x0cbf +#define WCD934X_SIDETONE_ASRC0_FIFO_CTL 0x0cc0 +#define WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB 0x0cc1 +#define WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB 0x0cc2 +#define WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB 0x0cc3 +#define WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB 0x0cc4 +#define WCD934X_SIDETONE_ASRC0_STATUS_FIFO 0x0cc5 +#define WCD934X_SIDETONE_ASRC1_CLK_RST_CTL 0x0cc9 +#define WCD934X_SIDETONE_ASRC1_CTL0 0x0cca +#define WCD934X_SIDETONE_ASRC1_CTL1 0x0ccb +#define WCD934X_SIDETONE_ASRC1_FIFO_CTL 0x0ccc +#define WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_LSB 0x0ccd +#define WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_MSB 0x0cce +#define WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_LSB 0x0ccf +#define WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_MSB 0x0cd0 +#define WCD934X_SIDETONE_ASRC1_STATUS_FIFO 0x0cd1 +#define WCD934X_EC_REF_HQ0_EC_REF_HQ_PATH_CTL 0x0cd5 +#define WCD934X_EC_REF_HQ0_EC_REF_HQ_CFG0 0x0cd6 +#define WCD934X_EC_REF_HQ1_EC_REF_HQ_PATH_CTL 0x0cdd +#define WCD934X_EC_REF_HQ1_EC_REF_HQ_CFG0 0x0cde +#define WCD934X_EC_ASRC0_CLK_RST_CTL 0x0ce5 +#define WCD934X_EC_ASRC0_CTL0 0x0ce6 +#define WCD934X_EC_ASRC0_CTL1 0x0ce7 +#define WCD934X_EC_ASRC0_FIFO_CTL 0x0ce8 +#define WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_LSB 0x0ce9 +#define WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_MSB 0x0cea +#define WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_LSB 0x0ceb +#define WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_MSB 0x0cec +#define WCD934X_EC_ASRC0_STATUS_FIFO 0x0ced +#define WCD934X_EC_ASRC1_CLK_RST_CTL 0x0cf1 +#define WCD934X_EC_ASRC1_CTL0 0x0cf2 +#define WCD934X_EC_ASRC1_CTL1 0x0cf3 +#define WCD934X_EC_ASRC1_FIFO_CTL 0x0cf4 +#define WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_LSB 0x0cf5 +#define WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_MSB 0x0cf6 +#define WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_LSB 0x0cf7 +#define WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_MSB 0x0cf8 +#define WCD934X_EC_ASRC1_STATUS_FIFO 0x0cf9 +#define WCD934X_PAGE13_PAGE_REGISTER 0x0d00 +#define WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0 0x0d01 +#define WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1 0x0d02 +#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_RX_INP_MUX_ANC_CFG0 0x0d1a +#define WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0 0x0d1b +#define WCD934X_CDC_RX_INP_MUX_EC_REF_HQ_CFG0 0x0d1c +#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_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42 +#define WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL 0x0d43 +#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_PROX_DETECT_PROX_CTL 0x0d49 +#define WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD0 0x0d4a +#define WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD1 0x0d4b +#define WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB 0x0d4c +#define WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB 0x0d4d +#define WCD934X_CDC_PROX_DETECT_PROX_STATUS 0x0d4e +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_CTRL 0x0d4f +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB 0x0d50 +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB 0x0d51 +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD 0x0d52 +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD 0x0d53 +#define WCD934X_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT 0x0d54 +#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_CFG0 0x0d81 +#define WCD934X_CDC_TOP_TOP_CFG1 0x0d82 +#define WCD934X_CDC_TOP_TOP_CFG7 0x0d88 +#define WCD934X_CDC_TOP_HPHL_COMP_WR_LSB 0x0d89 +#define WCD934X_CDC_TOP_HPHL_COMP_WR_MSB 0x0d8a +#define WCD934X_CDC_TOP_HPHL_COMP_LUT 0x0d8b +#define WCD934X_CDC_TOP_HPHL_COMP_RD_LSB 0x0d8c +#define WCD934X_CDC_TOP_HPHL_COMP_RD_MSB 0x0d8d +#define WCD934X_CDC_TOP_HPHR_COMP_WR_LSB 0x0d8e +#define WCD934X_CDC_TOP_HPHR_COMP_WR_MSB 0x0d8f +#define WCD934X_CDC_TOP_HPHR_COMP_LUT 0x0d90 +#define WCD934X_CDC_TOP_HPHR_COMP_RD_LSB 0x0d91 +#define WCD934X_CDC_TOP_HPHR_COMP_RD_MSB 0x0d92 +#define WCD934X_CDC_TOP_DIFFL_COMP_WR_LSB 0x0d93 +#define WCD934X_CDC_TOP_DIFFL_COMP_WR_MSB 0x0d94 +#define WCD934X_CDC_TOP_DIFFL_COMP_LUT 0x0d95 +#define WCD934X_CDC_TOP_DIFFL_COMP_RD_LSB 0x0d96 +#define WCD934X_CDC_TOP_DIFFL_COMP_RD_MSB 0x0d97 +#define WCD934X_CDC_TOP_DIFFR_COMP_WR_LSB 0x0d98 +#define WCD934X_CDC_TOP_DIFFR_COMP_WR_MSB 0x0d99 +#define WCD934X_CDC_TOP_DIFFR_COMP_LUT 0x0d9a +#define WCD934X_CDC_TOP_DIFFR_COMP_RD_LSB 0x0d9b +#define WCD934X_CDC_TOP_DIFFR_COMP_RD_MSB 0x0d9c +#define WCD934X_CDC_DSD0_PATH_CTL 0x0db1 +#define WCD934X_CDC_DSD0_CFG0 0x0db2 +#define WCD934X_CDC_DSD0_CFG1 0x0db3 +#define WCD934X_CDC_DSD0_CFG2 0x0db4 +#define WCD934X_CDC_DSD0_CFG3 0x0db5 +#define WCD934X_CDC_DSD0_CFG4 0x0db6 +#define WCD934X_CDC_DSD0_CFG5 0x0db7 +#define WCD934X_CDC_DSD1_PATH_CTL 0x0dc1 +#define WCD934X_CDC_DSD1_CFG0 0x0dc2 +#define WCD934X_CDC_DSD1_CFG1 0x0dc3 +#define WCD934X_CDC_DSD1_CFG2 0x0dc4 +#define WCD934X_CDC_DSD1_CFG3 0x0dc5 +#define WCD934X_CDC_DSD1_CFG4 0x0dc6 +#define WCD934X_CDC_DSD1_CFG5 0x0dc7 +#define WCD934X_CDC_RX_IDLE_DET_PATH_CTL 0x0dd1 +#define WCD934X_CDC_RX_IDLE_DET_CFG0 0x0dd2 +#define WCD934X_CDC_RX_IDLE_DET_CFG1 0x0dd3 +#define WCD934X_CDC_RX_IDLE_DET_CFG2 0x0dd4 +#define WCD934X_CDC_RX_IDLE_DET_CFG3 0x0dd5 +#define WCD934X_PAGE14_PAGE_REGISTER 0x0e00 +#define WCD934X_CDC_RATE_EST0_RE_CLK_RST_CTL 0x0e01 +#define WCD934X_CDC_RATE_EST0_RE_CTL 0x0e02 +#define WCD934X_CDC_RATE_EST0_RE_PULSE_SUPR_CTL 0x0e03 +#define WCD934X_CDC_RATE_EST0_RE_TIMER 0x0e04 +#define WCD934X_CDC_RATE_EST0_RE_BW_SW 0x0e05 +#define WCD934X_CDC_RATE_EST0_RE_THRESH 0x0e06 +#define WCD934X_CDC_RATE_EST0_RE_STATUS 0x0e07 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_CTRL 0x0e09 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_TIMER2 0x0e0c +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW1 0x0e0d +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW2 0x0e0e +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW3 0x0e0f +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW4 0x0e10 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW5 0x0e11 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW1 0x0e12 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW2 0x0e13 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW3 0x0e14 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW4 0x0e15 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW5 0x0e16 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW1 0x0e17 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW2 0x0e18 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW3 0x0e19 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW4 0x0e1a +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW5 0x0e1b +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW1 0x0e1c +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW2 0x0e1d +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW3 0x0e1e +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW4 0x0e1f +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW5 0x0e20 +#define WCD934X_CDC_RATE_EST0_RE_RMAX_DIAG 0x0e21 +#define WCD934X_CDC_RATE_EST0_RE_RMIN_DIAG 0x0e22 +#define WCD934X_CDC_RATE_EST0_RE_PH_DET 0x0e23 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_CLR 0x0e24 +#define WCD934X_CDC_RATE_EST0_RE_MB_SW_STATE 0x0e25 +#define WCD934X_CDC_RATE_EST0_RE_MAST_DIAG_STATE 0x0e26 +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_7_0 0x0e27 +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_15_8 0x0e28 +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_23_16 0x0e29 +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_31_24 0x0e2a +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_39_32 0x0e2b +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_40_43 0x0e2c +#define WCD934X_CDC_RATE_EST1_RE_CLK_RST_CTL 0x0e31 +#define WCD934X_CDC_RATE_EST1_RE_CTL 0x0e32 +#define WCD934X_CDC_RATE_EST1_RE_PULSE_SUPR_CTL 0x0e33 +#define WCD934X_CDC_RATE_EST1_RE_TIMER 0x0e34 +#define WCD934X_CDC_RATE_EST1_RE_BW_SW 0x0e35 +#define WCD934X_CDC_RATE_EST1_RE_THRESH 0x0e36 +#define WCD934X_CDC_RATE_EST1_RE_STATUS 0x0e37 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_CTRL 0x0e39 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_TIMER2 0x0e3c +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW1 0x0e3d +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW2 0x0e3e +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW3 0x0e3f +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW4 0x0e40 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW5 0x0e41 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW1 0x0e42 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW2 0x0e43 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW3 0x0e44 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW4 0x0e45 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW5 0x0e46 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW1 0x0e47 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW2 0x0e48 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW3 0x0e49 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW4 0x0e4a +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW5 0x0e4b +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW1 0x0e4c +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW2 0x0e4d +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW3 0x0e4e +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW4 0x0e4f +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW5 0x0e50 +#define WCD934X_CDC_RATE_EST1_RE_RMAX_DIAG 0x0e51 +#define WCD934X_CDC_RATE_EST1_RE_RMIN_DIAG 0x0e52 +#define WCD934X_CDC_RATE_EST1_RE_PH_DET 0x0e53 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_CLR 0x0e54 +#define WCD934X_CDC_RATE_EST1_RE_MB_SW_STATE 0x0e55 +#define WCD934X_CDC_RATE_EST1_RE_MAST_DIAG_STATE 0x0e56 +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_7_0 0x0e57 +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_15_8 0x0e58 +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_23_16 0x0e59 +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_31_24 0x0e5a +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_39_32 0x0e5b +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_40_43 0x0e5c +#define WCD934X_CDC_RATE_EST2_RE_CLK_RST_CTL 0x0e61 +#define WCD934X_CDC_RATE_EST2_RE_CTL 0x0e62 +#define WCD934X_CDC_RATE_EST2_RE_PULSE_SUPR_CTL 0x0e63 +#define WCD934X_CDC_RATE_EST2_RE_TIMER 0x0e64 +#define WCD934X_CDC_RATE_EST2_RE_BW_SW 0x0e65 +#define WCD934X_CDC_RATE_EST2_RE_THRESH 0x0e66 +#define WCD934X_CDC_RATE_EST2_RE_STATUS 0x0e67 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_CTRL 0x0e69 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_TIMER2 0x0e6c +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW1 0x0e6d +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW2 0x0e6e +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW3 0x0e6f +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW4 0x0e70 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW5 0x0e71 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW1 0x0e72 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW2 0x0e73 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW3 0x0e74 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW4 0x0e75 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW5 0x0e76 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW1 0x0e77 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW2 0x0e78 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW3 0x0e79 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW4 0x0e7a +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW5 0x0e7b +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW1 0x0e7c +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW2 0x0e7d +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW3 0x0e7e +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW4 0x0e7f +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW5 0x0e80 +#define WCD934X_CDC_RATE_EST2_RE_RMAX_DIAG 0x0e81 +#define WCD934X_CDC_RATE_EST2_RE_RMIN_DIAG 0x0e82 +#define WCD934X_CDC_RATE_EST2_RE_PH_DET 0x0e83 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_CLR 0x0e84 +#define WCD934X_CDC_RATE_EST2_RE_MB_SW_STATE 0x0e85 +#define WCD934X_CDC_RATE_EST2_RE_MAST_DIAG_STATE 0x0e86 +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_7_0 0x0e87 +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_15_8 0x0e88 +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_23_16 0x0e89 +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_31_24 0x0e8a +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_39_32 0x0e8b +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_40_43 0x0e8c +#define WCD934X_CDC_RATE_EST3_RE_CLK_RST_CTL 0x0e91 +#define WCD934X_CDC_RATE_EST3_RE_CTL 0x0e92 +#define WCD934X_CDC_RATE_EST3_RE_PULSE_SUPR_CTL 0x0e93 +#define WCD934X_CDC_RATE_EST3_RE_TIMER 0x0e94 +#define WCD934X_CDC_RATE_EST3_RE_BW_SW 0x0e95 +#define WCD934X_CDC_RATE_EST3_RE_THRESH 0x0e96 +#define WCD934X_CDC_RATE_EST3_RE_STATUS 0x0e97 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_CTRL 0x0e99 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_TIMER2 0x0e9c +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW1 0x0e9d +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW2 0x0e9e +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW3 0x0e9f +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW4 0x0ea0 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW5 0x0ea1 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW1 0x0ea2 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW2 0x0ea3 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW3 0x0ea4 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW4 0x0ea5 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW5 0x0ea6 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW1 0x0ea7 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW2 0x0ea8 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW3 0x0ea9 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW4 0x0eaa +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW5 0x0eab +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW1 0x0eac +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW2 0x0ead +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW3 0x0eae +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW4 0x0eaf +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW5 0x0eb0 +#define WCD934X_CDC_RATE_EST3_RE_RMAX_DIAG 0x0eb1 +#define WCD934X_CDC_RATE_EST3_RE_RMIN_DIAG 0x0eb2 +#define WCD934X_CDC_RATE_EST3_RE_PH_DET 0x0eb3 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_CLR 0x0eb4 +#define WCD934X_CDC_RATE_EST3_RE_MB_SW_STATE 0x0eb5 +#define WCD934X_CDC_RATE_EST3_RE_MAST_DIAG_STATE 0x0eb6 +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_7_0 0x0eb7 +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_15_8 0x0eb8 +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_23_16 0x0eb9 +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_31_24 0x0eba +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_39_32 0x0ebb +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_40_43 0x0ebc +#define WCD934X_PAGE15_PAGE_REGISTER 0x0f00 +#define WCD934X_SPLINE_SRC0_CLK_RST_CTL_0 0x0f01 +#define WCD934X_SPLINE_SRC0_STATUS 0x0f02 +#define WCD934X_SPLINE_SRC1_CLK_RST_CTL_0 0x0f19 +#define WCD934X_SPLINE_SRC1_STATUS 0x0f1a +#define WCD934X_SPLINE_SRC2_CLK_RST_CTL_0 0x0f31 +#define WCD934X_SPLINE_SRC2_STATUS 0x0f32 +#define WCD934X_SPLINE_SRC3_CLK_RST_CTL_0 0x0f49 +#define WCD934X_SPLINE_SRC3_STATUS 0x0f4a +#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0 0x0fa1 +#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1 0x0fa2 +#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG2 0x0fa3 +#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3 0x0fa4 +#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0 0x0fa5 +#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1 0x0fa6 +#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG2 0x0fa7 +#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3 0x0fa8 +#define WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG0 0x0fa9 +#define WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG1 0x0faa +#define WCD934X_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0 0x0fab +#define WCD934X_CDC_DEBUG_ANC0_RC0_FIFO_CTL 0x0fac +#define WCD934X_CDC_DEBUG_ANC0_RC1_FIFO_CTL 0x0fad +#define WCD934X_CDC_DEBUG_ANC1_RC0_FIFO_CTL 0x0fae +#define WCD934X_CDC_DEBUG_ANC1_RC1_FIFO_CTL 0x0faf +#define WCD934X_CDC_DEBUG_ANC_RC_RST_DBG_CNTR 0x0fb0 +#define WCD934X_PAGE80_PAGE_REGISTER 0x5000 +#define WCD934X_CODEC_CPR_WR_DATA_0 0x5001 +#define WCD934X_CODEC_CPR_WR_DATA_1 0x5002 +#define WCD934X_CODEC_CPR_WR_DATA_2 0x5003 +#define WCD934X_CODEC_CPR_WR_DATA_3 0x5004 +#define WCD934X_CODEC_CPR_WR_ADDR_0 0x5005 +#define WCD934X_CODEC_CPR_WR_ADDR_1 0x5006 +#define WCD934X_CODEC_CPR_WR_ADDR_2 0x5007 +#define WCD934X_CODEC_CPR_WR_ADDR_3 0x5008 +#define WCD934X_CODEC_CPR_RD_ADDR_0 0x5009 +#define WCD934X_CODEC_CPR_RD_ADDR_1 0x500a +#define WCD934X_CODEC_CPR_RD_ADDR_2 0x500b +#define WCD934X_CODEC_CPR_RD_ADDR_3 0x500c +#define WCD934X_CODEC_CPR_RD_DATA_0 0x500d +#define WCD934X_CODEC_CPR_RD_DATA_1 0x500e +#define WCD934X_CODEC_CPR_RD_DATA_2 0x500f +#define WCD934X_CODEC_CPR_RD_DATA_3 0x5010 +#define WCD934X_CODEC_CPR_ACCESS_CFG 0x5011 +#define WCD934X_CODEC_CPR_ACCESS_STATUS 0x5012 +#define WCD934X_CODEC_CPR_NOM_CX_VDD 0x5021 +#define WCD934X_CODEC_CPR_SVS_CX_VDD 0x5022 +#define WCD934X_CODEC_CPR_SVS2_CX_VDD 0x5023 +#define WCD934X_CODEC_CPR_NOM_MX_VDD 0x5024 +#define WCD934X_CODEC_CPR_SVS_MX_VDD 0x5025 +#define WCD934X_CODEC_CPR_SVS2_MX_VDD 0x5026 +#define WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD 0x5027 +#define WCD934X_CODEC_CPR_MAX_SVS2_STEP 0x5028 +#define WCD934X_CODEC_CPR_CTL 0x5029 +#define WCD934X_CODEC_CPR_SW_MODECHNG_STATUS 0x502a +#define WCD934X_CODEC_CPR_SW_MODECHNG_START 0x502b +#define WCD934X_CODEC_CPR_CPR_STATUS 0x502c +#define WCD934X_PAGE128_PAGE_REGISTER 0x8000 +#define WCD934X_TLMM_BIST_MODE_PINCFG 0x8001 +#define WCD934X_TLMM_RF_PA_ON_PINCFG 0x8002 +#define WCD934X_TLMM_INTR1_PINCFG 0x8003 +#define WCD934X_TLMM_INTR2_PINCFG 0x8004 +#define WCD934X_TLMM_SWR_DATA_PINCFG 0x8005 +#define WCD934X_TLMM_SWR_CLK_PINCFG 0x8006 +#define WCD934X_TLMM_I2S_2_SCK_PINCFG 0x8007 +#define WCD934X_TLMM_SLIMBUS_DATA1_PINCFG 0x8008 +#define WCD934X_TLMM_SLIMBUS_DATA2_PINCFG 0x8009 +#define WCD934X_TLMM_SLIMBUS_CLK_PINCFG 0x800a +#define WCD934X_TLMM_I2C_CLK_PINCFG 0x800b +#define WCD934X_TLMM_I2C_DATA_PINCFG 0x800c +#define WCD934X_TLMM_I2S_0_RX_PINCFG 0x800d +#define WCD934X_TLMM_I2S_0_TX_PINCFG 0x800e +#define WCD934X_TLMM_I2S_0_SCK_PINCFG 0x800f +#define WCD934X_TLMM_I2S_0_WS_PINCFG 0x8010 +#define WCD934X_TLMM_I2S_1_RX_PINCFG 0x8011 +#define WCD934X_TLMM_I2S_1_TX_PINCFG 0x8012 +#define WCD934X_TLMM_I2S_1_SCK_PINCFG 0x8013 +#define WCD934X_TLMM_I2S_1_WS_PINCFG 0x8014 +#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_TLMM_JTCK_PINCFG 0x801b +#define WCD934X_TLMM_GPIO1_PINCFG 0x801c +#define WCD934X_TLMM_GPIO2_PINCFG 0x801d +#define WCD934X_TLMM_GPIO3_PINCFG 0x801e +#define WCD934X_TLMM_GPIO4_PINCFG 0x801f +#define WCD934X_TLMM_SPI_S_CSN_PINCFG 0x8020 +#define WCD934X_TLMM_SPI_S_CLK_PINCFG 0x8021 +#define WCD934X_TLMM_SPI_S_DOUT_PINCFG 0x8022 +#define WCD934X_TLMM_SPI_S_DIN_PINCFG 0x8023 +#define WCD934X_TLMM_BA_N_PINCFG 0x8024 +#define WCD934X_TLMM_GPIO0_PINCFG 0x8025 +#define WCD934X_TLMM_I2S_2_RX_PINCFG 0x8026 +#define WCD934X_TLMM_I2S_2_WS_PINCFG 0x8027 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_0 0x8031 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_1 0x8032 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_2 0x8033 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_3 0x8034 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_4 0x8035 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_0 0x8036 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_1 0x8037 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_2 0x8038 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_3 0x8039 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_4 0x803a +#define WCD934X_TEST_DEBUG_PAD_DRVCTL_0 0x803b +#define WCD934X_TEST_DEBUG_PAD_DRVCTL_1 0x803c +#define WCD934X_TEST_DEBUG_PIN_STATUS 0x803d +#define WCD934X_TEST_DEBUG_NPL_DLY_TEST_1 0x803e +#define WCD934X_TEST_DEBUG_NPL_DLY_TEST_2 0x803f +#define WCD934X_TEST_DEBUG_MEM_CTRL 0x8040 +#define WCD934X_TEST_DEBUG_DEBUG_BUS_SEL 0x8041 +#define WCD934X_TEST_DEBUG_DEBUG_JTAG 0x8042 +#define WCD934X_TEST_DEBUG_DEBUG_EN_1 0x8043 +#define WCD934X_TEST_DEBUG_DEBUG_EN_2 0x8044 +#define WCD934X_TEST_DEBUG_DEBUG_EN_3 0x8045 +#define WCD934X_TEST_DEBUG_DEBUG_EN_4 0x8046 +#define WCD934X_TEST_DEBUG_DEBUG_EN_5 0x8047 +#define WCD934X_TEST_DEBUG_ANA_DTEST_DIR 0x804a +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_0 0x804b +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_1 0x804c +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_2 0x804d +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_3 0x804e +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_4 0x804f +#define WCD934X_TEST_DEBUG_SYSMEM_CTRL 0x8050 +#define WCD934X_TEST_DEBUG_SOC_SW_PWR_SEQ_DELAY 0x8051 +#define WCD934X_TEST_DEBUG_LVAL_NOM_LOW 0x8052 +#define WCD934X_TEST_DEBUG_LVAL_NOM_HIGH 0x8053 +#define WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW 0x8054 +#define WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH 0x8055 +#define WCD934X_TEST_DEBUG_SPI_SLAVE_CHAR 0x8056 +#define WCD934X_TEST_DEBUG_CODEC_DIAGS 0x8057 +#define WCD934X_MAX_REGISTER 0x80FF + +/* SLIMBUS Slave Registers */ +#define WCD934X_SLIM_PGD_PORT_INT_RX_EN0 (0x30) +#define WCD934X_SLIM_PGD_PORT_INT_TX_EN0 (0x32) +#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) + +#endif diff --git a/include/linux/mfd/wcd9xxx/Kbuild b/include/linux/mfd/wcd9xxx/Kbuild new file mode 100644 index 0000000000000000000000000000000000000000..8e55965bbe7ede83a4f163e70e1cfda693a8a4dd --- /dev/null +++ b/include/linux/mfd/wcd9xxx/Kbuild @@ -0,0 +1,2 @@ +header-y += wcd9xxx_registers.h +header-y += wcd9320_registers.h diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h new file mode 100644 index 0000000000000000000000000000000000000000..8a507d26c883a47623aefaaf821731f3468a3363 --- /dev/null +++ b/include/linux/mfd/wcd9xxx/core.h @@ -0,0 +1,436 @@ +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MFD_TABLA_CORE_H__ +#define __MFD_TABLA_CORE_H__ + +#include +#include +#include +#include +#include + +#define WCD9XXX_MAX_IRQ_REGS 4 +#define WCD9XXX_MAX_NUM_IRQS (WCD9XXX_MAX_IRQ_REGS * 8) +#define WCD9XXX_SLIM_NUM_PORT_REG 3 +#define TABLA_VERSION_1_0 0 +#define TABLA_VERSION_1_1 1 +#define TABLA_VERSION_2_0 2 +#define TABLA_IS_1_X(ver) \ + (((ver == TABLA_VERSION_1_0) || (ver == TABLA_VERSION_1_1)) ? 1 : 0) +#define TABLA_IS_2_0(ver) ((ver == TABLA_VERSION_2_0) ? 1 : 0) + +#define WCD9XXX_SUPPLY_BUCK_NAME "cdc-vdd-buck" + +#define SITAR_VERSION_1P0 0 +#define SITAR_VERSION_1P1 1 +#define SITAR_IS_1P0(ver) \ + ((ver == SITAR_VERSION_1P0) ? 1 : 0) +#define SITAR_IS_1P1(ver) \ + ((ver == SITAR_VERSION_1P1) ? 1 : 0) + +#define TAIKO_VERSION_1_0 1 +#define TAIKO_IS_1_0(ver) \ + ((ver == TAIKO_VERSION_1_0) ? 1 : 0) + +#define TAPAN_VERSION_1_0 0 +#define TAPAN_IS_1_0(ver) \ + ((ver == TAPAN_VERSION_1_0) ? 1 : 0) + +#define TOMTOM_VERSION_1_0 1 +#define TOMTOM_IS_1_0(ver) \ + ((ver == TOMTOM_VERSION_1_0) ? 1 : 0) + +#define TASHA_VERSION_1_0 0 +#define TASHA_VERSION_1_1 1 +#define TASHA_VERSION_2_0 2 + +#define TASHA_IS_1_0(wcd) \ + ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \ + ((wcd->version == TASHA_VERSION_1_0) ? 1 : 0) : 0) + +#define TASHA_IS_1_1(wcd) \ + ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \ + ((wcd->version == TASHA_VERSION_1_1) ? 1 : 0) : 0) + +#define TASHA_IS_2_0(wcd) \ + ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \ + ((wcd->version == TASHA_VERSION_2_0) ? 1 : 0) : 0) + +/* + * As fine version info cannot be retrieved before tavil probe. + * Define three coarse versions for possible future use before tavil probe. + */ +#define TAVIL_VERSION_1_0 0 +#define TAVIL_VERSION_1_1 1 +#define TAVIL_VERSION_WCD9340_1_0 2 +#define TAVIL_VERSION_WCD9341_1_0 3 +#define TAVIL_VERSION_WCD9340_1_1 4 +#define TAVIL_VERSION_WCD9341_1_1 5 + +#define TAVIL_IS_1_0(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_1_0 || \ + wcd->version == TAVIL_VERSION_WCD9340_1_0 || \ + wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0) +#define TAVIL_IS_1_1(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_1_1 || \ + wcd->version == TAVIL_VERSION_WCD9340_1_1 || \ + wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9340_1_0(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9340_1_0) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9341_1_0(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9340_1_1(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9340_1_1) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9341_1_1(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0) + +#define IS_CODEC_TYPE(wcd, wcdtype) \ + ((wcd->type == wcdtype) ? true : false) +#define IS_CODEC_VERSION(wcd, wcdversion) \ + ((wcd->version == wcdversion) ? true : false) + +enum { + CDC_V_1_0, + CDC_V_1_1, + CDC_V_2_0, +}; + +enum codec_variant { + WCD9XXX, + WCD9330, + WCD9335, + WCD9326, + WCD934X, +}; + +enum wcd9xxx_slim_slave_addr_type { + WCD9XXX_SLIM_SLAVE_ADDR_TYPE_0, + WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1, +}; + +enum wcd9xxx_pm_state { + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE, + WCD9XXX_PM_ASLEEP, +}; + +enum { + WCD9XXX_INTR_STATUS_BASE = 0, + WCD9XXX_INTR_CLEAR_BASE, + WCD9XXX_INTR_MASK_BASE, + WCD9XXX_INTR_LEVEL_BASE, + WCD9XXX_INTR_CLR_COMMIT, + WCD9XXX_INTR_REG_MAX, +}; + +enum wcd9xxx_intf_status { + WCD9XXX_INTERFACE_TYPE_PROBING, + WCD9XXX_INTERFACE_TYPE_SLIMBUS, + WCD9XXX_INTERFACE_TYPE_I2C, +}; + +enum { + /* INTR_REG 0 */ + WCD9XXX_IRQ_SLIMBUS = 0, + WCD9XXX_IRQ_MBHC_REMOVAL, + WCD9XXX_IRQ_MBHC_SHORT_TERM, + WCD9XXX_IRQ_MBHC_PRESS, + WCD9XXX_IRQ_MBHC_RELEASE, + WCD9XXX_IRQ_MBHC_POTENTIAL, + WCD9XXX_IRQ_MBHC_INSERTION, + WCD9XXX_IRQ_BG_PRECHARGE, + /* INTR_REG 1 */ + WCD9XXX_IRQ_PA1_STARTUP, + WCD9XXX_IRQ_PA2_STARTUP, + WCD9XXX_IRQ_PA3_STARTUP, + WCD9XXX_IRQ_PA4_STARTUP, + WCD9306_IRQ_HPH_PA_OCPR_FAULT = WCD9XXX_IRQ_PA4_STARTUP, + WCD9XXX_IRQ_PA5_STARTUP, + WCD9XXX_IRQ_MICBIAS1_PRECHARGE, + WCD9306_IRQ_HPH_PA_OCPL_FAULT = WCD9XXX_IRQ_MICBIAS1_PRECHARGE, + WCD9XXX_IRQ_MICBIAS2_PRECHARGE, + WCD9XXX_IRQ_MICBIAS3_PRECHARGE, + /* INTR_REG 2 */ + WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, + WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, + WCD9XXX_IRQ_EAR_PA_OCPL_FAULT, + WCD9XXX_IRQ_HPH_L_PA_STARTUP, + WCD9XXX_IRQ_HPH_R_PA_STARTUP, + WCD9320_IRQ_EAR_PA_STARTUP, + WCD9306_IRQ_MBHC_JACK_SWITCH = WCD9320_IRQ_EAR_PA_STARTUP, + WCD9310_NUM_IRQS, + WCD9XXX_IRQ_RESERVED_0 = WCD9310_NUM_IRQS, + WCD9XXX_IRQ_RESERVED_1, + WCD9330_IRQ_SVASS_ERR_EXCEPTION = WCD9310_NUM_IRQS, + WCD9330_IRQ_MBHC_JACK_SWITCH, + /* INTR_REG 3 */ + WCD9XXX_IRQ_MAD_AUDIO, + WCD9XXX_IRQ_MAD_ULTRASOUND, + WCD9XXX_IRQ_MAD_BEACON, + WCD9XXX_IRQ_SPEAKER_CLIPPING, + WCD9320_IRQ_MBHC_JACK_SWITCH, + WCD9306_NUM_IRQS, + WCD9XXX_IRQ_VBAT_MONITOR_ATTACK = WCD9306_NUM_IRQS, + WCD9XXX_IRQ_VBAT_MONITOR_RELEASE, + WCD9XXX_NUM_IRQS, + /* WCD9330 INTR1_REG 3*/ + WCD9330_IRQ_SVASS_ENGINE = WCD9XXX_IRQ_MAD_AUDIO, + WCD9330_IRQ_MAD_AUDIO, + WCD9330_IRQ_MAD_ULTRASOUND, + WCD9330_IRQ_MAD_BEACON, + WCD9330_IRQ_SPEAKER1_CLIPPING, + WCD9330_IRQ_SPEAKER2_CLIPPING, + WCD9330_IRQ_VBAT_MONITOR_ATTACK, + WCD9330_IRQ_VBAT_MONITOR_RELEASE, + WCD9330_NUM_IRQS, + WCD9XXX_IRQ_RESERVED_2 = WCD9330_NUM_IRQS, +}; + +enum { + TABLA_NUM_IRQS = WCD9310_NUM_IRQS, + SITAR_NUM_IRQS = WCD9310_NUM_IRQS, + TAIKO_NUM_IRQS = WCD9XXX_NUM_IRQS, + TAPAN_NUM_IRQS = WCD9306_NUM_IRQS, + TOMTOM_NUM_IRQS = WCD9330_NUM_IRQS, +}; + +struct intr_data { + int intr_num; + bool clear_first; +}; + +struct wcd9xxx_core_resource { + struct mutex irq_lock; + struct mutex nested_irq_lock; + + enum wcd9xxx_pm_state pm_state; + struct mutex pm_lock; + /* pm_wq notifies change of pm_state */ + wait_queue_head_t pm_wq; + struct pm_qos_request pm_qos_req; + int wlock_holders; + + + /* holds the table of interrupts per codec */ + const struct intr_data *intr_table; + int intr_table_size; + unsigned int irq_base; + unsigned int irq; + u8 irq_masks_cur[WCD9XXX_MAX_IRQ_REGS]; + u8 irq_masks_cache[WCD9XXX_MAX_IRQ_REGS]; + bool irq_level_high[WCD9XXX_MAX_NUM_IRQS]; + int num_irqs; + int num_irq_regs; + u16 intr_reg[WCD9XXX_INTR_REG_MAX]; + struct regmap *wcd_core_regmap; + + /* Pointer to parent container data structure */ + void *parent; + + struct device *dev; + struct irq_domain *domain; +}; + +/* + * data structure for Slimbus and I2S channel. + * Some of fields are only used in smilbus mode + */ +struct wcd9xxx_ch { + u32 sph; /* share channel handle - slimbus only */ + u32 ch_num; /* + * vitrual channel number, such as 128 -144. + * apply for slimbus only + */ + u16 ch_h; /* chanel handle - slimbus only */ + u16 port; /* + * tabla port for RX and TX + * such as 0-9 for TX and 10 -16 for RX + * apply for both i2s and slimbus + */ + u16 shift; /* + * shift bit for RX and TX + * apply for both i2s and slimbus + */ + struct list_head list; /* + * channel link list + * apply for both i2s and slimbus + */ +}; + +struct wcd9xxx_codec_dai_data { + u32 rate; /* sample rate */ + u32 bit_width; /* sit width 16,24,32 */ + struct list_head wcd9xxx_ch_list; /* channel list */ + u16 grph; /* slimbus group handle */ + unsigned long ch_mask; + wait_queue_head_t dai_wait; + bool bus_down_in_recovery; +}; + +#define WCD9XXX_CH(xport, xshift) \ + {.port = xport, .shift = xshift} + +enum wcd9xxx_chipid_major { + TABLA_MAJOR = cpu_to_le16(0x100), + SITAR_MAJOR = cpu_to_le16(0x101), + TAIKO_MAJOR = cpu_to_le16(0x102), + TAPAN_MAJOR = cpu_to_le16(0x103), + TOMTOM_MAJOR = cpu_to_le16(0x105), + TASHA_MAJOR = cpu_to_le16(0x0), + TASHA2P0_MAJOR = cpu_to_le16(0x107), + TAVIL_MAJOR = cpu_to_le16(0x108), +}; + +enum codec_power_states { + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD_REGION_POWER_COLLAPSE_BEGIN, + WCD_REGION_POWER_DOWN, +}; + +enum wcd_power_regions { + WCD9XXX_DIG_CORE_REGION_1, + WCD9XXX_MAX_PWR_REGIONS, +}; + +struct wcd9xxx_codec_type { + u16 id_major; + u16 id_minor; + struct mfd_cell *dev; + int size; + int num_irqs; + int version; /* -1 to retrieve version from chip version register */ + enum wcd9xxx_slim_slave_addr_type slim_slave_type; + u16 i2c_chip_status; + const struct intr_data *intr_tbl; + int intr_tbl_size; + u16 intr_reg[WCD9XXX_INTR_REG_MAX]; +}; + +struct wcd9xxx_power_region { + enum codec_power_states power_state; + u16 pwr_collapse_reg_min; + u16 pwr_collapse_reg_max; +}; + +struct wcd9xxx { + struct device *dev; + struct slim_device *slim; + struct slim_device *slim_slave; + struct mutex io_lock; + struct mutex xfer_lock; + u8 version; + + int reset_gpio; + struct device_node *wcd_rst_np; + + int (*read_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *dest, bool interface_reg); + int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *src, bool interface_reg); + int (*multi_reg_write)(struct wcd9xxx *wcd9xxx, const void *data, + size_t count); + int (*dev_down)(struct wcd9xxx *wcd9xxx); + int (*post_reset)(struct wcd9xxx *wcd9xxx); + + void *ssr_priv; + bool dev_up; + + u32 num_of_supplies; + struct regulator_bulk_data *supplies; + + struct wcd9xxx_core_resource core_res; + + u16 id_minor; + u16 id_major; + + /* Slimbus or I2S port */ + u32 num_rx_port; + u32 num_tx_port; + struct wcd9xxx_ch *rx_chs; + struct wcd9xxx_ch *tx_chs; + u32 mclk_rate; + enum codec_variant type; + struct regmap *regmap; + + struct wcd9xxx_codec_type *codec_type; + bool prev_pg_valid; + u8 prev_pg; + u8 avoid_cdc_rstlow; + struct wcd9xxx_power_region *wcd9xxx_pwr[WCD9XXX_MAX_PWR_REGIONS]; +}; + +struct wcd9xxx_reg_val { + unsigned short reg; /* register address */ + u8 *buf; /* buffer to be written to reg. addr */ + int bytes; /* number of bytes to be written */ +}; + +int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg); +int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg, + u8 val); +int wcd9xxx_get_logical_addresses(u8 *pgd_la, u8 *inf_la); +int wcd9xxx_slim_write_repeat(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *src); +int wcd9xxx_slim_reserve_bw(struct wcd9xxx *wcd9xxx, + u32 bw_ops, bool commit); +int wcd9xxx_set_power_state(struct wcd9xxx *wcd9xxx, enum codec_power_states, + enum wcd_power_regions); +int wcd9xxx_get_current_power_state(struct wcd9xxx *wcd9xxx, + enum wcd_power_regions); + +int wcd9xxx_page_write(struct wcd9xxx *wcd9xxx, unsigned short *reg); + +int wcd9xxx_slim_bulk_write(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_reg_val *bulk_reg, + unsigned int size, bool interface); + +extern int wcd9xxx_core_res_init( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + int num_irqs, int num_irq_regs, struct regmap *wcd_regmap); + +extern void wcd9xxx_core_res_deinit( + struct wcd9xxx_core_resource *wcd9xxx_core_res); + +extern int wcd9xxx_core_res_suspend( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + pm_message_t pmesg); + +extern int wcd9xxx_core_res_resume( + struct wcd9xxx_core_resource *wcd9xxx_core_res); + +extern int wcd9xxx_core_irq_init( + struct wcd9xxx_core_resource *wcd9xxx_core_res); + +extern int wcd9xxx_assign_irq(struct wcd9xxx_core_resource *wcd9xxx_core_res, + unsigned int irq, + unsigned int irq_base); + +extern enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void); +extern void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status); + +extern enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + enum wcd9xxx_pm_state o, + enum wcd9xxx_pm_state n); +static inline int __init wcd9xxx_irq_of_init(struct device_node *node, + struct device_node *parent) +{ + return 0; +} +#endif diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h new file mode 100644 index 0000000000000000000000000000000000000000..f188e850b800aa0588293b5382e07bac1087b122 --- /dev/null +++ b/include/linux/mfd/wcd9xxx/pdata.h @@ -0,0 +1,197 @@ +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MFD_WCD9XXX_PDATA_H__ + +#define __MFD_WCD9XXX_PDATA_H__ + +#include +#include + +#define MICBIAS_EXT_BYP_CAP 0x00 +#define MICBIAS_NO_EXT_BYP_CAP 0x01 + +#define SITAR_LDOH_1P95_V 0x0 +#define SITAR_LDOH_2P35_V 0x1 +#define SITAR_LDOH_2P75_V 0x2 +#define SITAR_LDOH_2P85_V 0x3 + +#define SITAR_CFILT1_SEL 0x0 +#define SITAR_CFILT2_SEL 0x1 +#define SITAR_CFILT3_SEL 0x2 + +#define WCD9XXX_LDOH_1P95_V 0x0 +#define WCD9XXX_LDOH_2P35_V 0x1 +#define WCD9XXX_LDOH_2P75_V 0x2 +#define WCD9XXX_LDOH_2P85_V 0x3 +#define WCD9XXX_LDOH_3P0_V 0x3 + +#define TABLA_LDOH_1P95_V 0x0 +#define TABLA_LDOH_2P35_V 0x1 +#define TABLA_LDOH_2P75_V 0x2 +#define TABLA_LDOH_2P85_V 0x3 + +#define TABLA_CFILT1_SEL 0x0 +#define TABLA_CFILT2_SEL 0x1 +#define TABLA_CFILT3_SEL 0x2 + +#define MAX_AMIC_CHANNEL 7 + +#define TABLA_OCP_300_MA 0x0 +#define TABLA_OCP_350_MA 0x2 +#define TABLA_OCP_365_MA 0x3 +#define TABLA_OCP_150_MA 0x4 +#define TABLA_OCP_190_MA 0x6 +#define TABLA_OCP_220_MA 0x7 + +#define TABLA_DCYCLE_255 0x0 +#define TABLA_DCYCLE_511 0x1 +#define TABLA_DCYCLE_767 0x2 +#define TABLA_DCYCLE_1023 0x3 +#define TABLA_DCYCLE_1279 0x4 +#define TABLA_DCYCLE_1535 0x5 +#define TABLA_DCYCLE_1791 0x6 +#define TABLA_DCYCLE_2047 0x7 +#define TABLA_DCYCLE_2303 0x8 +#define TABLA_DCYCLE_2559 0x9 +#define TABLA_DCYCLE_2815 0xA +#define TABLA_DCYCLE_3071 0xB +#define TABLA_DCYCLE_3327 0xC +#define TABLA_DCYCLE_3583 0xD +#define TABLA_DCYCLE_3839 0xE +#define TABLA_DCYCLE_4095 0xF + +#define WCD9XXX_MCLK_CLK_12P288MHZ 12288000 +#define WCD9XXX_MCLK_CLK_9P6HZ 9600000 + +/* Only valid for 9.6 MHz mclk */ +#define WCD9XXX_DMIC_SAMPLE_RATE_600KHZ 600000 +#define WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ 2400000 +#define WCD9XXX_DMIC_SAMPLE_RATE_3P2MHZ 3200000 +#define WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ 4800000 + +/* Only valid for 12.288 MHz mclk */ +#define WCD9XXX_DMIC_SAMPLE_RATE_768KHZ 768000 +#define WCD9XXX_DMIC_SAMPLE_RATE_2P048MHZ 2048000 +#define WCD9XXX_DMIC_SAMPLE_RATE_3P072MHZ 3072000 +#define WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ 4096000 +#define WCD9XXX_DMIC_SAMPLE_RATE_6P144MHZ 6144000 + +#define WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED 0 + +#define WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED 0 + +struct wcd9xxx_amic { + /*legacy mode, txfe_enable and txfe_buff take 7 input + * each bit represent the channel / TXFE number + * and numbered as below + * bit 0 = channel 1 / TXFE1_ENABLE / TXFE1_BUFF + * bit 1 = channel 2 / TXFE2_ENABLE / TXFE2_BUFF + * ... + * bit 7 = channel 7 / TXFE7_ENABLE / TXFE7_BUFF + */ + u8 legacy_mode:MAX_AMIC_CHANNEL; + u8 txfe_enable:MAX_AMIC_CHANNEL; + u8 txfe_buff:MAX_AMIC_CHANNEL; + u8 use_pdata:MAX_AMIC_CHANNEL; +}; + +/* Each micbias can be assigned to one of three cfilters + * Vbatt_min >= .15V + ldoh_v + * ldoh_v >= .15v + cfiltx_mv + * If ldoh_v = 1.95 160 mv < cfiltx_mv < 1800 mv + * If ldoh_v = 2.35 200 mv < cfiltx_mv < 2200 mv + * If ldoh_v = 2.75 240 mv < cfiltx_mv < 2600 mv + * If ldoh_v = 2.85 250 mv < cfiltx_mv < 2700 mv + */ + +struct wcd9xxx_micbias_setting { + u8 ldoh_v; + u32 cfilt1_mv; /* in mv */ + u32 cfilt2_mv; /* in mv */ + u32 cfilt3_mv; /* in mv */ + u32 micb1_mv; + u32 micb2_mv; + u32 micb3_mv; + u32 micb4_mv; + /* Different WCD9xxx series codecs may not + * have 4 mic biases. If a codec has fewer + * mic biases, some of these properties will + * not be used. + */ + u8 bias1_cfilt_sel; + u8 bias2_cfilt_sel; + u8 bias3_cfilt_sel; + u8 bias4_cfilt_sel; + u8 bias1_cap_mode; + u8 bias2_cap_mode; + u8 bias3_cap_mode; + u8 bias4_cap_mode; + bool bias2_is_headset_only; +}; + +struct wcd9xxx_ocp_setting { + unsigned int use_pdata:1; /* 0 - use sys default as recommended */ + unsigned int num_attempts:4; /* up to 15 attempts */ + unsigned int run_time:4; /* in duty cycle */ + unsigned int wait_time:4; /* in duty cycle */ + unsigned int hph_ocp_limit:3; /* Headphone OCP current limit */ +}; + +#define WCD9XXX_MAX_REGULATOR 9 +/* + * format : TABLA__CUR_MAX + * + * from Tabla objective spec + */ + +#define WCD9XXX_CDC_VDDA_CP_CUR_MAX 500000 +#define WCD9XXX_CDC_VDDA_RX_CUR_MAX 20000 +#define WCD9XXX_CDC_VDDA_TX_CUR_MAX 20000 +#define WCD9XXX_VDDIO_CDC_CUR_MAX 5000 + +#define WCD9XXX_VDDD_CDC_D_CUR_MAX 5000 +#define WCD9XXX_VDDD_CDC_A_CUR_MAX 5000 + +#define WCD9XXX_VDD_SPKDRV_NAME "cdc-vdd-spkdrv" +#define WCD9XXX_VDD_SPKDRV2_NAME "cdc-vdd-spkdrv-2" + +struct wcd9xxx_regulator { + const char *name; + int min_uV; + int max_uV; + int optimum_uA; + bool ondemand; + struct regulator *regulator; +}; + +struct wcd9xxx_pdata { + int irq; + int irq_base; + int num_irqs; + int reset_gpio; + struct device_node *wcd_rst_np; + struct wcd9xxx_amic amic_settings; + struct slim_device slimbus_slave_device; + struct wcd9xxx_micbias_setting micbias; + struct wcd9xxx_ocp_setting ocp; + struct cdc_regulator *regulator; + int num_supplies; + u32 mclk_rate; + u32 dmic_sample_rate; + u32 mad_dmic_sample_rate; + u32 ecpp_dmic_sample_rate; + u32 dmic_clk_drv; + u16 use_pinctrl; +}; + +#endif diff --git a/include/linux/mfd/wcd9xxx/wcd9330_registers.h b/include/linux/mfd/wcd9xxx/wcd9330_registers.h new file mode 100644 index 0000000000000000000000000000000000000000..c37d25f3f528ee28b81e2e90a224409993c33c54 --- /dev/null +++ b/include/linux/mfd/wcd9xxx/wcd9330_registers.h @@ -0,0 +1,1626 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef WCD9330_REGISTERS_H +#define WCD9330_REGISTERS_H + +#include + +#define TOMTOM_A_CHIP_CTL (0x000) +#define TOMTOM_A_CHIP_CTL__POR (0x38) +#define TOMTOM_A_CHIP_STATUS (0x001) +#define TOMTOM_A_CHIP_STATUS__POR (0x00) +#define TOMTOM_A_CHIP_ID_BYTE_0 (0x004) +#define TOMTOM_A_CHIP_ID_BYTE_0__POR (0x00) +#define TOMTOM_A_CHIP_ID_BYTE_1 (0x005) +#define TOMTOM_A_CHIP_ID_BYTE_1__POR (0x00) +#define TOMTOM_A_CHIP_ID_BYTE_2 (0x006) +#define TOMTOM_A_CHIP_ID_BYTE_2__POR (0x05) +#define TOMTOM_A_CHIP_ID_BYTE_3 (0x007) +#define TOMTOM_A_CHIP_ID_BYTE_3__POR (0x01) +#define TOMTOM_A_CHIP_I2C_SLAVE_ID (0x008) +#define TOMTOM_A_CHIP_I2C_SLAVE_ID__POR (0x01) +#define TOMTOM_A_SLAVE_ID_1 (0x00C) +#define TOMTOM_A_SLAVE_ID_1__POR (0x77) +#define TOMTOM_A_SLAVE_ID_2 (0x00D) +#define TOMTOM_A_SLAVE_ID_2__POR (0x66) +#define TOMTOM_A_SLAVE_ID_3 (0x00E) +#define TOMTOM_A_SLAVE_ID_3__POR (0x55) +#define TOMTOM_A_PIN_CTL_OE0 (0x010) +#define TOMTOM_A_PIN_CTL_OE0__POR (0x00) +#define TOMTOM_A_PIN_CTL_OE1 (0x011) +#define TOMTOM_A_PIN_CTL_OE1__POR (0x00) +#define TOMTOM_A_PIN_CTL_OE2 (0x012) +#define TOMTOM_A_PIN_CTL_OE2__POR (0x00) +#define TOMTOM_A_PIN_CTL_DATA0 (0x013) +#define TOMTOM_A_PIN_CTL_DATA0__POR (0x00) +#define TOMTOM_A_PIN_CTL_DATA1 (0x014) +#define TOMTOM_A_PIN_CTL_DATA1__POR (0x00) +#define TOMTOM_A_PIN_CTL_DATA2 (0x015) +#define TOMTOM_A_PIN_CTL_DATA2__POR (0x00) +#define TOMTOM_A_HDRIVE_GENERIC (0x018) +#define TOMTOM_A_HDRIVE_GENERIC__POR (0x00) +#define TOMTOM_A_HDRIVE_OVERRIDE (0x019) +#define TOMTOM_A_HDRIVE_OVERRIDE__POR (0x08) +#define TOMTOM_A_ANA_CSR_WAIT_STATE (0x01C) +#define TOMTOM_A_ANA_CSR_WAIT_STATE__POR (0x44) +#define TOMTOM_A_PROCESS_MONITOR_CTL0 (0x020) +#define TOMTOM_A_PROCESS_MONITOR_CTL0__POR (0x80) +#define TOMTOM_A_PROCESS_MONITOR_CTL1 (0x021) +#define TOMTOM_A_PROCESS_MONITOR_CTL1__POR (0x00) +#define TOMTOM_A_PROCESS_MONITOR_CTL2 (0x022) +#define TOMTOM_A_PROCESS_MONITOR_CTL2__POR (0x00) +#define TOMTOM_A_PROCESS_MONITOR_CTL3 (0x023) +#define TOMTOM_A_PROCESS_MONITOR_CTL3__POR (0x01) +#define TOMTOM_A_QFUSE_CTL (0x028) +#define TOMTOM_A_QFUSE_CTL__POR (0x00) +#define TOMTOM_A_QFUSE_STATUS (0x029) +#define TOMTOM_A_QFUSE_STATUS__POR (0x00) +#define TOMTOM_A_QFUSE_DATA_OUT0 (0x02A) +#define TOMTOM_A_QFUSE_DATA_OUT0__POR (0x00) +#define TOMTOM_A_QFUSE_DATA_OUT1 (0x02B) +#define TOMTOM_A_QFUSE_DATA_OUT1__POR (0x00) +#define TOMTOM_A_QFUSE_DATA_OUT2 (0x02C) +#define TOMTOM_A_QFUSE_DATA_OUT2__POR (0x00) +#define TOMTOM_A_QFUSE_DATA_OUT3 (0x02D) +#define TOMTOM_A_QFUSE_DATA_OUT3__POR (0x00) +#define TOMTOM_A_QFUSE_DATA_OUT4 (0x02E) +#define TOMTOM_A_QFUSE_DATA_OUT4__POR (0x00) +#define TOMTOM_A_QFUSE_DATA_OUT5 (0x02F) +#define TOMTOM_A_QFUSE_DATA_OUT5__POR (0x00) +#define TOMTOM_A_QFUSE_DATA_OUT6 (0x030) +#define TOMTOM_A_QFUSE_DATA_OUT6__POR (0x00) +#define TOMTOM_A_QFUSE_DATA_OUT7 (0x031) +#define TOMTOM_A_QFUSE_DATA_OUT7__POR (0x00) +#define TOMTOM_A_CDC_CTL (0x034) +#define TOMTOM_A_CDC_CTL__POR (0x00) +#define TOMTOM_A_LEAKAGE_CTL (0x03C) +#define TOMTOM_A_LEAKAGE_CTL__POR (0x04) +#define TOMTOM_A_SVASS_MEM_PTR0 (0x044) +#define TOMTOM_A_SVASS_MEM_PTR0__POR (0x00) +#define TOMTOM_A_SVASS_MEM_PTR1 (0x045) +#define TOMTOM_A_SVASS_MEM_PTR1__POR (0x00) +#define TOMTOM_A_SVASS_MEM_PTR2 (0x046) +#define TOMTOM_A_SVASS_MEM_PTR2__POR (0x00) +#define TOMTOM_A_SVASS_MEM_CTL (0x048) +#define TOMTOM_A_SVASS_MEM_CTL__POR (0x04) +#define TOMTOM_A_SVASS_MEM_BANK (0x049) +#define TOMTOM_A_SVASS_MEM_BANK__POR (0x00) +#define TOMTOM_A_DMIC_B1_CTL (0x04A) +#define TOMTOM_A_DMIC_B1_CTL__POR (0x00) +#define TOMTOM_A_DMIC_B2_CTL (0x04B) +#define TOMTOM_A_DMIC_B2_CTL__POR (0x00) +#define TOMTOM_A_SVASS_CLKRST_CTL (0x04C) +#define TOMTOM_A_SVASS_CLKRST_CTL__POR (0x00) +#define TOMTOM_A_SVASS_CPAR_CFG (0x04D) +#define TOMTOM_A_SVASS_CPAR_CFG__POR (0x00) +#define TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD (0x04E) +#define TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD__POR (0x14) +#define TOMTOM_A_SVASS_CPAR_WDOG_CFG (0x04F) +#define TOMTOM_A_SVASS_CPAR_WDOG_CFG__POR (0x00) +#define TOMTOM_A_SVASS_CFG (0x050) +#define TOMTOM_A_SVASS_CFG__POR (0x01) +#define TOMTOM_A_SVASS_SPE_CFG (0x051) +#define TOMTOM_A_SVASS_SPE_CFG__POR (0x04) +#define TOMTOM_A_SVASS_STATUS (0x052) +#define TOMTOM_A_SVASS_STATUS__POR (0x00) +#define TOMTOM_A_SVASS_INT_MASK (0x053) +#define TOMTOM_A_SVASS_INT_MASK__POR (0x3F) +#define TOMTOM_A_SVASS_INT_STATUS (0x054) +#define TOMTOM_A_SVASS_INT_STATUS__POR (0x00) +#define TOMTOM_A_SVASS_INT_CLR (0x055) +#define TOMTOM_A_SVASS_INT_CLR__POR (0x00) +#define TOMTOM_A_SVASS_DEBUG (0x056) +#define TOMTOM_A_SVASS_DEBUG__POR (0x00) +#define TOMTOM_A_SVASS_SPE_BKUP_INT (0x057) +#define TOMTOM_A_SVASS_SPE_BKUP_INT__POR (0x00) +#define TOMTOM_A_SVASS_MEM_ACC (0x058) +#define TOMTOM_A_SVASS_MEM_ACC__POR (0x00) +#define TOMTOM_A_MEM_LEAKAGE_CTL (0x059) +#define TOMTOM_A_MEM_LEAKAGE_CTL__POR (0x04) +#define TOMTOM_A_SVASS_SPE_INBOX_TRG (0x05A) +#define TOMTOM_A_SVASS_SPE_INBOX_TRG__POR (0x00) +#define TOMTOM_A_SVASS_SPE_INBOX_0 (0x060) +#define TOMTOM_A_SVASS_SPE_INBOX_0__POR (0x00) +#define TOMTOM_A_SVASS_SPE_INBOX_1 (0x061) +#define TOMTOM_A_SVASS_SPE_INBOX_1__POR (0x00) +#define TOMTOM_A_SVASS_SPE_INBOX_2 (0x062) +#define TOMTOM_A_SVASS_SPE_INBOX_2__POR (0x00) +#define TOMTOM_A_SVASS_SPE_INBOX_3 (0x063) +#define TOMTOM_A_SVASS_SPE_INBOX_3__POR (0x00) +#define TOMTOM_A_SVASS_SPE_INBOX_4 (0x064) +#define TOMTOM_A_SVASS_SPE_INBOX_4__POR (0x00) +#define TOMTOM_A_SVASS_SPE_INBOX_5 (0x065) +#define TOMTOM_A_SVASS_SPE_INBOX_5__POR (0x00) +#define TOMTOM_A_SVASS_SPE_INBOX_6 (0x066) +#define TOMTOM_A_SVASS_SPE_INBOX_6__POR (0x00) +#define TOMTOM_A_SVASS_SPE_INBOX_7 (0x067) +#define TOMTOM_A_SVASS_SPE_INBOX_7__POR (0x00) +#define TOMTOM_A_SVASS_SPE_INBOX_8 (0x068) +#define TOMTOM_A_SVASS_SPE_INBOX_8__POR (0x00) +#define TOMTOM_A_SVASS_SPE_INBOX_9 (0x069) +#define TOMTOM_A_SVASS_SPE_INBOX_9__POR (0x00) +#define TOMTOM_A_SVASS_SPE_INBOX_10 (0x06A) +#define TOMTOM_A_SVASS_SPE_INBOX_10__POR (0x00) +#define TOMTOM_A_SVASS_SPE_INBOX_11 (0x06B) +#define TOMTOM_A_SVASS_SPE_INBOX_11__POR (0x00) +#define TOMTOM_A_SVASS_SPE_OUTBOX_0 (0x070) +#define TOMTOM_A_SVASS_SPE_OUTBOX_0__POR (0x00) +#define TOMTOM_A_SVASS_SPE_OUTBOX_1 (0x071) +#define TOMTOM_A_SVASS_SPE_OUTBOX_1__POR (0x00) +#define TOMTOM_A_SVASS_SPE_OUTBOX_2 (0x072) +#define TOMTOM_A_SVASS_SPE_OUTBOX_2__POR (0x00) +#define TOMTOM_A_SVASS_SPE_OUTBOX_3 (0x073) +#define TOMTOM_A_SVASS_SPE_OUTBOX_3__POR (0x00) +#define TOMTOM_A_SVASS_SPE_OUTBOX_4 (0x074) +#define TOMTOM_A_SVASS_SPE_OUTBOX_4__POR (0x00) +#define TOMTOM_A_SVASS_SPE_OUTBOX_5 (0x075) +#define TOMTOM_A_SVASS_SPE_OUTBOX_5__POR (0x00) +#define TOMTOM_A_SVASS_SPE_OUTBOX_6 (0x076) +#define TOMTOM_A_SVASS_SPE_OUTBOX_6__POR (0x00) +#define TOMTOM_A_SVASS_SPE_OUTBOX_7 (0x077) +#define TOMTOM_A_SVASS_SPE_OUTBOX_7__POR (0x00) +#define TOMTOM_A_SVASS_SPE_OUTBOX_8 (0x078) +#define TOMTOM_A_SVASS_SPE_OUTBOX_8__POR (0x00) +#define TOMTOM_A_SVASS_SPE_OUTBOX_9 (0x079) +#define TOMTOM_A_SVASS_SPE_OUTBOX_9__POR (0x00) +#define TOMTOM_A_SVASS_SPE_OUTBOX_10 (0x07A) +#define TOMTOM_A_SVASS_SPE_OUTBOX_10__POR (0x00) +#define TOMTOM_A_SVASS_SPE_OUTBOX_11 (0x07B) +#define TOMTOM_A_SVASS_SPE_OUTBOX_11__POR (0x00) +#define TOMTOM_A_INTR_MODE (0x090) +#define TOMTOM_A_INTR_MODE__POR (0x00) +#define TOMTOM_A_INTR1_MASK0 (0x094) +#define TOMTOM_A_INTR1_MASK0__POR (0xFF) +#define TOMTOM_A_INTR1_MASK1 (0x095) +#define TOMTOM_A_INTR1_MASK1__POR (0xFF) +#define TOMTOM_A_INTR1_MASK2 (0x096) +#define TOMTOM_A_INTR1_MASK2__POR (0xFF) +#define TOMTOM_A_INTR1_MASK3 (0x097) +#define TOMTOM_A_INTR1_MASK3__POR (0xFF) +#define TOMTOM_A_INTR1_STATUS0 (0x098) +#define TOMTOM_A_INTR1_STATUS0__POR (0x00) +#define TOMTOM_A_INTR1_STATUS1 (0x099) +#define TOMTOM_A_INTR1_STATUS1__POR (0x00) +#define TOMTOM_A_INTR1_STATUS2 (0x09A) +#define TOMTOM_A_INTR1_STATUS2__POR (0x00) +#define TOMTOM_A_INTR1_STATUS3 (0x09B) +#define TOMTOM_A_INTR1_STATUS3__POR (0x00) +#define TOMTOM_A_INTR1_CLEAR0 (0x09C) +#define TOMTOM_A_INTR1_CLEAR0__POR (0x00) +#define TOMTOM_A_INTR1_CLEAR1 (0x09D) +#define TOMTOM_A_INTR1_CLEAR1__POR (0x00) +#define TOMTOM_A_INTR1_CLEAR2 (0x09E) +#define TOMTOM_A_INTR1_CLEAR2__POR (0x00) +#define TOMTOM_A_INTR1_CLEAR3 (0x09F) +#define TOMTOM_A_INTR1_CLEAR3__POR (0x00) +#define TOMTOM_A_INTR1_LEVEL0 (0x0A0) +#define TOMTOM_A_INTR1_LEVEL0__POR (0x01) +#define TOMTOM_A_INTR1_LEVEL1 (0x0A1) +#define TOMTOM_A_INTR1_LEVEL1__POR (0x00) +#define TOMTOM_A_INTR1_LEVEL2 (0x0A2) +#define TOMTOM_A_INTR1_LEVEL2__POR (0x40) +#define TOMTOM_A_INTR1_LEVEL3 (0x0A3) +#define TOMTOM_A_INTR1_LEVEL3__POR (0x00) +#define TOMTOM_A_INTR1_TEST0 (0x0A4) +#define TOMTOM_A_INTR1_TEST0__POR (0x00) +#define TOMTOM_A_INTR1_TEST1 (0x0A5) +#define TOMTOM_A_INTR1_TEST1__POR (0x00) +#define TOMTOM_A_INTR1_TEST2 (0x0A6) +#define TOMTOM_A_INTR1_TEST2__POR (0x00) +#define TOMTOM_A_INTR1_TEST3 (0x0A7) +#define TOMTOM_A_INTR1_TEST3__POR (0x00) +#define TOMTOM_A_INTR1_SET0 (0x0A8) +#define TOMTOM_A_INTR1_SET0__POR (0x00) +#define TOMTOM_A_INTR1_SET1 (0x0A9) +#define TOMTOM_A_INTR1_SET1__POR (0x00) +#define TOMTOM_A_INTR1_SET2 (0x0AA) +#define TOMTOM_A_INTR1_SET2__POR (0x00) +#define TOMTOM_A_INTR1_SET3 (0x0AB) +#define TOMTOM_A_INTR1_SET3__POR (0x00) +#define TOMTOM_A_INTR2_MASK0 (0x0B0) +#define TOMTOM_A_INTR2_MASK0__POR (0xFF) +#define TOMTOM_A_INTR2_STATUS0 (0x0B2) +#define TOMTOM_A_INTR2_STATUS0__POR (0x00) +#define TOMTOM_A_INTR2_CLEAR0 (0x0B4) +#define TOMTOM_A_INTR2_CLEAR0__POR (0x00) +#define TOMTOM_A_INTR2_LEVEL0 (0x0B6) +#define TOMTOM_A_INTR2_LEVEL0__POR (0x00) +#define TOMTOM_A_INTR2_TEST0 (0x0B8) +#define TOMTOM_A_INTR2_TEST0__POR (0x00) +#define TOMTOM_A_INTR2_SET0 (0x0BA) +#define TOMTOM_A_INTR2_SET0__POR (0x00) +#define TOMTOM_A_CDC_TX_I2S_SCK_MODE (0x0C0) +#define TOMTOM_A_CDC_TX_I2S_SCK_MODE__POR (0x00) +#define TOMTOM_A_CDC_TX_I2S_WS_MODE (0x0C1) +#define TOMTOM_A_CDC_TX_I2S_WS_MODE__POR (0x00) +#define TOMTOM_A_CDC_DMIC_DATA0_MODE (0x0C4) +#define TOMTOM_A_CDC_DMIC_DATA0_MODE__POR (0x00) +#define TOMTOM_A_CDC_DMIC_CLK0_MODE (0x0C5) +#define TOMTOM_A_CDC_DMIC_CLK0_MODE__POR (0x00) +#define TOMTOM_A_CDC_DMIC_DATA1_MODE (0x0C6) +#define TOMTOM_A_CDC_DMIC_DATA1_MODE__POR (0x00) +#define TOMTOM_A_CDC_DMIC_CLK1_MODE (0x0C7) +#define TOMTOM_A_CDC_DMIC_CLK1_MODE__POR (0x00) +#define TOMTOM_A_CDC_RX_I2S_SCK_MODE (0x0C8) +#define TOMTOM_A_CDC_RX_I2S_SCK_MODE__POR (0x00) +#define TOMTOM_A_CDC_RX_I2S_WS_MODE (0x0C9) +#define TOMTOM_A_CDC_RX_I2S_WS_MODE__POR (0x00) +#define TOMTOM_A_CDC_DMIC_DATA2_MODE (0x0CA) +#define TOMTOM_A_CDC_DMIC_DATA2_MODE__POR (0x00) +#define TOMTOM_A_CDC_DMIC_CLK2_MODE (0x0CB) +#define TOMTOM_A_CDC_DMIC_CLK2_MODE__POR (0x00) +#define TOMTOM_A_CDC_INTR1_MODE (0x0CC) +#define TOMTOM_A_CDC_INTR1_MODE__POR (0x00) +#define TOMTOM_A_CDC_SB_NRZ_SEL_MODE (0x0CD) +#define TOMTOM_A_CDC_SB_NRZ_SEL_MODE__POR (0x00) +#define TOMTOM_A_CDC_INTR2_MODE (0x0CE) +#define TOMTOM_A_CDC_INTR2_MODE__POR (0x00) +#define TOMTOM_A_CDC_RF_PA_ON_MODE (0x0CF) +#define TOMTOM_A_CDC_RF_PA_ON_MODE__POR (0x00) +#define TOMTOM_A_CDC_BOOST_MODE (0x0D0) +#define TOMTOM_A_CDC_BOOST_MODE__POR (0x00) +#define TOMTOM_A_CDC_JTCK_MODE (0x0D1) +#define TOMTOM_A_CDC_JTCK_MODE__POR (0x00) +#define TOMTOM_A_CDC_JTDI_MODE (0x0D2) +#define TOMTOM_A_CDC_JTDI_MODE__POR (0x00) +#define TOMTOM_A_CDC_JTMS_MODE (0x0D3) +#define TOMTOM_A_CDC_JTMS_MODE__POR (0x00) +#define TOMTOM_A_CDC_JTDO_MODE (0x0D4) +#define TOMTOM_A_CDC_JTDO_MODE__POR (0x00) +#define TOMTOM_A_CDC_JTRST_MODE (0x0D5) +#define TOMTOM_A_CDC_JTRST_MODE__POR (0x00) +#define TOMTOM_A_CDC_BIST_MODE_MODE (0x0D6) +#define TOMTOM_A_CDC_BIST_MODE_MODE__POR (0x00) +#define TOMTOM_A_CDC_MAD_MAIN_CTL_1 (0x0E0) +#define TOMTOM_A_CDC_MAD_MAIN_CTL_1__POR (0x00) +#define TOMTOM_A_CDC_MAD_MAIN_CTL_2 (0x0E1) +#define TOMTOM_A_CDC_MAD_MAIN_CTL_2__POR (0x00) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_1 (0x0E2) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_1__POR (0x00) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_2 (0x0E3) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_2__POR (0x00) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_3 (0x0E4) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_3__POR (0x00) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_4 (0x0E5) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_4__POR (0x00) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_5 (0x0E6) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_5__POR (0x00) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_6 (0x0E7) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_6__POR (0x00) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_7 (0x0E8) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_7__POR (0x00) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_8 (0x0E9) +#define TOMTOM_A_CDC_MAD_AUDIO_CTL_8__POR (0x00) +#define TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR (0x0EA) +#define TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR__POR (0x00) +#define TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL (0x0EB) +#define TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL__POR (0x40) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_1 (0x0EC) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_1__POR (0x00) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_2 (0x0ED) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_2__POR (0x00) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_3 (0x0EE) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_3__POR (0x00) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_4 (0x0EF) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_4__POR (0x00) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_5 (0x0F0) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_5__POR (0x00) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_6 (0x0F1) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_6__POR (0x00) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_7 (0x0F2) +#define TOMTOM_A_CDC_MAD_ULTR_CTL_7__POR (0x00) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_1 (0x0F3) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_1__POR (0x00) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_2 (0x0F4) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_2__POR (0x00) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_3 (0x0F5) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_3__POR (0x00) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_4 (0x0F6) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_4__POR (0x00) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_5 (0x0F7) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_5__POR (0x00) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_6 (0x0F8) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_6__POR (0x00) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_7 (0x0F9) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_7__POR (0x00) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_8 (0x0FA) +#define TOMTOM_A_CDC_MAD_BEACON_CTL_8__POR (0x00) +#define TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR (0x0FB) +#define TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR__POR (0x00) +#define TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL (0x0FC) +#define TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL__POR (0x00) +#define TOMTOM_A_CDC_MAD_INP_SEL (0x0FD) +#define TOMTOM_A_CDC_MAD_INP_SEL__POR (0x00) +#define TOMTOM_A_BIAS_REF_CTL (0x100) +#define TOMTOM_A_BIAS_REF_CTL__POR (0x1C) +#define TOMTOM_A_BIAS_CENTRAL_BG_CTL (0x101) +#define TOMTOM_A_BIAS_CENTRAL_BG_CTL__POR (0x50) +#define TOMTOM_A_BIAS_PRECHRG_CTL (0x102) +#define TOMTOM_A_BIAS_PRECHRG_CTL__POR (0x07) +#define TOMTOM_A_BIAS_CURR_CTL_1 (0x103) +#define TOMTOM_A_BIAS_CURR_CTL_1__POR (0x52) +#define TOMTOM_A_BIAS_CURR_CTL_2 (0x104) +#define TOMTOM_A_BIAS_CURR_CTL_2__POR (0x00) +#define TOMTOM_A_BIAS_OSC_BG_CTL (0x105) +#define TOMTOM_A_BIAS_OSC_BG_CTL__POR (0x36) +#define TOMTOM_A_CLK_BUFF_EN1 (0x108) +#define TOMTOM_A_CLK_BUFF_EN1__POR (0x04) +#define TOMTOM_A_CLK_BUFF_EN2 (0x109) +#define TOMTOM_A_CLK_BUFF_EN2__POR (0x02) +#define TOMTOM_A_LDO_L_MODE_1 (0x10A) +#define TOMTOM_A_LDO_L_MODE_1__POR (0x08) +#define TOMTOM_A_LDO_L_MODE_2 (0x10B) +#define TOMTOM_A_LDO_L_MODE_2__POR (0x50) +#define TOMTOM_A_LDO_L_CTRL_1 (0x10C) +#define TOMTOM_A_LDO_L_CTRL_1__POR (0x70) +#define TOMTOM_A_LDO_L_CTRL_2 (0x10D) +#define TOMTOM_A_LDO_L_CTRL_2__POR (0x55) +#define TOMTOM_A_LDO_L_CTRL_3 (0x10E) +#define TOMTOM_A_LDO_L_CTRL_3__POR (0x56) +#define TOMTOM_A_LDO_L_CTRL_4 (0x10F) +#define TOMTOM_A_LDO_L_CTRL_4__POR (0x55) +#define TOMTOM_A_LDO_H_MODE_1 (0x110) +#define TOMTOM_A_LDO_H_MODE_1__POR (0x65) +#define TOMTOM_A_LDO_H_MODE_2 (0x111) +#define TOMTOM_A_LDO_H_MODE_2__POR (0xA8) +#define TOMTOM_A_LDO_H_LOOP_CTL (0x112) +#define TOMTOM_A_LDO_H_LOOP_CTL__POR (0x6B) +#define TOMTOM_A_LDO_H_COMP_1 (0x113) +#define TOMTOM_A_LDO_H_COMP_1__POR (0x84) +#define TOMTOM_A_LDO_H_COMP_2 (0x114) +#define TOMTOM_A_LDO_H_COMP_2__POR (0xE0) +#define TOMTOM_A_LDO_H_BIAS_1 (0x115) +#define TOMTOM_A_LDO_H_BIAS_1__POR (0x6D) +#define TOMTOM_A_LDO_H_BIAS_2 (0x116) +#define TOMTOM_A_LDO_H_BIAS_2__POR (0xA5) +#define TOMTOM_A_LDO_H_BIAS_3 (0x117) +#define TOMTOM_A_LDO_H_BIAS_3__POR (0x60) +#define TOMTOM_A_VBAT_CLK (0x118) +#define TOMTOM_A_VBAT_CLK__POR (0x03) +#define TOMTOM_A_VBAT_LOOP (0x119) +#define TOMTOM_A_VBAT_LOOP__POR (0x02) +#define TOMTOM_A_VBAT_REF (0x11A) +#define TOMTOM_A_VBAT_REF__POR (0x20) +#define TOMTOM_A_VBAT_ADC_TEST (0x11B) +#define TOMTOM_A_VBAT_ADC_TEST__POR (0x00) +#define TOMTOM_A_VBAT_FE (0x11C) +#define TOMTOM_A_VBAT_FE__POR (0x48) +#define TOMTOM_A_VBAT_BIAS_1 (0x11D) +#define TOMTOM_A_VBAT_BIAS_1__POR (0x03) +#define TOMTOM_A_VBAT_BIAS_2 (0x11E) +#define TOMTOM_A_VBAT_BIAS_2__POR (0x00) +#define TOMTOM_A_VBAT_ADC_DATA_MSB (0x11F) +#define TOMTOM_A_VBAT_ADC_DATA_MSB__POR (0x00) +#define TOMTOM_A_VBAT_ADC_DATA_LSB (0x120) +#define TOMTOM_A_VBAT_ADC_DATA_LSB__POR (0x00) +#define TOMTOM_A_FLL_NREF (0x121) +#define TOMTOM_A_FLL_NREF__POR (0x12) +#define TOMTOM_A_FLL_KDCO_TUNE (0x122) +#define TOMTOM_A_FLL_KDCO_TUNE__POR (0x05) +#define TOMTOM_A_FLL_LOCK_THRESH (0x123) +#define TOMTOM_A_FLL_LOCK_THRESH__POR (0xC2) +#define TOMTOM_A_FLL_LOCK_DET_COUNT (0x124) +#define TOMTOM_A_FLL_LOCK_DET_COUNT__POR (0x40) +#define TOMTOM_A_FLL_DAC_THRESHOLD (0x125) +#define TOMTOM_A_FLL_DAC_THRESHOLD__POR (0xC8) +#define TOMTOM_A_FLL_TEST_DCO_FREERUN (0x126) +#define TOMTOM_A_FLL_TEST_DCO_FREERUN__POR (0x00) +#define TOMTOM_A_FLL_TEST_ENABLE (0x127) +#define TOMTOM_A_FLL_TEST_ENABLE__POR (0x00) +#define TOMTOM_A_MICB_CFILT_1_CTL (0x128) +#define TOMTOM_A_MICB_CFILT_1_CTL__POR (0x40) +#define TOMTOM_A_MICB_CFILT_1_VAL (0x129) +#define TOMTOM_A_MICB_CFILT_1_VAL__POR (0x80) +#define TOMTOM_A_MICB_CFILT_1_PRECHRG (0x12A) +#define TOMTOM_A_MICB_CFILT_1_PRECHRG__POR (0x38) +#define TOMTOM_A_MICB_1_CTL (0x12B) +#define TOMTOM_A_MICB_1_CTL__POR (0x16) +#define TOMTOM_A_MICB_1_INT_RBIAS (0x12C) +#define TOMTOM_A_MICB_1_INT_RBIAS__POR (0x24) +#define TOMTOM_A_MICB_1_MBHC (0x12D) +#define TOMTOM_A_MICB_1_MBHC__POR (0x01) +#define TOMTOM_A_MICB_CFILT_2_CTL (0x12E) +#define TOMTOM_A_MICB_CFILT_2_CTL__POR (0x41) +#define TOMTOM_A_MICB_CFILT_2_VAL (0x12F) +#define TOMTOM_A_MICB_CFILT_2_VAL__POR (0x80) +#define TOMTOM_A_MICB_CFILT_2_PRECHRG (0x130) +#define TOMTOM_A_MICB_CFILT_2_PRECHRG__POR (0x38) +#define TOMTOM_A_MICB_2_CTL (0x131) +#define TOMTOM_A_MICB_2_CTL__POR (0x16) +#define TOMTOM_A_MICB_2_INT_RBIAS (0x132) +#define TOMTOM_A_MICB_2_INT_RBIAS__POR (0x24) +#define TOMTOM_A_MICB_2_MBHC (0x133) +#define TOMTOM_A_MICB_2_MBHC__POR (0x02) +#define TOMTOM_A_MICB_CFILT_3_CTL (0x134) +#define TOMTOM_A_MICB_CFILT_3_CTL__POR (0x40) +#define TOMTOM_A_MICB_CFILT_3_VAL (0x135) +#define TOMTOM_A_MICB_CFILT_3_VAL__POR (0x80) +#define TOMTOM_A_MICB_CFILT_3_PRECHRG (0x136) +#define TOMTOM_A_MICB_CFILT_3_PRECHRG__POR (0x38) +#define TOMTOM_A_MICB_3_CTL (0x137) +#define TOMTOM_A_MICB_3_CTL__POR (0x16) +#define TOMTOM_A_MICB_3_INT_RBIAS (0x138) +#define TOMTOM_A_MICB_3_INT_RBIAS__POR (0x24) +#define TOMTOM_A_MICB_3_MBHC (0x139) +#define TOMTOM_A_MICB_3_MBHC__POR (0x00) +#define TOMTOM_A_MICB_4_CTL (0x13A) +#define TOMTOM_A_MICB_4_CTL__POR (0x16) +#define TOMTOM_A_MICB_4_INT_RBIAS (0x13B) +#define TOMTOM_A_MICB_4_INT_RBIAS__POR (0x24) +#define TOMTOM_A_MICB_4_MBHC (0x13C) +#define TOMTOM_A_MICB_4_MBHC__POR (0x01) +#define TOMTOM_A_SPKR_DRV2_EN (0x13D) +#define TOMTOM_A_SPKR_DRV2_EN__POR (0x6F) +#define TOMTOM_A_SPKR_DRV2_GAIN (0x13E) +#define TOMTOM_A_SPKR_DRV2_GAIN__POR (0x00) +#define TOMTOM_A_SPKR_DRV2_DAC_CTL (0x13F) +#define TOMTOM_A_SPKR_DRV2_DAC_CTL__POR (0x04) +#define TOMTOM_A_SPKR_DRV2_OCP_CTL (0x140) +#define TOMTOM_A_SPKR_DRV2_OCP_CTL__POR (0x97) +#define TOMTOM_A_SPKR_DRV2_CLIP_DET (0x141) +#define TOMTOM_A_SPKR_DRV2_CLIP_DET__POR (0x01) +#define TOMTOM_A_SPKR_DRV2_DBG_DAC (0x142) +#define TOMTOM_A_SPKR_DRV2_DBG_DAC__POR (0x05) +#define TOMTOM_A_SPKR_DRV2_DBG_PA (0x143) +#define TOMTOM_A_SPKR_DRV2_DBG_PA__POR (0x18) +#define TOMTOM_A_SPKR_DRV2_DBG_PWRSTG (0x144) +#define TOMTOM_A_SPKR_DRV2_DBG_PWRSTG__POR (0x00) +#define TOMTOM_A_SPKR_DRV2_BIAS_LDO (0x145) +#define TOMTOM_A_SPKR_DRV2_BIAS_LDO__POR (0x45) +#define TOMTOM_A_SPKR_DRV2_BIAS_INT (0x146) +#define TOMTOM_A_SPKR_DRV2_BIAS_INT__POR (0xA5) +#define TOMTOM_A_SPKR_DRV2_BIAS_PA (0x147) +#define TOMTOM_A_SPKR_DRV2_BIAS_PA__POR (0x55) +#define TOMTOM_A_SPKR_DRV2_STATUS_OCP (0x148) +#define TOMTOM_A_SPKR_DRV2_STATUS_OCP__POR (0x00) +#define TOMTOM_A_SPKR_DRV2_STATUS_PA (0x149) +#define TOMTOM_A_SPKR_DRV2_STATUS_PA__POR (0x00) +#define TOMTOM_A_MBHC_INSERT_DETECT (0x14A) +#define TOMTOM_A_MBHC_INSERT_DETECT__POR (0x00) +#define TOMTOM_A_MBHC_INSERT_DET_STATUS (0x14B) +#define TOMTOM_A_MBHC_INSERT_DET_STATUS__POR (0x00) +#define TOMTOM_A_TX_COM_BIAS (0x14C) +#define TOMTOM_A_TX_COM_BIAS__POR (0xF0) +#define TOMTOM_A_MBHC_INSERT_DETECT2 (0x14D) +#define TOMTOM_A_MBHC_INSERT_DETECT2__POR (0xD0) +#define TOMTOM_A_MBHC_SCALING_MUX_1 (0x14E) +#define TOMTOM_A_MBHC_SCALING_MUX_1__POR (0x00) +#define TOMTOM_A_MBHC_SCALING_MUX_2 (0x14F) +#define TOMTOM_A_MBHC_SCALING_MUX_2__POR (0x80) +#define TOMTOM_A_MAD_ANA_CTRL (0x150) +#define TOMTOM_A_MAD_ANA_CTRL__POR (0xF1) +#define TOMTOM_A_TX_SUP_SWITCH_CTRL_1 (0x151) +#define TOMTOM_A_TX_SUP_SWITCH_CTRL_1__POR (0x00) +#define TOMTOM_A_TX_SUP_SWITCH_CTRL_2 (0x152) +#define TOMTOM_A_TX_SUP_SWITCH_CTRL_2__POR (0x80) +#define TOMTOM_A_TX_1_GAIN (0x153) +#define TOMTOM_A_TX_1_GAIN__POR (0x02) +#define TOMTOM_A_TX_1_2_TEST_EN (0x154) +#define TOMTOM_A_TX_1_2_TEST_EN__POR (0xCC) +#define TOMTOM_A_TX_2_GAIN (0x155) +#define TOMTOM_A_TX_2_GAIN__POR (0x02) +#define TOMTOM_A_TX_1_2_ADC_IB (0x156) +#define TOMTOM_A_TX_1_2_ADC_IB__POR (0x44) +#define TOMTOM_A_TX_1_2_ATEST_REFCTRL (0x157) +#define TOMTOM_A_TX_1_2_ATEST_REFCTRL__POR (0x00) +#define TOMTOM_A_TX_1_2_TEST_CTL (0x158) +#define TOMTOM_A_TX_1_2_TEST_CTL__POR (0x38) +#define TOMTOM_A_TX_1_2_TEST_BLOCK_EN (0x159) +#define TOMTOM_A_TX_1_2_TEST_BLOCK_EN__POR (0xFC) +#define TOMTOM_A_TX_1_2_TXFE_CLKDIV (0x15A) +#define TOMTOM_A_TX_1_2_TXFE_CLKDIV__POR (0x55) +#define TOMTOM_A_TX_1_2_SAR_ERR_CH1 (0x15B) +#define TOMTOM_A_TX_1_2_SAR_ERR_CH1__POR (0x00) +#define TOMTOM_A_TX_1_2_SAR_ERR_CH2 (0x15C) +#define TOMTOM_A_TX_1_2_SAR_ERR_CH2__POR (0x00) +#define TOMTOM_A_TX_3_GAIN (0x15D) +#define TOMTOM_A_TX_3_GAIN__POR (0x02) +#define TOMTOM_A_TX_3_4_TEST_EN (0x15E) +#define TOMTOM_A_TX_3_4_TEST_EN__POR (0xCC) +#define TOMTOM_A_TX_4_GAIN (0x15F) +#define TOMTOM_A_TX_4_GAIN__POR (0x02) +#define TOMTOM_A_TX_3_4_ADC_IB (0x160) +#define TOMTOM_A_TX_3_4_ADC_IB__POR (0x44) +#define TOMTOM_A_TX_3_4_ATEST_REFCTRL (0x161) +#define TOMTOM_A_TX_3_4_ATEST_REFCTRL__POR (0x00) +#define TOMTOM_A_TX_3_4_TEST_CTL (0x162) +#define TOMTOM_A_TX_3_4_TEST_CTL__POR (0x38) +#define TOMTOM_A_TX_3_4_TEST_BLOCK_EN (0x163) +#define TOMTOM_A_TX_3_4_TEST_BLOCK_EN__POR (0xFC) +#define TOMTOM_A_TX_3_4_TXFE_CKDIV (0x164) +#define TOMTOM_A_TX_3_4_TXFE_CKDIV__POR (0x55) +#define TOMTOM_A_TX_3_4_SAR_ERR_CH3 (0x165) +#define TOMTOM_A_TX_3_4_SAR_ERR_CH3__POR (0x00) +#define TOMTOM_A_TX_3_4_SAR_ERR_CH4 (0x166) +#define TOMTOM_A_TX_3_4_SAR_ERR_CH4__POR (0x00) +#define TOMTOM_A_TX_5_GAIN (0x167) +#define TOMTOM_A_TX_5_GAIN__POR (0x02) +#define TOMTOM_A_TX_5_6_TEST_EN (0x168) +#define TOMTOM_A_TX_5_6_TEST_EN__POR (0xCC) +#define TOMTOM_A_TX_6_GAIN (0x169) +#define TOMTOM_A_TX_6_GAIN__POR (0x02) +#define TOMTOM_A_TX_5_6_ADC_IB (0x16A) +#define TOMTOM_A_TX_5_6_ADC_IB__POR (0x44) +#define TOMTOM_A_TX_5_6_ATEST_REFCTRL (0x16B) +#define TOMTOM_A_TX_5_6_ATEST_REFCTRL__POR (0x00) +#define TOMTOM_A_TX_5_6_TEST_CTL (0x16C) +#define TOMTOM_A_TX_5_6_TEST_CTL__POR (0x38) +#define TOMTOM_A_TX_5_6_TEST_BLOCK_EN (0x16D) +#define TOMTOM_A_TX_5_6_TEST_BLOCK_EN__POR (0xFC) +#define TOMTOM_A_TX_5_6_TXFE_CKDIV (0x16E) +#define TOMTOM_A_TX_5_6_TXFE_CKDIV__POR (0x55) +#define TOMTOM_A_TX_5_6_SAR_ERR_CH5 (0x16F) +#define TOMTOM_A_TX_5_6_SAR_ERR_CH5__POR (0x00) +#define TOMTOM_A_TX_5_6_SAR_ERR_CH6 (0x170) +#define TOMTOM_A_TX_5_6_SAR_ERR_CH6__POR (0x00) +#define TOMTOM_A_TX_7_MBHC_EN (0x171) +#define TOMTOM_A_TX_7_MBHC_EN__POR (0x0C) +#define TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL (0x172) +#define TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL__POR (0x00) +#define TOMTOM_A_TX_7_MBHC_ADC (0x173) +#define TOMTOM_A_TX_7_MBHC_ADC__POR (0x44) +#define TOMTOM_A_TX_7_MBHC_TEST_CTL (0x174) +#define TOMTOM_A_TX_7_MBHC_TEST_CTL__POR (0x38) +#define TOMTOM_A_TX_7_MBHC_SAR_ERR (0x175) +#define TOMTOM_A_TX_7_MBHC_SAR_ERR__POR (0x00) +#define TOMTOM_A_TX_7_TXFE_CLKDIV (0x176) +#define TOMTOM_A_TX_7_TXFE_CLKDIV__POR (0x8B) +#define TOMTOM_A_RCO_CTRL (0x177) +#define TOMTOM_A_RCO_CTRL__POR (0x00) +#define TOMTOM_A_RCO_CALIBRATION_CTRL1 (0x178) +#define TOMTOM_A_RCO_CALIBRATION_CTRL1__POR (0x00) +#define TOMTOM_A_RCO_CALIBRATION_CTRL2 (0x179) +#define TOMTOM_A_RCO_CALIBRATION_CTRL2__POR (0x00) +#define TOMTOM_A_RCO_CALIBRATION_CTRL3 (0x17A) +#define TOMTOM_A_RCO_CALIBRATION_CTRL3__POR (0x00) +#define TOMTOM_A_RCO_TEST_CTRL (0x17B) +#define TOMTOM_A_RCO_TEST_CTRL__POR (0x00) +#define TOMTOM_A_RCO_CALIBRATION_RESULT1 (0x17C) +#define TOMTOM_A_RCO_CALIBRATION_RESULT1__POR (0x00) +#define TOMTOM_A_RCO_CALIBRATION_RESULT2 (0x17D) +#define TOMTOM_A_RCO_CALIBRATION_RESULT2__POR (0x00) +#define TOMTOM_A_BUCK_MODE_1 (0x181) +#define TOMTOM_A_BUCK_MODE_1__POR (0x21) +#define TOMTOM_A_BUCK_MODE_2 (0x182) +#define TOMTOM_A_BUCK_MODE_2__POR (0xFF) +#define TOMTOM_A_BUCK_MODE_3 (0x183) +#define TOMTOM_A_BUCK_MODE_3__POR (0xCE) +#define TOMTOM_A_BUCK_MODE_4 (0x184) +#define TOMTOM_A_BUCK_MODE_4__POR (0x3A) +#define TOMTOM_A_BUCK_MODE_5 (0x185) +#define TOMTOM_A_BUCK_MODE_5__POR (0x00) +#define TOMTOM_A_BUCK_CTRL_VCL_1 (0x186) +#define TOMTOM_A_BUCK_CTRL_VCL_1__POR (0x08) +#define TOMTOM_A_BUCK_CTRL_VCL_2 (0x187) +#define TOMTOM_A_BUCK_CTRL_VCL_2__POR (0xA3) +#define TOMTOM_A_BUCK_CTRL_VCL_3 (0x188) +#define TOMTOM_A_BUCK_CTRL_VCL_3__POR (0x82) +#define TOMTOM_A_BUCK_CTRL_CCL_1 (0x189) +#define TOMTOM_A_BUCK_CTRL_CCL_1__POR (0x5B) +#define TOMTOM_A_BUCK_CTRL_CCL_2 (0x18A) +#define TOMTOM_A_BUCK_CTRL_CCL_2__POR (0xDC) +#define TOMTOM_A_BUCK_CTRL_CCL_3 (0x18B) +#define TOMTOM_A_BUCK_CTRL_CCL_3__POR (0x6A) +#define TOMTOM_A_BUCK_CTRL_CCL_4 (0x18C) +#define TOMTOM_A_BUCK_CTRL_CCL_4__POR (0x51) +#define TOMTOM_A_BUCK_CTRL_PWM_DRVR_1 (0x18D) +#define TOMTOM_A_BUCK_CTRL_PWM_DRVR_1__POR (0x50) +#define TOMTOM_A_BUCK_CTRL_PWM_DRVR_2 (0x18E) +#define TOMTOM_A_BUCK_CTRL_PWM_DRVR_2__POR (0x64) +#define TOMTOM_A_BUCK_CTRL_PWM_DRVR_3 (0x18F) +#define TOMTOM_A_BUCK_CTRL_PWM_DRVR_3__POR (0x77) +#define TOMTOM_A_BUCK_TMUX_A_D (0x190) +#define TOMTOM_A_BUCK_TMUX_A_D__POR (0x00) +#define TOMTOM_A_NCP_BUCKREF (0x191) +#define TOMTOM_A_NCP_BUCKREF__POR (0x00) +#define TOMTOM_A_NCP_EN (0x192) +#define TOMTOM_A_NCP_EN__POR (0xFE) +#define TOMTOM_A_NCP_CLK (0x193) +#define TOMTOM_A_NCP_CLK__POR (0x94) +#define TOMTOM_A_NCP_STATIC (0x194) +#define TOMTOM_A_NCP_STATIC__POR (0x28) +#define TOMTOM_A_NCP_VTH_LOW (0x195) +#define TOMTOM_A_NCP_VTH_LOW__POR (0x88) +#define TOMTOM_A_NCP_VTH_HIGH (0x196) +#define TOMTOM_A_NCP_VTH_HIGH__POR (0xA0) +#define TOMTOM_A_NCP_ATEST (0x197) +#define TOMTOM_A_NCP_ATEST__POR (0x00) +#define TOMTOM_A_NCP_DTEST (0x198) +#define TOMTOM_A_NCP_DTEST__POR (0x10) +#define TOMTOM_A_NCP_DLY1 (0x199) +#define TOMTOM_A_NCP_DLY1__POR (0x06) +#define TOMTOM_A_NCP_DLY2 (0x19A) +#define TOMTOM_A_NCP_DLY2__POR (0x06) +#define TOMTOM_A_RX_AUX_SW_CTL (0x19B) +#define TOMTOM_A_RX_AUX_SW_CTL__POR (0x00) +#define TOMTOM_A_RX_PA_AUX_IN_CONN (0x19C) +#define TOMTOM_A_RX_PA_AUX_IN_CONN__POR (0x00) +#define TOMTOM_A_RX_COM_TIMER_DIV (0x19E) +#define TOMTOM_A_RX_COM_TIMER_DIV__POR (0xE8) +#define TOMTOM_A_RX_COM_OCP_CTL (0x19F) +#define TOMTOM_A_RX_COM_OCP_CTL__POR (0x1F) +#define TOMTOM_A_RX_COM_OCP_COUNT (0x1A0) +#define TOMTOM_A_RX_COM_OCP_COUNT__POR (0x77) +#define TOMTOM_A_RX_COM_DAC_CTL (0x1A1) +#define TOMTOM_A_RX_COM_DAC_CTL__POR (0x00) +#define TOMTOM_A_RX_COM_BIAS (0x1A2) +#define TOMTOM_A_RX_COM_BIAS__POR (0x20) +#define TOMTOM_A_RX_HPH_AUTO_CHOP (0x1A4) +#define TOMTOM_A_RX_HPH_AUTO_CHOP__POR (0x38) +#define TOMTOM_A_RX_HPH_CHOP_CTL (0x1A5) +#define TOMTOM_A_RX_HPH_CHOP_CTL__POR (0xA4) +#define TOMTOM_A_RX_HPH_BIAS_PA (0x1A6) +#define TOMTOM_A_RX_HPH_BIAS_PA__POR (0x7A) +#define TOMTOM_A_RX_HPH_BIAS_LDO (0x1A7) +#define TOMTOM_A_RX_HPH_BIAS_LDO__POR (0x87) +#define TOMTOM_A_RX_HPH_BIAS_CNP (0x1A8) +#define TOMTOM_A_RX_HPH_BIAS_CNP__POR (0x8A) +#define TOMTOM_A_RX_HPH_BIAS_WG_OCP (0x1A9) +#define TOMTOM_A_RX_HPH_BIAS_WG_OCP__POR (0x2A) +#define TOMTOM_A_RX_HPH_OCP_CTL (0x1AA) +#define TOMTOM_A_RX_HPH_OCP_CTL__POR (0x69) +#define TOMTOM_A_RX_HPH_CNP_EN (0x1AB) +#define TOMTOM_A_RX_HPH_CNP_EN__POR (0x80) +#define TOMTOM_A_RX_HPH_CNP_WG_CTL (0x1AC) +#define TOMTOM_A_RX_HPH_CNP_WG_CTL__POR (0xDA) +#define TOMTOM_A_RX_HPH_CNP_WG_TIME (0x1AD) +#define TOMTOM_A_RX_HPH_CNP_WG_TIME__POR (0x15) +#define TOMTOM_A_RX_HPH_L_GAIN (0x1AE) +#define TOMTOM_A_RX_HPH_L_GAIN__POR (0xC0) +#define TOMTOM_A_RX_HPH_L_TEST (0x1AF) +#define TOMTOM_A_RX_HPH_L_TEST__POR (0x02) +#define TOMTOM_A_RX_HPH_L_PA_CTL (0x1B0) +#define TOMTOM_A_RX_HPH_L_PA_CTL__POR (0x42) +#define TOMTOM_A_RX_HPH_L_DAC_CTL (0x1B1) +#define TOMTOM_A_RX_HPH_L_DAC_CTL__POR (0x00) +#define TOMTOM_A_RX_HPH_L_ATEST (0x1B2) +#define TOMTOM_A_RX_HPH_L_ATEST__POR (0x00) +#define TOMTOM_A_RX_HPH_L_STATUS (0x1B3) +#define TOMTOM_A_RX_HPH_L_STATUS__POR (0x00) +#define TOMTOM_A_RX_HPH_R_GAIN (0x1B4) +#define TOMTOM_A_RX_HPH_R_GAIN__POR (0x00) +#define TOMTOM_A_RX_HPH_R_TEST (0x1B5) +#define TOMTOM_A_RX_HPH_R_TEST__POR (0x02) +#define TOMTOM_A_RX_HPH_R_PA_CTL (0x1B6) +#define TOMTOM_A_RX_HPH_R_PA_CTL__POR (0x42) +#define TOMTOM_A_RX_HPH_R_DAC_CTL (0x1B7) +#define TOMTOM_A_RX_HPH_R_DAC_CTL__POR (0x00) +#define TOMTOM_A_RX_HPH_R_ATEST (0x1B8) +#define TOMTOM_A_RX_HPH_R_ATEST__POR (0x00) +#define TOMTOM_A_RX_HPH_R_STATUS (0x1B9) +#define TOMTOM_A_RX_HPH_R_STATUS__POR (0x00) +#define TOMTOM_A_RX_EAR_BIAS_PA (0x1BA) +#define TOMTOM_A_RX_EAR_BIAS_PA__POR (0x76) +#define TOMTOM_A_RX_EAR_BIAS_CMBUFF (0x1BB) +#define TOMTOM_A_RX_EAR_BIAS_CMBUFF__POR (0xA0) +#define TOMTOM_A_RX_EAR_EN (0x1BC) +#define TOMTOM_A_RX_EAR_EN__POR (0x00) +#define TOMTOM_A_RX_EAR_GAIN (0x1BD) +#define TOMTOM_A_RX_EAR_GAIN__POR (0x02) +#define TOMTOM_A_RX_EAR_CMBUFF (0x1BE) +#define TOMTOM_A_RX_EAR_CMBUFF__POR (0x05) +#define TOMTOM_A_RX_EAR_ICTL (0x1BF) +#define TOMTOM_A_RX_EAR_ICTL__POR (0x40) +#define TOMTOM_A_RX_EAR_CCOMP (0x1C0) +#define TOMTOM_A_RX_EAR_CCOMP__POR (0x08) +#define TOMTOM_A_RX_EAR_VCM (0x1C1) +#define TOMTOM_A_RX_EAR_VCM__POR (0x03) +#define TOMTOM_A_RX_EAR_CNP (0x1C2) +#define TOMTOM_A_RX_EAR_CNP__POR (0xC0) +#define TOMTOM_A_RX_EAR_DAC_CTL_ATEST (0x1C3) +#define TOMTOM_A_RX_EAR_DAC_CTL_ATEST__POR (0x00) +#define TOMTOM_A_RX_EAR_STATUS (0x1C5) +#define TOMTOM_A_RX_EAR_STATUS__POR (0x04) +#define TOMTOM_A_RX_LINE_BIAS_PA (0x1C6) +#define TOMTOM_A_RX_LINE_BIAS_PA__POR (0x78) +#define TOMTOM_A_RX_BUCK_BIAS1 (0x1C7) +#define TOMTOM_A_RX_BUCK_BIAS1__POR (0x42) +#define TOMTOM_A_RX_BUCK_BIAS2 (0x1C8) +#define TOMTOM_A_RX_BUCK_BIAS2__POR (0x84) +#define TOMTOM_A_RX_LINE_COM (0x1C9) +#define TOMTOM_A_RX_LINE_COM__POR (0x80) +#define TOMTOM_A_RX_LINE_CNP_EN (0x1CA) +#define TOMTOM_A_RX_LINE_CNP_EN__POR (0x00) +#define TOMTOM_A_RX_LINE_CNP_WG_CTL (0x1CB) +#define TOMTOM_A_RX_LINE_CNP_WG_CTL__POR (0x00) +#define TOMTOM_A_RX_LINE_CNP_WG_TIME (0x1CC) +#define TOMTOM_A_RX_LINE_CNP_WG_TIME__POR (0x04) +#define TOMTOM_A_RX_LINE_1_GAIN (0x1CD) +#define TOMTOM_A_RX_LINE_1_GAIN__POR (0x00) +#define TOMTOM_A_RX_LINE_1_TEST (0x1CE) +#define TOMTOM_A_RX_LINE_1_TEST__POR (0x02) +#define TOMTOM_A_RX_LINE_1_DAC_CTL (0x1CF) +#define TOMTOM_A_RX_LINE_1_DAC_CTL__POR (0x00) +#define TOMTOM_A_RX_LINE_1_STATUS (0x1D0) +#define TOMTOM_A_RX_LINE_1_STATUS__POR (0x00) +#define TOMTOM_A_RX_LINE_2_GAIN (0x1D1) +#define TOMTOM_A_RX_LINE_2_GAIN__POR (0x00) +#define TOMTOM_A_RX_LINE_2_TEST (0x1D2) +#define TOMTOM_A_RX_LINE_2_TEST__POR (0x02) +#define TOMTOM_A_RX_LINE_2_DAC_CTL (0x1D3) +#define TOMTOM_A_RX_LINE_2_DAC_CTL__POR (0x00) +#define TOMTOM_A_RX_LINE_2_STATUS (0x1D4) +#define TOMTOM_A_RX_LINE_2_STATUS__POR (0x00) +#define TOMTOM_A_RX_LINE_3_GAIN (0x1D5) +#define TOMTOM_A_RX_LINE_3_GAIN__POR (0x00) +#define TOMTOM_A_RX_LINE_3_TEST (0x1D6) +#define TOMTOM_A_RX_LINE_3_TEST__POR (0x02) +#define TOMTOM_A_RX_LINE_3_DAC_CTL (0x1D7) +#define TOMTOM_A_RX_LINE_3_DAC_CTL__POR (0x00) +#define TOMTOM_A_RX_LINE_3_STATUS (0x1D8) +#define TOMTOM_A_RX_LINE_3_STATUS__POR (0x00) +#define TOMTOM_A_RX_LINE_4_GAIN (0x1D9) +#define TOMTOM_A_RX_LINE_4_GAIN__POR (0x00) +#define TOMTOM_A_RX_LINE_4_TEST (0x1DA) +#define TOMTOM_A_RX_LINE_4_TEST__POR (0x02) +#define TOMTOM_A_RX_LINE_4_DAC_CTL (0x1DB) +#define TOMTOM_A_RX_LINE_4_DAC_CTL__POR (0x00) +#define TOMTOM_A_RX_LINE_4_STATUS (0x1DC) +#define TOMTOM_A_RX_LINE_4_STATUS__POR (0x00) +#define TOMTOM_A_RX_LINE_CNP_DBG (0x1DD) +#define TOMTOM_A_RX_LINE_CNP_DBG__POR (0x00) +#define TOMTOM_A_SPKR_DRV1_EN (0x1DF) +#define TOMTOM_A_SPKR_DRV1_EN__POR (0x6F) +#define TOMTOM_A_SPKR_DRV1_GAIN (0x1E0) +#define TOMTOM_A_SPKR_DRV1_GAIN__POR (0x00) +#define TOMTOM_A_SPKR_DRV1_DAC_CTL (0x1E1) +#define TOMTOM_A_SPKR_DRV1_DAC_CTL__POR (0x04) +#define TOMTOM_A_SPKR_DRV1_OCP_CTL (0x1E2) +#define TOMTOM_A_SPKR_DRV1_OCP_CTL__POR (0x97) +#define TOMTOM_A_SPKR_DRV1_CLIP_DET (0x1E3) +#define TOMTOM_A_SPKR_DRV1_CLIP_DET__POR (0x01) +#define TOMTOM_A_SPKR_DRV1_IEC (0x1E4) +#define TOMTOM_A_SPKR_DRV1_IEC__POR (0x00) +#define TOMTOM_A_SPKR_DRV1_DBG_DAC (0x1E5) +#define TOMTOM_A_SPKR_DRV1_DBG_DAC__POR (0x05) +#define TOMTOM_A_SPKR_DRV1_DBG_PA (0x1E6) +#define TOMTOM_A_SPKR_DRV1_DBG_PA__POR (0x18) +#define TOMTOM_A_SPKR_DRV1_DBG_PWRSTG (0x1E7) +#define TOMTOM_A_SPKR_DRV1_DBG_PWRSTG__POR (0x00) +#define TOMTOM_A_SPKR_DRV1_BIAS_LDO (0x1E8) +#define TOMTOM_A_SPKR_DRV1_BIAS_LDO__POR (0x45) +#define TOMTOM_A_SPKR_DRV1_BIAS_INT (0x1E9) +#define TOMTOM_A_SPKR_DRV1_BIAS_INT__POR (0xA5) +#define TOMTOM_A_SPKR_DRV1_BIAS_PA (0x1EA) +#define TOMTOM_A_SPKR_DRV1_BIAS_PA__POR (0x55) +#define TOMTOM_A_SPKR_DRV1_STATUS_OCP (0x1EB) +#define TOMTOM_A_SPKR_DRV1_STATUS_OCP__POR (0x00) +#define TOMTOM_A_SPKR_DRV1_STATUS_PA (0x1EC) +#define TOMTOM_A_SPKR_DRV1_STATUS_PA__POR (0x00) +#define TOMTOM_A_SPKR1_PROT_EN (0x1ED) +#define TOMTOM_A_SPKR1_PROT_EN__POR (0x00) +#define TOMTOM_A_SPKR1_PROT_ADC_TEST_EN (0x1EE) +#define TOMTOM_A_SPKR1_PROT_ADC_TEST_EN__POR (0x44) +#define TOMTOM_A_SPKR1_PROT_ATEST (0x1EF) +#define TOMTOM_A_SPKR1_PROT_ATEST__POR (0x00) +#define TOMTOM_A_SPKR1_PROT_LDO_CTRL (0x1F0) +#define TOMTOM_A_SPKR1_PROT_LDO_CTRL__POR (0x00) +#define TOMTOM_A_SPKR1_PROT_ISENSE_CTRL (0x1F1) +#define TOMTOM_A_SPKR1_PROT_ISENSE_CTRL__POR (0x00) +#define TOMTOM_A_SPKR1_PROT_VSENSE_CTRL (0x1F2) +#define TOMTOM_A_SPKR1_PROT_VSENSE_CTRL__POR (0x00) +#define TOMTOM_A_SPKR2_PROT_EN (0x1F3) +#define TOMTOM_A_SPKR2_PROT_EN__POR (0x00) +#define TOMTOM_A_SPKR2_PROT_ADC_TEST_EN (0x1F4) +#define TOMTOM_A_SPKR2_PROT_ADC_TEST_EN__POR (0x44) +#define TOMTOM_A_SPKR2_PROT_ATEST (0x1F5) +#define TOMTOM_A_SPKR2_PROT_ATEST__POR (0x00) +#define TOMTOM_A_SPKR2_PROT_LDO_CTRL (0x1F6) +#define TOMTOM_A_SPKR2_PROT_LDO_CTRL__POR (0x00) +#define TOMTOM_A_SPKR2_PROT_ISENSE_CTRL (0x1F7) +#define TOMTOM_A_SPKR2_PROT_ISENSE_CTRL__POR (0x00) +#define TOMTOM_A_SPKR2_PROT_VSENSE_CTRL (0x1F8) +#define TOMTOM_A_SPKR2_PROT_VSENSE_CTRL__POR (0x00) +#define TOMTOM_A_MBHC_HPH (0x1FE) +#define TOMTOM_A_MBHC_HPH__POR (0x44) +#define TOMTOM_A_CDC_ANC1_B1_CTL (0x200) +#define TOMTOM_A_CDC_ANC1_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC2_B1_CTL (0x280) +#define TOMTOM_A_CDC_ANC2_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC1_SHIFT (0x201) +#define TOMTOM_A_CDC_ANC1_SHIFT__POR (0x00) +#define TOMTOM_A_CDC_ANC2_SHIFT (0x281) +#define TOMTOM_A_CDC_ANC2_SHIFT__POR (0x00) +#define TOMTOM_A_CDC_ANC1_IIR_B1_CTL (0x202) +#define TOMTOM_A_CDC_ANC1_IIR_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC2_IIR_B1_CTL (0x282) +#define TOMTOM_A_CDC_ANC2_IIR_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC1_IIR_B2_CTL (0x203) +#define TOMTOM_A_CDC_ANC1_IIR_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC2_IIR_B2_CTL (0x283) +#define TOMTOM_A_CDC_ANC2_IIR_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC1_IIR_B3_CTL (0x204) +#define TOMTOM_A_CDC_ANC1_IIR_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC2_IIR_B3_CTL (0x284) +#define TOMTOM_A_CDC_ANC2_IIR_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC1_LPF_B1_CTL (0x206) +#define TOMTOM_A_CDC_ANC1_LPF_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC2_LPF_B1_CTL (0x286) +#define TOMTOM_A_CDC_ANC2_LPF_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC1_LPF_B2_CTL (0x207) +#define TOMTOM_A_CDC_ANC1_LPF_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC2_LPF_B2_CTL (0x287) +#define TOMTOM_A_CDC_ANC2_LPF_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC1_SPARE (0x209) +#define TOMTOM_A_CDC_ANC1_SPARE__POR (0x00) +#define TOMTOM_A_CDC_ANC2_SPARE (0x289) +#define TOMTOM_A_CDC_ANC2_SPARE__POR (0x00) +#define TOMTOM_A_CDC_ANC1_SMLPF_CTL (0x20A) +#define TOMTOM_A_CDC_ANC1_SMLPF_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC2_SMLPF_CTL (0x28A) +#define TOMTOM_A_CDC_ANC2_SMLPF_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC1_DCFLT_CTL (0x20B) +#define TOMTOM_A_CDC_ANC1_DCFLT_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC2_DCFLT_CTL (0x28B) +#define TOMTOM_A_CDC_ANC2_DCFLT_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC1_GAIN_CTL (0x20C) +#define TOMTOM_A_CDC_ANC1_GAIN_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC2_GAIN_CTL (0x28C) +#define TOMTOM_A_CDC_ANC2_GAIN_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC1_B2_CTL (0x20D) +#define TOMTOM_A_CDC_ANC1_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_ANC2_B2_CTL (0x28D) +#define TOMTOM_A_CDC_ANC2_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_TX1_VOL_CTL_TIMER (0x220) +#define TOMTOM_A_CDC_TX1_VOL_CTL_TIMER__POR (0x00) +#define TOMTOM_A_CDC_TX2_VOL_CTL_TIMER (0x228) +#define TOMTOM_A_CDC_TX2_VOL_CTL_TIMER__POR (0x00) +#define TOMTOM_A_CDC_TX3_VOL_CTL_TIMER (0x230) +#define TOMTOM_A_CDC_TX3_VOL_CTL_TIMER__POR (0x00) +#define TOMTOM_A_CDC_TX4_VOL_CTL_TIMER (0x238) +#define TOMTOM_A_CDC_TX4_VOL_CTL_TIMER__POR (0x00) +#define TOMTOM_A_CDC_TX5_VOL_CTL_TIMER (0x240) +#define TOMTOM_A_CDC_TX5_VOL_CTL_TIMER__POR (0x00) +#define TOMTOM_A_CDC_TX6_VOL_CTL_TIMER (0x248) +#define TOMTOM_A_CDC_TX6_VOL_CTL_TIMER__POR (0x00) +#define TOMTOM_A_CDC_TX7_VOL_CTL_TIMER (0x250) +#define TOMTOM_A_CDC_TX7_VOL_CTL_TIMER__POR (0x00) +#define TOMTOM_A_CDC_TX8_VOL_CTL_TIMER (0x258) +#define TOMTOM_A_CDC_TX8_VOL_CTL_TIMER__POR (0x00) +#define TOMTOM_A_CDC_TX9_VOL_CTL_TIMER (0x260) +#define TOMTOM_A_CDC_TX9_VOL_CTL_TIMER__POR (0x00) +#define TOMTOM_A_CDC_TX10_VOL_CTL_TIMER (0x268) +#define TOMTOM_A_CDC_TX10_VOL_CTL_TIMER__POR (0x00) +#define TOMTOM_A_CDC_TX1_VOL_CTL_GAIN (0x221) +#define TOMTOM_A_CDC_TX1_VOL_CTL_GAIN__POR (0x00) +#define TOMTOM_A_CDC_TX2_VOL_CTL_GAIN (0x229) +#define TOMTOM_A_CDC_TX2_VOL_CTL_GAIN__POR (0x00) +#define TOMTOM_A_CDC_TX3_VOL_CTL_GAIN (0x231) +#define TOMTOM_A_CDC_TX3_VOL_CTL_GAIN__POR (0x00) +#define TOMTOM_A_CDC_TX4_VOL_CTL_GAIN (0x239) +#define TOMTOM_A_CDC_TX4_VOL_CTL_GAIN__POR (0x00) +#define TOMTOM_A_CDC_TX5_VOL_CTL_GAIN (0x241) +#define TOMTOM_A_CDC_TX5_VOL_CTL_GAIN__POR (0x00) +#define TOMTOM_A_CDC_TX6_VOL_CTL_GAIN (0x249) +#define TOMTOM_A_CDC_TX6_VOL_CTL_GAIN__POR (0x00) +#define TOMTOM_A_CDC_TX7_VOL_CTL_GAIN (0x251) +#define TOMTOM_A_CDC_TX7_VOL_CTL_GAIN__POR (0x00) +#define TOMTOM_A_CDC_TX8_VOL_CTL_GAIN (0x259) +#define TOMTOM_A_CDC_TX8_VOL_CTL_GAIN__POR (0x00) +#define TOMTOM_A_CDC_TX9_VOL_CTL_GAIN (0x261) +#define TOMTOM_A_CDC_TX9_VOL_CTL_GAIN__POR (0x00) +#define TOMTOM_A_CDC_TX10_VOL_CTL_GAIN (0x269) +#define TOMTOM_A_CDC_TX10_VOL_CTL_GAIN__POR (0x00) +#define TOMTOM_A_CDC_TX1_VOL_CTL_CFG (0x222) +#define TOMTOM_A_CDC_TX1_VOL_CTL_CFG__POR (0x00) +#define TOMTOM_A_CDC_TX2_VOL_CTL_CFG (0x22A) +#define TOMTOM_A_CDC_TX2_VOL_CTL_CFG__POR (0x00) +#define TOMTOM_A_CDC_TX3_VOL_CTL_CFG (0x232) +#define TOMTOM_A_CDC_TX3_VOL_CTL_CFG__POR (0x00) +#define TOMTOM_A_CDC_TX4_VOL_CTL_CFG (0x23A) +#define TOMTOM_A_CDC_TX4_VOL_CTL_CFG__POR (0x00) +#define TOMTOM_A_CDC_TX5_VOL_CTL_CFG (0x242) +#define TOMTOM_A_CDC_TX5_VOL_CTL_CFG__POR (0x00) +#define TOMTOM_A_CDC_TX6_VOL_CTL_CFG (0x24A) +#define TOMTOM_A_CDC_TX6_VOL_CTL_CFG__POR (0x00) +#define TOMTOM_A_CDC_TX7_VOL_CTL_CFG (0x252) +#define TOMTOM_A_CDC_TX7_VOL_CTL_CFG__POR (0x00) +#define TOMTOM_A_CDC_TX8_VOL_CTL_CFG (0x25A) +#define TOMTOM_A_CDC_TX8_VOL_CTL_CFG__POR (0x00) +#define TOMTOM_A_CDC_TX9_VOL_CTL_CFG (0x262) +#define TOMTOM_A_CDC_TX9_VOL_CTL_CFG__POR (0x00) +#define TOMTOM_A_CDC_TX10_VOL_CTL_CFG (0x26A) +#define TOMTOM_A_CDC_TX10_VOL_CTL_CFG__POR (0x00) +#define TOMTOM_A_CDC_TX1_MUX_CTL (0x223) +#define TOMTOM_A_CDC_TX1_MUX_CTL__POR (0x48) +#define TOMTOM_A_CDC_TX2_MUX_CTL (0x22B) +#define TOMTOM_A_CDC_TX2_MUX_CTL__POR (0x48) +#define TOMTOM_A_CDC_TX3_MUX_CTL (0x233) +#define TOMTOM_A_CDC_TX3_MUX_CTL__POR (0x48) +#define TOMTOM_A_CDC_TX4_MUX_CTL (0x23B) +#define TOMTOM_A_CDC_TX4_MUX_CTL__POR (0x48) +#define TOMTOM_A_CDC_TX5_MUX_CTL (0x243) +#define TOMTOM_A_CDC_TX5_MUX_CTL__POR (0x48) +#define TOMTOM_A_CDC_TX6_MUX_CTL (0x24B) +#define TOMTOM_A_CDC_TX6_MUX_CTL__POR (0x48) +#define TOMTOM_A_CDC_TX7_MUX_CTL (0x253) +#define TOMTOM_A_CDC_TX7_MUX_CTL__POR (0x48) +#define TOMTOM_A_CDC_TX8_MUX_CTL (0x25B) +#define TOMTOM_A_CDC_TX8_MUX_CTL__POR (0x48) +#define TOMTOM_A_CDC_TX9_MUX_CTL (0x263) +#define TOMTOM_A_CDC_TX9_MUX_CTL__POR (0x48) +#define TOMTOM_A_CDC_TX10_MUX_CTL (0x26B) +#define TOMTOM_A_CDC_TX10_MUX_CTL__POR (0x48) +#define TOMTOM_A_CDC_TX1_CLK_FS_CTL (0x224) +#define TOMTOM_A_CDC_TX1_CLK_FS_CTL__POR (0x03) +#define TOMTOM_A_CDC_TX2_CLK_FS_CTL (0x22C) +#define TOMTOM_A_CDC_TX2_CLK_FS_CTL__POR (0x03) +#define TOMTOM_A_CDC_TX3_CLK_FS_CTL (0x234) +#define TOMTOM_A_CDC_TX3_CLK_FS_CTL__POR (0x03) +#define TOMTOM_A_CDC_TX4_CLK_FS_CTL (0x23C) +#define TOMTOM_A_CDC_TX4_CLK_FS_CTL__POR (0x03) +#define TOMTOM_A_CDC_TX5_CLK_FS_CTL (0x244) +#define TOMTOM_A_CDC_TX5_CLK_FS_CTL__POR (0x03) +#define TOMTOM_A_CDC_TX6_CLK_FS_CTL (0x24C) +#define TOMTOM_A_CDC_TX6_CLK_FS_CTL__POR (0x03) +#define TOMTOM_A_CDC_TX7_CLK_FS_CTL (0x254) +#define TOMTOM_A_CDC_TX7_CLK_FS_CTL__POR (0x03) +#define TOMTOM_A_CDC_TX8_CLK_FS_CTL (0x25C) +#define TOMTOM_A_CDC_TX8_CLK_FS_CTL__POR (0x03) +#define TOMTOM_A_CDC_TX9_CLK_FS_CTL (0x264) +#define TOMTOM_A_CDC_TX9_CLK_FS_CTL__POR (0x03) +#define TOMTOM_A_CDC_TX10_CLK_FS_CTL (0x26C) +#define TOMTOM_A_CDC_TX10_CLK_FS_CTL__POR (0x03) +#define TOMTOM_A_CDC_TX1_DMIC_CTL (0x225) +#define TOMTOM_A_CDC_TX1_DMIC_CTL__POR (0x00) +#define TOMTOM_A_CDC_TX2_DMIC_CTL (0x22D) +#define TOMTOM_A_CDC_TX2_DMIC_CTL__POR (0x00) +#define TOMTOM_A_CDC_TX3_DMIC_CTL (0x235) +#define TOMTOM_A_CDC_TX3_DMIC_CTL__POR (0x00) +#define TOMTOM_A_CDC_TX4_DMIC_CTL (0x23D) +#define TOMTOM_A_CDC_TX4_DMIC_CTL__POR (0x00) +#define TOMTOM_A_CDC_TX5_DMIC_CTL (0x245) +#define TOMTOM_A_CDC_TX5_DMIC_CTL__POR (0x00) +#define TOMTOM_A_CDC_TX6_DMIC_CTL (0x24D) +#define TOMTOM_A_CDC_TX6_DMIC_CTL__POR (0x00) +#define TOMTOM_A_CDC_TX7_DMIC_CTL (0x255) +#define TOMTOM_A_CDC_TX7_DMIC_CTL__POR (0x00) +#define TOMTOM_A_CDC_TX8_DMIC_CTL (0x25D) +#define TOMTOM_A_CDC_TX8_DMIC_CTL__POR (0x00) +#define TOMTOM_A_CDC_TX9_DMIC_CTL (0x265) +#define TOMTOM_A_CDC_TX9_DMIC_CTL__POR (0x00) +#define TOMTOM_A_CDC_TX10_DMIC_CTL (0x26D) +#define TOMTOM_A_CDC_TX10_DMIC_CTL__POR (0x00) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL0 (0x270) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL0__POR (0x00) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL1 (0x271) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL1__POR (0x00) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL2 (0x272) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL2__POR (0x00) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL3 (0x273) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL3__POR (0x00) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL4 (0x274) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL4__POR (0x00) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL5 (0x275) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL5__POR (0x00) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL6 (0x276) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL6__POR (0x00) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL7 (0x277) +#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL7__POR (0x00) +#define TOMTOM_A_CDC_DEBUG_B1_CTL (0x278) +#define TOMTOM_A_CDC_DEBUG_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_DEBUG_B2_CTL (0x279) +#define TOMTOM_A_CDC_DEBUG_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_DEBUG_B3_CTL (0x27A) +#define TOMTOM_A_CDC_DEBUG_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_DEBUG_B4_CTL (0x27B) +#define TOMTOM_A_CDC_DEBUG_B4_CTL__POR (0x00) +#define TOMTOM_A_CDC_DEBUG_B5_CTL (0x27C) +#define TOMTOM_A_CDC_DEBUG_B5_CTL__POR (0x00) +#define TOMTOM_A_CDC_DEBUG_B6_CTL (0x27D) +#define TOMTOM_A_CDC_DEBUG_B6_CTL__POR (0x00) +#define TOMTOM_A_CDC_DEBUG_B7_CTL (0x27E) +#define TOMTOM_A_CDC_DEBUG_B7_CTL__POR (0x00) +#define TOMTOM_A_CDC_SRC1_PDA_CFG (0x2A0) +#define TOMTOM_A_CDC_SRC1_PDA_CFG__POR (0x00) +#define TOMTOM_A_CDC_SRC2_PDA_CFG (0x2A8) +#define TOMTOM_A_CDC_SRC2_PDA_CFG__POR (0x00) +#define TOMTOM_A_CDC_SRC1_FS_CTL (0x2A1) +#define TOMTOM_A_CDC_SRC1_FS_CTL__POR (0x1B) +#define TOMTOM_A_CDC_SRC2_FS_CTL (0x2A9) +#define TOMTOM_A_CDC_SRC2_FS_CTL__POR (0x1B) +#define TOMTOM_A_CDC_RX1_B1_CTL (0x2B0) +#define TOMTOM_A_CDC_RX1_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX2_B1_CTL (0x2B8) +#define TOMTOM_A_CDC_RX2_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX3_B1_CTL (0x2C0) +#define TOMTOM_A_CDC_RX3_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX4_B1_CTL (0x2C8) +#define TOMTOM_A_CDC_RX4_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX5_B1_CTL (0x2D0) +#define TOMTOM_A_CDC_RX5_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX6_B1_CTL (0x2D8) +#define TOMTOM_A_CDC_RX6_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX7_B1_CTL (0x2E0) +#define TOMTOM_A_CDC_RX7_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX1_B2_CTL (0x2B1) +#define TOMTOM_A_CDC_RX1_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX2_B2_CTL (0x2B9) +#define TOMTOM_A_CDC_RX2_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX3_B2_CTL (0x2C1) +#define TOMTOM_A_CDC_RX3_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX4_B2_CTL (0x2C9) +#define TOMTOM_A_CDC_RX4_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX5_B2_CTL (0x2D1) +#define TOMTOM_A_CDC_RX5_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX6_B2_CTL (0x2D9) +#define TOMTOM_A_CDC_RX6_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX7_B2_CTL (0x2E1) +#define TOMTOM_A_CDC_RX7_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX1_B3_CTL (0x2B2) +#define TOMTOM_A_CDC_RX1_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX2_B3_CTL (0x2BA) +#define TOMTOM_A_CDC_RX2_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX3_B3_CTL (0x2C2) +#define TOMTOM_A_CDC_RX3_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX4_B3_CTL (0x2CA) +#define TOMTOM_A_CDC_RX4_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX5_B3_CTL (0x2D2) +#define TOMTOM_A_CDC_RX5_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX6_B3_CTL (0x2DA) +#define TOMTOM_A_CDC_RX6_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX7_B3_CTL (0x2E2) +#define TOMTOM_A_CDC_RX7_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX1_B4_CTL (0x2B3) +#define TOMTOM_A_CDC_RX1_B4_CTL__POR (0x0B) +#define TOMTOM_A_CDC_RX2_B4_CTL (0x2BB) +#define TOMTOM_A_CDC_RX2_B4_CTL__POR (0x0B) +#define TOMTOM_A_CDC_RX3_B4_CTL (0x2C3) +#define TOMTOM_A_CDC_RX3_B4_CTL__POR (0x0B) +#define TOMTOM_A_CDC_RX4_B4_CTL (0x2CB) +#define TOMTOM_A_CDC_RX4_B4_CTL__POR (0x0B) +#define TOMTOM_A_CDC_RX5_B4_CTL (0x2D3) +#define TOMTOM_A_CDC_RX5_B4_CTL__POR (0x0B) +#define TOMTOM_A_CDC_RX6_B4_CTL (0x2DB) +#define TOMTOM_A_CDC_RX6_B4_CTL__POR (0x0B) +#define TOMTOM_A_CDC_RX7_B4_CTL (0x2E3) +#define TOMTOM_A_CDC_RX7_B4_CTL__POR (0x0B) +#define TOMTOM_A_CDC_RX1_B5_CTL (0x2B4) +#define TOMTOM_A_CDC_RX1_B5_CTL__POR (0x78) +#define TOMTOM_A_CDC_RX2_B5_CTL (0x2BC) +#define TOMTOM_A_CDC_RX2_B5_CTL__POR (0x78) +#define TOMTOM_A_CDC_RX3_B5_CTL (0x2C4) +#define TOMTOM_A_CDC_RX3_B5_CTL__POR (0x78) +#define TOMTOM_A_CDC_RX4_B5_CTL (0x2CC) +#define TOMTOM_A_CDC_RX4_B5_CTL__POR (0x78) +#define TOMTOM_A_CDC_RX5_B5_CTL (0x2D4) +#define TOMTOM_A_CDC_RX5_B5_CTL__POR (0x78) +#define TOMTOM_A_CDC_RX6_B5_CTL (0x2DC) +#define TOMTOM_A_CDC_RX6_B5_CTL__POR (0x78) +#define TOMTOM_A_CDC_RX7_B5_CTL (0x2E4) +#define TOMTOM_A_CDC_RX7_B5_CTL__POR (0x78) +#define TOMTOM_A_CDC_RX1_B6_CTL (0x2B5) +#define TOMTOM_A_CDC_RX1_B6_CTL__POR (0x80) +#define TOMTOM_A_CDC_RX2_B6_CTL (0x2BD) +#define TOMTOM_A_CDC_RX2_B6_CTL__POR (0x80) +#define TOMTOM_A_CDC_RX3_B6_CTL (0x2C5) +#define TOMTOM_A_CDC_RX3_B6_CTL__POR (0x80) +#define TOMTOM_A_CDC_RX4_B6_CTL (0x2CD) +#define TOMTOM_A_CDC_RX4_B6_CTL__POR (0x80) +#define TOMTOM_A_CDC_RX5_B6_CTL (0x2D5) +#define TOMTOM_A_CDC_RX5_B6_CTL__POR (0x80) +#define TOMTOM_A_CDC_RX6_B6_CTL (0x2DD) +#define TOMTOM_A_CDC_RX6_B6_CTL__POR (0x80) +#define TOMTOM_A_CDC_RX7_B6_CTL (0x2E5) +#define TOMTOM_A_CDC_RX7_B6_CTL__POR (0x80) +#define TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL (0x2B6) +#define TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL (0x2BE) +#define TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL (0x2C6) +#define TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL (0x2CE) +#define TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL (0x2D6) +#define TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL (0x2DE) +#define TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL (0x2E6) +#define TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL (0x2B7) +#define TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL (0x2BF) +#define TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL (0x2C7) +#define TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL (0x2CF) +#define TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL (0x2D7) +#define TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL (0x2DF) +#define TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL (0x2E7) +#define TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_VBAT_CFG (0x2E8) +#define TOMTOM_A_CDC_VBAT_CFG__POR (0x1A) +#define TOMTOM_A_CDC_VBAT_ADC_CAL1 (0x2E9) +#define TOMTOM_A_CDC_VBAT_ADC_CAL1__POR (0x00) +#define TOMTOM_A_CDC_VBAT_ADC_CAL2 (0x2EA) +#define TOMTOM_A_CDC_VBAT_ADC_CAL2__POR (0x00) +#define TOMTOM_A_CDC_VBAT_ADC_CAL3 (0x2EB) +#define TOMTOM_A_CDC_VBAT_ADC_CAL3__POR (0x04) +#define TOMTOM_A_CDC_VBAT_PK_EST1 (0x2EC) +#define TOMTOM_A_CDC_VBAT_PK_EST1__POR (0xE0) +#define TOMTOM_A_CDC_VBAT_PK_EST2 (0x2ED) +#define TOMTOM_A_CDC_VBAT_PK_EST2__POR (0x01) +#define TOMTOM_A_CDC_VBAT_PK_EST3 (0x2EE) +#define TOMTOM_A_CDC_VBAT_PK_EST3__POR (0x40) +#define TOMTOM_A_CDC_VBAT_RF_PROC1 (0x2EF) +#define TOMTOM_A_CDC_VBAT_RF_PROC1__POR (0x2A) +#define TOMTOM_A_CDC_VBAT_RF_PROC2 (0x2F0) +#define TOMTOM_A_CDC_VBAT_RF_PROC2__POR (0x86) +#define TOMTOM_A_CDC_VBAT_TAC1 (0x2F1) +#define TOMTOM_A_CDC_VBAT_TAC1__POR (0x70) +#define TOMTOM_A_CDC_VBAT_TAC2 (0x2F2) +#define TOMTOM_A_CDC_VBAT_TAC2__POR (0x18) +#define TOMTOM_A_CDC_VBAT_TAC3 (0x2F3) +#define TOMTOM_A_CDC_VBAT_TAC3__POR (0x18) +#define TOMTOM_A_CDC_VBAT_TAC4 (0x2F4) +#define TOMTOM_A_CDC_VBAT_TAC4__POR (0x03) +#define TOMTOM_A_CDC_VBAT_GAIN_UPD1 (0x2F5) +#define TOMTOM_A_CDC_VBAT_GAIN_UPD1__POR (0x01) +#define TOMTOM_A_CDC_VBAT_GAIN_UPD2 (0x2F6) +#define TOMTOM_A_CDC_VBAT_GAIN_UPD2__POR (0x00) +#define TOMTOM_A_CDC_VBAT_GAIN_UPD3 (0x2F7) +#define TOMTOM_A_CDC_VBAT_GAIN_UPD3__POR (0x64) +#define TOMTOM_A_CDC_VBAT_GAIN_UPD4 (0x2F8) +#define TOMTOM_A_CDC_VBAT_GAIN_UPD4__POR (0x01) +#define TOMTOM_A_CDC_VBAT_DEBUG1 (0x2F9) +#define TOMTOM_A_CDC_VBAT_DEBUG1__POR (0x00) +#define TOMTOM_A_CDC_VBAT_GAIN_UPD_MON (0x2FA) +#define TOMTOM_A_CDC_VBAT_GAIN_UPD_MON__POR (0x00) +#define TOMTOM_A_CDC_VBAT_GAIN_MON_VAL (0x2FB) +#define TOMTOM_A_CDC_VBAT_GAIN_MON_VAL__POR (0x00) +#define TOMTOM_A_CDC_CLK_ANC_RESET_CTL (0x300) +#define TOMTOM_A_CDC_CLK_ANC_RESET_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_RX_RESET_CTL (0x301) +#define TOMTOM_A_CDC_CLK_RX_RESET_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL (0x302) +#define TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL (0x303) +#define TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_RX_I2S_CTL (0x306) +#define TOMTOM_A_CDC_CLK_RX_I2S_CTL__POR (0x03) +#define TOMTOM_A_CDC_CLK_TX_I2S_CTL (0x307) +#define TOMTOM_A_CDC_CLK_TX_I2S_CTL__POR (0x03) +#define TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL (0x308) +#define TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL (0x309) +#define TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL (0x30A) +#define TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL (0x30B) +#define TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_OTHR_CTL (0x30C) +#define TOMTOM_A_CDC_CLK_OTHR_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL (0x30E) +#define TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_RX_B1_CTL (0x30F) +#define TOMTOM_A_CDC_CLK_RX_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_RX_B2_CTL (0x310) +#define TOMTOM_A_CDC_CLK_RX_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_MCLK_CTL (0x311) +#define TOMTOM_A_CDC_CLK_MCLK_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_PDM_CTL (0x312) +#define TOMTOM_A_CDC_CLK_PDM_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLK_SD_CTL (0x313) +#define TOMTOM_A_CDC_CLK_SD_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLSH_B1_CTL (0x320) +#define TOMTOM_A_CDC_CLSH_B1_CTL__POR (0xE4) +#define TOMTOM_A_CDC_CLSH_B2_CTL (0x321) +#define TOMTOM_A_CDC_CLSH_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLSH_B3_CTL (0x322) +#define TOMTOM_A_CDC_CLSH_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS (0x323) +#define TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS__POR (0x00) +#define TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD (0x324) +#define TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD__POR (0x12) +#define TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD (0x325) +#define TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD__POR (0x0C) +#define TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD (0x326) +#define TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR (0x18) +#define TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD (0x327) +#define TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR (0x23) +#define TOMTOM_A_CDC_CLSH_K_ADDR (0x328) +#define TOMTOM_A_CDC_CLSH_K_ADDR__POR (0x00) +#define TOMTOM_A_CDC_CLSH_K_DATA (0x329) +#define TOMTOM_A_CDC_CLSH_K_DATA__POR (0xA4) +#define TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L (0x32A) +#define TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L__POR (0xD7) +#define TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U (0x32B) +#define TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U__POR (0x05) +#define TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L (0x32C) +#define TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L__POR (0x60) +#define TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U (0x32D) +#define TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U__POR (0x09) +#define TOMTOM_A_CDC_CLSH_V_PA_HD_EAR (0x32E) +#define TOMTOM_A_CDC_CLSH_V_PA_HD_EAR__POR (0x00) +#define TOMTOM_A_CDC_CLSH_V_PA_HD_HPH (0x32F) +#define TOMTOM_A_CDC_CLSH_V_PA_HD_HPH__POR (0x00) +#define TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR (0x330) +#define TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR__POR (0x00) +#define TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH (0x331) +#define TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH__POR (0x00) +#define TOMTOM_A_CDC_IIR1_GAIN_B1_CTL (0x340) +#define TOMTOM_A_CDC_IIR1_GAIN_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR2_GAIN_B1_CTL (0x350) +#define TOMTOM_A_CDC_IIR2_GAIN_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR1_GAIN_B2_CTL (0x341) +#define TOMTOM_A_CDC_IIR1_GAIN_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR2_GAIN_B2_CTL (0x351) +#define TOMTOM_A_CDC_IIR2_GAIN_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR1_GAIN_B3_CTL (0x342) +#define TOMTOM_A_CDC_IIR1_GAIN_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR2_GAIN_B3_CTL (0x352) +#define TOMTOM_A_CDC_IIR2_GAIN_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR1_GAIN_B4_CTL (0x343) +#define TOMTOM_A_CDC_IIR1_GAIN_B4_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR2_GAIN_B4_CTL (0x353) +#define TOMTOM_A_CDC_IIR2_GAIN_B4_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR1_GAIN_B5_CTL (0x344) +#define TOMTOM_A_CDC_IIR1_GAIN_B5_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR2_GAIN_B5_CTL (0x354) +#define TOMTOM_A_CDC_IIR2_GAIN_B5_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR1_GAIN_B6_CTL (0x345) +#define TOMTOM_A_CDC_IIR1_GAIN_B6_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR2_GAIN_B6_CTL (0x355) +#define TOMTOM_A_CDC_IIR2_GAIN_B6_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR1_GAIN_B7_CTL (0x346) +#define TOMTOM_A_CDC_IIR1_GAIN_B7_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR2_GAIN_B7_CTL (0x356) +#define TOMTOM_A_CDC_IIR2_GAIN_B7_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR1_GAIN_B8_CTL (0x347) +#define TOMTOM_A_CDC_IIR1_GAIN_B8_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR2_GAIN_B8_CTL (0x357) +#define TOMTOM_A_CDC_IIR2_GAIN_B8_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR1_CTL (0x348) +#define TOMTOM_A_CDC_IIR1_CTL__POR (0x40) +#define TOMTOM_A_CDC_IIR2_CTL (0x358) +#define TOMTOM_A_CDC_IIR2_CTL__POR (0x40) +#define TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL (0x349) +#define TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL (0x359) +#define TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR1_COEF_B1_CTL (0x34A) +#define TOMTOM_A_CDC_IIR1_COEF_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR2_COEF_B1_CTL (0x35A) +#define TOMTOM_A_CDC_IIR2_COEF_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR1_COEF_B2_CTL (0x34B) +#define TOMTOM_A_CDC_IIR1_COEF_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_IIR2_COEF_B2_CTL (0x35B) +#define TOMTOM_A_CDC_IIR2_COEF_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_TOP_GAIN_UPDATE (0x360) +#define TOMTOM_A_CDC_TOP_GAIN_UPDATE__POR (0x00) +#define TOMTOM_A_CDC_PA_RAMP_B1_CTL (0x361) +#define TOMTOM_A_CDC_PA_RAMP_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_PA_RAMP_B2_CTL (0x362) +#define TOMTOM_A_CDC_PA_RAMP_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_PA_RAMP_B3_CTL (0x363) +#define TOMTOM_A_CDC_PA_RAMP_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_PA_RAMP_B4_CTL (0x364) +#define TOMTOM_A_CDC_PA_RAMP_B4_CTL__POR (0x00) +#define TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL (0x365) +#define TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL (0x366) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_COMP0_B1_CTL (0x368) +#define TOMTOM_A_CDC_COMP0_B1_CTL__POR (0x30) +#define TOMTOM_A_CDC_COMP1_B1_CTL (0x370) +#define TOMTOM_A_CDC_COMP1_B1_CTL__POR (0x30) +#define TOMTOM_A_CDC_COMP2_B1_CTL (0x378) +#define TOMTOM_A_CDC_COMP2_B1_CTL__POR (0x30) +#define TOMTOM_A_CDC_COMP0_B2_CTL (0x369) +#define TOMTOM_A_CDC_COMP0_B2_CTL__POR (0xB5) +#define TOMTOM_A_CDC_COMP1_B2_CTL (0x371) +#define TOMTOM_A_CDC_COMP1_B2_CTL__POR (0xB5) +#define TOMTOM_A_CDC_COMP2_B2_CTL (0x379) +#define TOMTOM_A_CDC_COMP2_B2_CTL__POR (0xB5) +#define TOMTOM_A_CDC_COMP0_B3_CTL (0x36A) +#define TOMTOM_A_CDC_COMP0_B3_CTL__POR (0x28) +#define TOMTOM_A_CDC_COMP1_B3_CTL (0x372) +#define TOMTOM_A_CDC_COMP1_B3_CTL__POR (0x28) +#define TOMTOM_A_CDC_COMP2_B3_CTL (0x37A) +#define TOMTOM_A_CDC_COMP2_B3_CTL__POR (0x28) +#define TOMTOM_A_CDC_COMP0_B4_CTL (0x36B) +#define TOMTOM_A_CDC_COMP0_B4_CTL__POR (0x37) +#define TOMTOM_A_CDC_COMP1_B4_CTL (0x373) +#define TOMTOM_A_CDC_COMP1_B4_CTL__POR (0x37) +#define TOMTOM_A_CDC_COMP2_B4_CTL (0x37B) +#define TOMTOM_A_CDC_COMP2_B4_CTL__POR (0x37) +#define TOMTOM_A_CDC_COMP0_B5_CTL (0x36C) +#define TOMTOM_A_CDC_COMP0_B5_CTL__POR (0x7F) +#define TOMTOM_A_CDC_COMP1_B5_CTL (0x374) +#define TOMTOM_A_CDC_COMP1_B5_CTL__POR (0x7F) +#define TOMTOM_A_CDC_COMP2_B5_CTL (0x37C) +#define TOMTOM_A_CDC_COMP2_B5_CTL__POR (0x7F) +#define TOMTOM_A_CDC_COMP0_B6_CTL (0x36D) +#define TOMTOM_A_CDC_COMP0_B6_CTL__POR (0x00) +#define TOMTOM_A_CDC_COMP1_B6_CTL (0x375) +#define TOMTOM_A_CDC_COMP1_B6_CTL__POR (0x00) +#define TOMTOM_A_CDC_COMP2_B6_CTL (0x37D) +#define TOMTOM_A_CDC_COMP2_B6_CTL__POR (0x00) +#define TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS (0x36E) +#define TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS__POR (0x03) +#define TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS (0x376) +#define TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS__POR (0x03) +#define TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS (0x37E) +#define TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS__POR (0x03) +#define TOMTOM_A_CDC_COMP0_FS_CFG (0x36F) +#define TOMTOM_A_CDC_COMP0_FS_CFG__POR (0x03) +#define TOMTOM_A_CDC_COMP1_FS_CFG (0x377) +#define TOMTOM_A_CDC_COMP1_FS_CFG__POR (0x03) +#define TOMTOM_A_CDC_COMP2_FS_CFG (0x37F) +#define TOMTOM_A_CDC_COMP2_FS_CFG__POR (0x03) +#define TOMTOM_A_CDC_CONN_RX1_B1_CTL (0x380) +#define TOMTOM_A_CDC_CONN_RX1_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX1_B2_CTL (0x381) +#define TOMTOM_A_CDC_CONN_RX1_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX1_B3_CTL (0x382) +#define TOMTOM_A_CDC_CONN_RX1_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX2_B1_CTL (0x383) +#define TOMTOM_A_CDC_CONN_RX2_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX2_B2_CTL (0x384) +#define TOMTOM_A_CDC_CONN_RX2_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX2_B3_CTL (0x385) +#define TOMTOM_A_CDC_CONN_RX2_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX3_B1_CTL (0x386) +#define TOMTOM_A_CDC_CONN_RX3_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX3_B2_CTL (0x387) +#define TOMTOM_A_CDC_CONN_RX3_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX4_B1_CTL (0x388) +#define TOMTOM_A_CDC_CONN_RX4_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX4_B2_CTL (0x389) +#define TOMTOM_A_CDC_CONN_RX4_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX5_B1_CTL (0x38A) +#define TOMTOM_A_CDC_CONN_RX5_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX5_B2_CTL (0x38B) +#define TOMTOM_A_CDC_CONN_RX5_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX6_B1_CTL (0x38C) +#define TOMTOM_A_CDC_CONN_RX6_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX6_B2_CTL (0x38D) +#define TOMTOM_A_CDC_CONN_RX6_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX7_B1_CTL (0x38E) +#define TOMTOM_A_CDC_CONN_RX7_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX7_B2_CTL (0x38F) +#define TOMTOM_A_CDC_CONN_RX7_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX7_B3_CTL (0x390) +#define TOMTOM_A_CDC_CONN_RX7_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_ANC_B1_CTL (0x391) +#define TOMTOM_A_CDC_CONN_ANC_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_ANC_B2_CTL (0x392) +#define TOMTOM_A_CDC_CONN_ANC_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_B1_CTL (0x393) +#define TOMTOM_A_CDC_CONN_TX_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_B2_CTL (0x394) +#define TOMTOM_A_CDC_CONN_TX_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_B3_CTL (0x395) +#define TOMTOM_A_CDC_CONN_TX_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_B4_CTL (0x396) +#define TOMTOM_A_CDC_CONN_TX_B4_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_EQ1_B1_CTL (0x397) +#define TOMTOM_A_CDC_CONN_EQ1_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_EQ1_B2_CTL (0x398) +#define TOMTOM_A_CDC_CONN_EQ1_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_EQ1_B3_CTL (0x399) +#define TOMTOM_A_CDC_CONN_EQ1_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_EQ1_B4_CTL (0x39A) +#define TOMTOM_A_CDC_CONN_EQ1_B4_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_EQ2_B1_CTL (0x39B) +#define TOMTOM_A_CDC_CONN_EQ2_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_EQ2_B2_CTL (0x39C) +#define TOMTOM_A_CDC_CONN_EQ2_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_EQ2_B3_CTL (0x39D) +#define TOMTOM_A_CDC_CONN_EQ2_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_EQ2_B4_CTL (0x39E) +#define TOMTOM_A_CDC_CONN_EQ2_B4_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_SRC1_B1_CTL (0x39F) +#define TOMTOM_A_CDC_CONN_SRC1_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_SRC1_B2_CTL (0x3A0) +#define TOMTOM_A_CDC_CONN_SRC1_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_SRC2_B1_CTL (0x3A1) +#define TOMTOM_A_CDC_CONN_SRC2_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_SRC2_B2_CTL (0x3A2) +#define TOMTOM_A_CDC_CONN_SRC2_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_SB_B1_CTL (0x3A3) +#define TOMTOM_A_CDC_CONN_TX_SB_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_SB_B2_CTL (0x3A4) +#define TOMTOM_A_CDC_CONN_TX_SB_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_SB_B3_CTL (0x3A5) +#define TOMTOM_A_CDC_CONN_TX_SB_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_SB_B4_CTL (0x3A6) +#define TOMTOM_A_CDC_CONN_TX_SB_B4_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_SB_B5_CTL (0x3A7) +#define TOMTOM_A_CDC_CONN_TX_SB_B5_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_SB_B6_CTL (0x3A8) +#define TOMTOM_A_CDC_CONN_TX_SB_B6_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_SB_B7_CTL (0x3A9) +#define TOMTOM_A_CDC_CONN_TX_SB_B7_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_SB_B8_CTL (0x3AA) +#define TOMTOM_A_CDC_CONN_TX_SB_B8_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_SB_B9_CTL (0x3AB) +#define TOMTOM_A_CDC_CONN_TX_SB_B9_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_SB_B10_CTL (0x3AC) +#define TOMTOM_A_CDC_CONN_TX_SB_B10_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_TX_SB_B11_CTL (0x3AD) +#define TOMTOM_A_CDC_CONN_TX_SB_B11_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX_SB_B1_CTL (0x3AE) +#define TOMTOM_A_CDC_CONN_RX_SB_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_RX_SB_B2_CTL (0x3AF) +#define TOMTOM_A_CDC_CONN_RX_SB_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_CLSH_CTL (0x3B0) +#define TOMTOM_A_CDC_CONN_CLSH_CTL__POR (0x00) +#define TOMTOM_A_CDC_CONN_MISC (0x3B1) +#define TOMTOM_A_CDC_CONN_MISC__POR (0x01) +#define TOMTOM_A_CDC_CONN_RX8_B1_CTL (0x3B3) +#define TOMTOM_A_CDC_CONN_RX8_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL (0x3B4) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL__POR (0x81) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST (0x3B5) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST__POR (0x00) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD (0x3B6) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD__POR (0xFF) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS (0x3B7) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS__POR (0x00) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK (0x3B8) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK__POR (0x04) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING (0x3B9) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING__POR (0x04) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL (0x3BA) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL__POR (0x81) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST (0x3BB) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST__POR (0x00) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD (0x3BC) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD__POR (0xFF) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS (0x3BD) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS__POR (0x00) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK (0x3BE) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK__POR (0x04) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING (0x3BF) +#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING__POR (0x04) +#define TOMTOM_A_CDC_MBHC_EN_CTL (0x3C0) +#define TOMTOM_A_CDC_MBHC_EN_CTL__POR (0x00) +#define TOMTOM_A_CDC_MBHC_FIR_B1_CFG (0x3C1) +#define TOMTOM_A_CDC_MBHC_FIR_B1_CFG__POR (0x00) +#define TOMTOM_A_CDC_MBHC_FIR_B2_CFG (0x3C2) +#define TOMTOM_A_CDC_MBHC_FIR_B2_CFG__POR (0x06) +#define TOMTOM_A_CDC_MBHC_TIMER_B1_CTL (0x3C3) +#define TOMTOM_A_CDC_MBHC_TIMER_B1_CTL__POR (0x03) +#define TOMTOM_A_CDC_MBHC_TIMER_B2_CTL (0x3C4) +#define TOMTOM_A_CDC_MBHC_TIMER_B2_CTL__POR (0x09) +#define TOMTOM_A_CDC_MBHC_TIMER_B3_CTL (0x3C5) +#define TOMTOM_A_CDC_MBHC_TIMER_B3_CTL__POR (0x1E) +#define TOMTOM_A_CDC_MBHC_TIMER_B4_CTL (0x3C6) +#define TOMTOM_A_CDC_MBHC_TIMER_B4_CTL__POR (0x45) +#define TOMTOM_A_CDC_MBHC_TIMER_B5_CTL (0x3C7) +#define TOMTOM_A_CDC_MBHC_TIMER_B5_CTL__POR (0x04) +#define TOMTOM_A_CDC_MBHC_TIMER_B6_CTL (0x3C8) +#define TOMTOM_A_CDC_MBHC_TIMER_B6_CTL__POR (0x78) +#define TOMTOM_A_CDC_MBHC_B1_STATUS (0x3C9) +#define TOMTOM_A_CDC_MBHC_B1_STATUS__POR (0x00) +#define TOMTOM_A_CDC_MBHC_B2_STATUS (0x3CA) +#define TOMTOM_A_CDC_MBHC_B2_STATUS__POR (0x00) +#define TOMTOM_A_CDC_MBHC_B3_STATUS (0x3CB) +#define TOMTOM_A_CDC_MBHC_B3_STATUS__POR (0x00) +#define TOMTOM_A_CDC_MBHC_B4_STATUS (0x3CC) +#define TOMTOM_A_CDC_MBHC_B4_STATUS__POR (0x00) +#define TOMTOM_A_CDC_MBHC_B5_STATUS (0x3CD) +#define TOMTOM_A_CDC_MBHC_B5_STATUS__POR (0x00) +#define TOMTOM_A_CDC_MBHC_B1_CTL (0x3CE) +#define TOMTOM_A_CDC_MBHC_B1_CTL__POR (0xC0) +#define TOMTOM_A_CDC_MBHC_B2_CTL (0x3CF) +#define TOMTOM_A_CDC_MBHC_B2_CTL__POR (0x5D) +#define TOMTOM_A_CDC_MBHC_VOLT_B1_CTL (0x3D0) +#define TOMTOM_A_CDC_MBHC_VOLT_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_MBHC_VOLT_B2_CTL (0x3D1) +#define TOMTOM_A_CDC_MBHC_VOLT_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_MBHC_VOLT_B3_CTL (0x3D2) +#define TOMTOM_A_CDC_MBHC_VOLT_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_MBHC_VOLT_B4_CTL (0x3D3) +#define TOMTOM_A_CDC_MBHC_VOLT_B4_CTL__POR (0x00) +#define TOMTOM_A_CDC_MBHC_VOLT_B5_CTL (0x3D4) +#define TOMTOM_A_CDC_MBHC_VOLT_B5_CTL__POR (0x00) +#define TOMTOM_A_CDC_MBHC_VOLT_B6_CTL (0x3D5) +#define TOMTOM_A_CDC_MBHC_VOLT_B6_CTL__POR (0x00) +#define TOMTOM_A_CDC_MBHC_VOLT_B7_CTL (0x3D6) +#define TOMTOM_A_CDC_MBHC_VOLT_B7_CTL__POR (0xFF) +#define TOMTOM_A_CDC_MBHC_VOLT_B8_CTL (0x3D7) +#define TOMTOM_A_CDC_MBHC_VOLT_B8_CTL__POR (0x07) +#define TOMTOM_A_CDC_MBHC_VOLT_B9_CTL (0x3D8) +#define TOMTOM_A_CDC_MBHC_VOLT_B9_CTL__POR (0xFF) +#define TOMTOM_A_CDC_MBHC_VOLT_B10_CTL (0x3D9) +#define TOMTOM_A_CDC_MBHC_VOLT_B10_CTL__POR (0x7F) +#define TOMTOM_A_CDC_MBHC_VOLT_B11_CTL (0x3DA) +#define TOMTOM_A_CDC_MBHC_VOLT_B11_CTL__POR (0x00) +#define TOMTOM_A_CDC_MBHC_VOLT_B12_CTL (0x3DB) +#define TOMTOM_A_CDC_MBHC_VOLT_B12_CTL__POR (0x80) +#define TOMTOM_A_CDC_MBHC_CLK_CTL (0x3DC) +#define TOMTOM_A_CDC_MBHC_CLK_CTL__POR (0x00) +#define TOMTOM_A_CDC_MBHC_INT_CTL (0x3DD) +#define TOMTOM_A_CDC_MBHC_INT_CTL__POR (0x00) +#define TOMTOM_A_CDC_MBHC_DEBUG_CTL (0x3DE) +#define TOMTOM_A_CDC_MBHC_DEBUG_CTL__POR (0x00) +#define TOMTOM_A_CDC_MBHC_SPARE (0x3DF) +#define TOMTOM_A_CDC_MBHC_SPARE__POR (0x00) +#define TOMTOM_A_CDC_RX8_B1_CTL (0x3E0) +#define TOMTOM_A_CDC_RX8_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX8_B2_CTL (0x3E1) +#define TOMTOM_A_CDC_RX8_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX8_B3_CTL (0x3E2) +#define TOMTOM_A_CDC_RX8_B3_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX8_B4_CTL (0x3E3) +#define TOMTOM_A_CDC_RX8_B4_CTL__POR (0x0B) +#define TOMTOM_A_CDC_RX8_B5_CTL (0x3E4) +#define TOMTOM_A_CDC_RX8_B5_CTL__POR (0x78) +#define TOMTOM_A_CDC_RX8_B6_CTL (0x3E5) +#define TOMTOM_A_CDC_RX8_B6_CTL__POR (0x80) +#define TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL (0x3E6) +#define TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL__POR (0x00) +#define TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL (0x3E7) +#define TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL__POR (0x00) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0 (0x3E8) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0__POR (0x00) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1 (0x3E9) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1__POR (0x00) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2 (0x3EA) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2__POR (0x00) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3 (0x3EB) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3__POR (0x00) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4 (0x3EC) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4__POR (0x00) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5 (0x3ED) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5__POR (0x00) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6 (0x3EE) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6__POR (0x00) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7 (0x3EF) +#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7__POR (0x00) +#define TOMTOM_A_CDC_BOOST_MODE_CTL (0x3F0) +#define TOMTOM_A_CDC_BOOST_MODE_CTL__POR (0x00) +#define TOMTOM_A_CDC_BOOST_THRESHOLD (0x3F1) +#define TOMTOM_A_CDC_BOOST_THRESHOLD__POR (0x02) +#define TOMTOM_A_CDC_BOOST_TAP_SEL (0x3F2) +#define TOMTOM_A_CDC_BOOST_TAP_SEL__POR (0x00) +#define TOMTOM_A_CDC_BOOST_HOLD_TIME (0x3F3) +#define TOMTOM_A_CDC_BOOST_HOLD_TIME__POR (0x02) +#define TOMTOM_A_CDC_BOOST_TRGR_EN (0x3F4) +#define TOMTOM_A_CDC_BOOST_TRGR_EN__POR (0x00) + +/* SLIMBUS Slave Registers */ +#define TOMTOM_SLIM_PGD_PORT_INT_EN0 (0x30) +#define TOMTOM_SLIM_PGD_PORT_INT_STATUS_RX_0 (0x34) +#define TOMTOM_SLIM_PGD_PORT_INT_STATUS_RX_1 (0x35) +#define TOMTOM_SLIM_PGD_PORT_INT_STATUS_TX_0 (0x36) +#define TOMTOM_SLIM_PGD_PORT_INT_STATUS_TX_1 (0x37) +#define TOMTOM_SLIM_PGD_PORT_INT_CLR_RX_0 (0x38) +#define TOMTOM_SLIM_PGD_PORT_INT_CLR_RX_1 (0x39) +#define TOMTOM_SLIM_PGD_PORT_INT_CLR_TX_0 (0x3A) +#define TOMTOM_SLIM_PGD_PORT_INT_CLR_TX_1 (0x3B) +#define TOMTOM_SLIM_PGD_PORT_INT_RX_SOURCE0 (0x60) +#define TOMTOM_SLIM_PGD_PORT_INT_TX_SOURCE0 (0x70) + +/* Macros for Packing Register Writes into a U32 */ +#define TOMTOM_PACKED_REG_SIZE sizeof(u32) + +#define TOMTOM_CODEC_PACK_ENTRY(reg, mask, val) ((val & 0xff)|\ + ((mask & 0xff) << 8)|((reg & 0xffff) << 16)) +#define TOMTOM_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xffff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0) + +#define TOMTOM_SB_PGD_PORT_TX_BASE 0x50 +#define TOMTOM_SB_PGD_PORT_RX_BASE 0x40 +#define WCD9330_MAX_REGISTER 0x3FF +extern const u8 tomtom_reg_readable[WCD9330_MAX_REGISTER + 1]; +#endif diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-irq.h b/include/linux/mfd/wcd9xxx/wcd9xxx-irq.h new file mode 100644 index 0000000000000000000000000000000000000000..1e428a1e8b269d5df6a631d0793dbb0b30a51de4 --- /dev/null +++ b/include/linux/mfd/wcd9xxx/wcd9xxx-irq.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#ifndef __MFD_WCD9XXX_IRQ_H +#define __MFD_WCD9XXX_IRQ_H +bool wcd9xxx_lock_sleep(struct wcd9xxx_core_resource *wcd9xxx_res); +void wcd9xxx_unlock_sleep(struct wcd9xxx_core_resource *wcd9xxx_res); +void wcd9xxx_nested_irq_lock(struct wcd9xxx_core_resource *wcd9xxx_res); +void wcd9xxx_nested_irq_unlock(struct wcd9xxx_core_resource *wcd9xxx_res); +int wcd9xxx_request_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq, + irq_handler_t handler, const char *name, void *data); + +void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq, void *data); +void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq); +void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq); +void wcd9xxx_disable_irq_sync(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq); + +int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res); +void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res); +#endif diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h new file mode 100644 index 0000000000000000000000000000000000000000..96fdb00a2e033c4d016ba1ca8ba1bea10da11c95 --- /dev/null +++ b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h @@ -0,0 +1,119 @@ +/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD9310_SLIMSLAVE_H_ +#define __WCD9310_SLIMSLAVE_H_ + +#include +#include + + +/* + * client is expected to give port ids in the range of + * 1-10 for pre Taiko Tx ports and 1-16 for Taiko + * 1-7 for pre Taiko Rx ports and 1-16 for Tako, + * we need to add offset for getting the absolute slave + * port id before configuring the HW + */ +#define TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS 10 +#define TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS 16 + +#define SLIM_MAX_TX_PORTS TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS + +#define TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS \ + TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS +#define TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS \ + TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS + +#define TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS 7 +#define TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS 13 + +#define SLIM_MAX_RX_PORTS TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS + +#define SLIM_MAX_REG_ADDR (0x180 + 4 * (SLIM_MAX_RX_PORTS)) + +#define TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID \ + TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS +#define TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID \ + TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS + +#define TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID 16 +#define TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID 31 + +#define TABLA_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID 9 +#define TAIKO_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID 15 + +/* below details are taken from SLIMBUS slave SWI */ +#define SB_PGD_PORT_BASE 0x000 + +#define SB_PGD_PORT_CFG_BYTE_ADDR(offset, port_num) \ + (SB_PGD_PORT_BASE + offset + (1 * port_num)) + +#define SB_PGD_TX_PORT_MULTI_CHANNEL_0(port_num) \ + (SB_PGD_PORT_BASE + 0x100 + 4*port_num) +#define SB_PGD_TX_PORT_MULTI_CHANNEL_0_START_PORT_ID 0 +#define SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID 7 + +#define SB_PGD_TX_PORT_MULTI_CHANNEL_1(port_num) \ + (SB_PGD_PORT_BASE + 0x101 + 4*port_num) +#define SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID 8 + +#define SB_PGD_RX_PORT_MULTI_CHANNEL_0(offset, port_num) \ + (SB_PGD_PORT_BASE + offset + (4 * port_num)) + +/* 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 WATER_MARK_VAL \ + ((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \ + (SLAVE_PORT_ENABLE)) +#define BASE_CH_NUM 128 + + +int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, + u8 wcd9xxx_pgd_la, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot); + +int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx); + +int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + unsigned int rate, unsigned int bit_width, + u16 *grph); +int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + unsigned int rate, unsigned int bit_width, + u16 *grph); +int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph); +int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph); +int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx, + unsigned int *rx_ch, + unsigned int *tx_ch); +int wcd9xxx_get_slave_port(unsigned int ch_num); +int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph); +int wcd9xxx_rx_vport_validation(u32 port_id, + struct list_head *codec_dai_list); +int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id, + struct wcd9xxx_codec_dai_data *codec_dai, + u32 num_codec_dais); +#endif /* __WCD9310_SLIMSLAVE_H_ */ diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-utils.h b/include/linux/mfd/wcd9xxx/wcd9xxx-utils.h new file mode 100644 index 0000000000000000000000000000000000000000..d0ac0ac17587251e8352614b9dcfb02f734ea866 --- /dev/null +++ b/include/linux/mfd/wcd9xxx/wcd9xxx-utils.h @@ -0,0 +1,140 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD9XXX_UTILS_H__ +#define __WCD9XXX_UTILS_H__ + +#include +#include +#include +#include +#include + +struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev); +int wcd9xxx_bringup(struct device *dev); +int wcd9xxx_bringdown(struct device *dev); +struct regmap *wcd9xxx_regmap_init(struct device *dev, + const struct regmap_config *config); +int wcd9xxx_reset(struct device *dev); +int wcd9xxx_reset_low(struct device *dev); +int wcd9xxx_get_codec_info(struct device *dev); + +typedef int (*codec_bringup_fn)(struct wcd9xxx *); +typedef int (*codec_bringdown_fn)(struct wcd9xxx *); +typedef int (*codec_type_fn)(struct wcd9xxx *, + struct wcd9xxx_codec_type *); + +#ifdef CONFIG_WCD934X_CODEC +extern int wcd934x_bringup(struct wcd9xxx *wcd9xxx); +extern int wcd934x_bringdown(struct wcd9xxx *wcd9xxx); +extern int wcd934x_get_codec_info(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_codec_type *wcd_type); +#endif + +#ifdef CONFIG_WCD9335_CODEC +extern int wcd9335_bringup(struct wcd9xxx *wcd9xxx); +extern int wcd9335_bringdown(struct wcd9xxx *wcd9xxx); +extern int wcd9335_get_codec_info(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_codec_type *wcd_type); +#endif + +#ifdef CONFIG_WCD9330_CODEC +extern int wcd9330_bringup(struct wcd9xxx *wcd9xxx); +extern int wcd9330_bringdown(struct wcd9xxx *wcd9xxx); +extern int wcd9330_get_codec_info(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_codec_type *wcd_type); +#endif + +static inline codec_bringdown_fn wcd9xxx_bringdown_fn(int type) +{ + codec_bringdown_fn cdc_bdown_fn; + + switch (type) { +#ifdef CONFIG_WCD934X_CODEC + case WCD934X: + cdc_bdown_fn = wcd934x_bringdown; + break; +#endif +#ifdef CONFIG_WCD9335_CODEC + case WCD9335: + cdc_bdown_fn = wcd9335_bringdown; + break; +#endif +#ifdef CONFIG_WCD9330_CODEC + case WCD9330: + cdc_bdown_fn = wcd9330_bringdown; + break; +#endif + default: + cdc_bdown_fn = NULL; + break; + } + + return cdc_bdown_fn; +} + +static inline codec_bringup_fn wcd9xxx_bringup_fn(int type) +{ + codec_bringup_fn cdc_bup_fn; + + switch (type) { +#ifdef CONFIG_WCD934X_CODEC + case WCD934X: + cdc_bup_fn = wcd934x_bringup; + break; +#endif +#ifdef CONFIG_WCD9335_CODEC + case WCD9335: + cdc_bup_fn = wcd9335_bringup; + break; +#endif +#ifdef CONFIG_WCD9330_CODEC + case WCD9330: + cdc_bup_fn = wcd9330_bringup; + break; +#endif + default: + cdc_bup_fn = NULL; + break; + } + + return cdc_bup_fn; +} + +static inline codec_type_fn wcd9xxx_get_codec_info_fn(int type) +{ + codec_type_fn cdc_type_fn; + + switch (type) { +#ifdef CONFIG_WCD934X_CODEC + case WCD934X: + cdc_type_fn = wcd934x_get_codec_info; + break; +#endif +#ifdef CONFIG_WCD9335_CODEC + case WCD9335: + cdc_type_fn = wcd9335_get_codec_info; + break; +#endif +#ifdef CONFIG_WCD9330_CODEC + case WCD9330: + cdc_type_fn = wcd9330_get_codec_info; + break; +#endif + default: + cdc_type_fn = NULL; + break; + } + + return cdc_type_fn; +} +#endif diff --git a/include/linux/soundwire/soundwire.h b/include/linux/soundwire/soundwire.h new file mode 100644 index 0000000000000000000000000000000000000000..752a0017caddff91d31bdf80a15496eb7e219821 --- /dev/null +++ b/include/linux/soundwire/soundwire.h @@ -0,0 +1,312 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_SOUNDWIRE_H +#define _LINUX_SOUNDWIRE_H +#include +#include +#include + +extern struct bus_type soundwire_type; + +/* Soundwire supports max. of 8 channels per port */ +#define SWR_MAX_CHANNEL_NUM 8 +/* Soundwire supports max. of 14 ports on each device */ +#define SWR_MAX_DEV_PORT_NUM 14 +/* Maximum number of slave devices that a master can control */ +#define SWR_MAX_DEV_NUM 11 +/* Maximum number of ports on master so that it can accommodate all the port + * configurations of all devices + */ +#define SWR_MAX_MSTR_PORT_NUM (SWR_MAX_DEV_NUM * SWR_MAX_DEV_PORT_NUM) + +/* Indicates soundwire devices group information */ +enum { + SWR_GROUP_NONE = 0, + SWR_GROUP_12 = 12, + SWR_GROUP_13 = 13, + SWR_BROADCAST = 15, +}; + +/* + * struct swr_port_info - represent soundwire frame shape + * @dev_id: logical device number of the soundwire slave device + * @port_en: flag indicates whether the port is enabled + * @port_id: logical port number of the soundwire slave device + * @offset1: sample offset indicating the offset of the channel + * from the start of the frame + * @offset2: channel offset indicating offset between to channels + * @sinterval: sample interval indicates spacing from one sample + * event to the next + * @ch_en: channels in a port that need to be enabled + * @num_ch: number of channels enabled in a port + * @ch_rate: sampling rate of the channel with which data will be + * transferred + * + * Soundwire frame shape is created based on swr_port_info struct + * parameters. + */ +struct swr_port_info { + u8 dev_id; + u8 port_en; + u8 port_id; + u8 offset1; + u8 offset2; + u8 sinterval; + u8 ch_en; + u8 num_ch; + u32 ch_rate; +}; + +/* + * struct swr_params - represent transfer of data from soundwire slave + * to soundwire master + * @tid: transaction ID to track each transaction + * @dev_id: logical device number of the soundwire slave device + * @num_port: number of ports that needs to be configured + * @port_id: array of logical port numbers of the soundwire slave device + * @num_ch: array of number of channels enabled + * @ch_rate: array of sampling rate of different channels that need to + * be configured + * @ch_en: array of channels mask for all the ports + */ +struct swr_params { + u8 tid; + u8 dev_id; + u8 num_port; + u8 port_id[SWR_MAX_DEV_PORT_NUM]; + u8 num_ch[SWR_MAX_DEV_PORT_NUM]; + u32 ch_rate[SWR_MAX_DEV_PORT_NUM]; + u8 ch_en[SWR_MAX_DEV_PORT_NUM]; +}; + +/* + * struct swr_reg - struct to handle soundwire slave register read/writes + * @tid: transaction id for reg read/writes + * @dev_id: logical device number of the soundwire slave device + * @regaddr: 16 bit regaddr of soundwire slave + * @buf: value to be written/read to/from regaddr + * @len: length of the buffer buf + */ +struct swr_reg { + u8 tid; + u8 dev_id; + u32 regaddr; + u32 *buf; + u32 len; +}; + +/* + * struct swr_master - Interface to the soundwire master controller + * @dev: device interface to this driver + * @list: link with other soundwire master controllers + * @bus_num: board/SoC specific identifier for a soundwire master + * @mlock: mutex protecting master data structures + * @devices: list of devices on this master + * @port: logical port numbers of the soundwire master. This array + * can hold maximum master ports which is equal to number of slave + * devices multiplied by number of ports in each slave device + * @port_txn: table of port config transactions with transaction id + * @reg_txn: table of register transactions with transaction id + * @last_tid: size of table port_txn (can't grow beyond 256 since + * tid is 8 bits) + * @num_port: number of active ports on soundwire master + * @gr_sid: slave id used by the group for write operations + * @connect_port: callback for configuration of soundwire port(s) + * @disconnect_port: callback for disable of soundwire port(s) + * @read: callback for soundwire slave register read + * @write: callback for soundwire slave register write + * @get_logical_dev_num: callback to get soundwire slave logical + * device number + */ +struct swr_master { + struct device dev; + struct list_head list; + unsigned int bus_num; + struct mutex mlock; + struct list_head devices; + struct swr_port_info port[SWR_MAX_MSTR_PORT_NUM]; + struct swr_params **port_txn; + struct swr_reg **reg_txn; + u8 last_tid; + u8 num_port; + u8 num_dev; + u8 gr_sid; + int (*connect_port)(struct swr_master *mstr, struct swr_params *txn); + int (*disconnect_port)(struct swr_master *mstr, struct swr_params *txn); + int (*read)(struct swr_master *mstr, u8 dev_num, u16 reg_addr, + void *buf, u32 len); + int (*write)(struct swr_master *mstr, u8 dev_num, u16 reg_addr, + const void *buf); + int (*bulk_write)(struct swr_master *master, u8 dev_num, void *reg, + const void *buf, size_t len); + int (*get_logical_dev_num)(struct swr_master *mstr, u64 dev_id, + u8 *dev_num); + void (*slvdev_datapath_control)(struct swr_master *mstr, bool enable); + bool (*remove_from_group)(struct swr_master *mstr); +}; + +static inline struct swr_master *to_swr_master(struct device *dev) +{ + return dev ? container_of(dev, struct swr_master, dev) : NULL; +} + +/* + * struct swr_device - represent a soundwire slave device + * @name: indicates the name of the device, defined in devicetree + * binding under soundwire slave device node as a compatible field. + * @master: soundwire master managing the bus hosting this device + * @driver: Device's driver. Pointer to access routines + * @dev_list: list of devices on a controller + * @dev_num: logical device number of the soundwire slave device + * @dev: driver model representation of the device + * @addr: represents "ea-addr" which is unique-id of soundwire slave + * device + * @group_id: group id supported by the slave device + */ +struct swr_device { + char name[SOUNDWIRE_NAME_SIZE]; + struct swr_master *master; + struct swr_driver *driver; + struct list_head dev_list; + u8 dev_num; + struct device dev; + unsigned long addr; + u8 group_id; +}; + +static inline struct swr_device *to_swr_device(struct device *dev) +{ + return dev ? container_of(dev, struct swr_device, dev) : NULL; +} + +/* + * struct swr_driver - Manage soundwire slave device driver + * @probe: binds this driver to soundwire device + * @remove: unbinds this driver from soundwire device + * @shutdown: standard shutdown callback used during power down/halt + * @suspend: standard suspend callback used during system suspend + * @resume: standard resume callback used during system resume + * @startup: additional init operation for slave devices + * @driver: soundwire device drivers should initialize name and + * owner field of this structure + * @id_table: list of soundwire devices supported by this driver + */ +struct swr_driver { + int (*probe)(struct swr_device *swr); + int (*remove)(struct swr_device *swr); + void (*shutdown)(struct swr_device *swr); + int (*suspend)(struct swr_device *swr, pm_message_t pmesg); + int (*resume)(struct swr_device *swr); + int (*device_up)(struct swr_device *swr); + int (*device_down)(struct swr_device *swr); + int (*reset_device)(struct swr_device *swr); + int (*startup)(struct swr_device *swr); + struct device_driver driver; + const struct swr_device_id *id_table; +}; + +static inline struct swr_driver *to_swr_driver(struct device_driver *drv) +{ + return drv ? container_of(drv, struct swr_driver, driver) : NULL; +} + +/* + * struct swr_boardinfo - Declare board info for soundwire device bringup + * @name: name to initialize swr_device.name + * @bus_num: identifies which soundwire master parents the soundwire + * slave_device + * @addr: represents "ea-addr" of soundwire slave device + * @of_node: pointer to OpenFirmware device node + * @swr_slave: device to be registered with soundwire + */ +struct swr_boardinfo { + char name[SOUNDWIRE_NAME_SIZE]; + int bus_num; + u64 addr; + struct device_node *of_node; + struct swr_device *swr_slave; +}; + +static inline void *swr_get_ctrl_data(const struct swr_master *master) +{ + return master ? dev_get_drvdata(&master->dev) : NULL; +} + +static inline void swr_set_ctrl_data(struct swr_master *master, void *data) +{ + dev_set_drvdata(&master->dev, data); +} + +static inline void *swr_get_dev_data(const struct swr_device *dev) +{ + return dev ? dev_get_drvdata(&dev->dev) : NULL; +} + +static inline void swr_set_dev_data(struct swr_device *dev, void *data) +{ + dev_set_drvdata(&dev->dev, data); +} + +extern int swr_startup_devices(struct swr_device *swr_dev); + +extern struct swr_device *swr_new_device(struct swr_master *master, + struct swr_boardinfo const *info); + +extern int of_register_swr_devices(struct swr_master *master); + +extern void swr_port_response(struct swr_master *mstr, u8 tid); + +extern int swr_get_logical_dev_num(struct swr_device *dev, u64 dev_id, + u8 *dev_num); + +extern int swr_read(struct swr_device *dev, u8 dev_num, u16 reg_addr, + void *buf, u32 len); + +extern int swr_write(struct swr_device *dev, u8 dev_num, u16 reg_addr, + const void *buf); + +extern int swr_bulk_write(struct swr_device *dev, u8 dev_num, void *reg_addr, + const void *buf, size_t len); + +extern int swr_connect_port(struct swr_device *dev, u8 *port_id, u8 num_port, + u8 *ch_mask, u32 *ch_rate, u8 *num_ch); + +extern int swr_disconnect_port(struct swr_device *dev, + u8 *port_id, u8 num_port); + +extern int swr_set_device_group(struct swr_device *swr_dev, u8 id); + +extern int swr_driver_register(struct swr_driver *drv); + +extern void swr_driver_unregister(struct swr_driver *drv); + +extern int swr_add_device(struct swr_master *master, + struct swr_device *swrdev); +extern void swr_remove_device(struct swr_device *swr); + +extern void swr_master_add_boarddevices(struct swr_master *master); + +extern void swr_unregister_master(struct swr_master *master); + +extern int swr_register_master(struct swr_master *master); + +extern int swr_device_up(struct swr_device *swr_dev); + +extern int swr_device_down(struct swr_device *swr_dev); + +extern int swr_reset_device(struct swr_device *swr_dev); + +extern int swr_slvdev_datapath_control(struct swr_device *swr_dev, u8 dev_num, + bool enable); +extern int swr_remove_from_group(struct swr_device *dev, u8 dev_num); +#endif /* _LINUX_SOUNDWIRE_H */ diff --git a/include/linux/soundwire/swr-wcd.h b/include/linux/soundwire/swr-wcd.h new file mode 100644 index 0000000000000000000000000000000000000000..041b901fd954c0955220307d6cff5bc4152b66c4 --- /dev/null +++ b/include/linux/soundwire/swr-wcd.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_SWR_WCD_H +#define _LINUX_SWR_WCD_H +#include +#include +#include +#include + +enum { + SWR_CH_MAP, + SWR_DEVICE_DOWN, + SWR_DEVICE_UP, + SWR_SUBSYS_RESTART, + SWR_SET_NUM_RX_CH, +}; + +struct swr_mstr_port { + int num_port; + u8 *port; +}; + +extern int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data); + +#endif /* _LINUX_SWR_WCD_H */ diff --git a/include/sound/wcd-dsp-mgr.h b/include/sound/wcd-dsp-mgr.h new file mode 100644 index 0000000000000000000000000000000000000000..2beb9b38a46a71e38bb3e59d7f4b124771cc2ee8 --- /dev/null +++ b/include/sound/wcd-dsp-mgr.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD_DSP_MGR_H__ +#define __WCD_DSP_MGR_H__ + +#include + +/* + * These enums correspond to the component types + * that wcd-dsp-manager driver will use. The order + * of the enums specifies the order in which the + * manager driver will perform the sequencing. + * Changing this will cause the sequencing order + * to be changed as well. + */ +enum wdsp_cmpnt_type { + /* Component to control the DSP */ + WDSP_CMPNT_CONTROL = 0, + /* Component to perform data transfer to/from DSP */ + WDSP_CMPNT_TRANSPORT, + /* Component that performs high level IPC */ + WDSP_CMPNT_IPC, + + WDSP_CMPNT_TYPE_MAX, +}; + +enum wdsp_event_type { + /* Initialization related */ + WDSP_EVENT_POST_INIT, + + /* Image download related */ + WDSP_EVENT_PRE_DLOAD_CODE, + WDSP_EVENT_DLOAD_SECTION, + WDSP_EVENT_POST_DLOAD_CODE, + WDSP_EVENT_PRE_DLOAD_DATA, + WDSP_EVENT_POST_DLOAD_DATA, + WDSP_EVENT_DLOAD_FAILED, + + WDSP_EVENT_READ_SECTION, + + /* DSP boot related */ + WDSP_EVENT_PRE_BOOTUP, + WDSP_EVENT_DO_BOOT, + WDSP_EVENT_POST_BOOTUP, + WDSP_EVENT_PRE_SHUTDOWN, + WDSP_EVENT_DO_SHUTDOWN, + WDSP_EVENT_POST_SHUTDOWN, + + /* IRQ handling related */ + WDSP_EVENT_IPC1_INTR, + + /* Suspend/Resume related */ + WDSP_EVENT_SUSPEND, + WDSP_EVENT_RESUME, +}; + +enum wdsp_signal { + /* Hardware generated interrupts signalled to manager */ + WDSP_IPC1_INTR, + WDSP_ERR_INTR, + + /* Other signals */ + WDSP_CDC_DOWN_SIGNAL, + WDSP_CDC_UP_SIGNAL, +}; + +/* + * wdsp_cmpnt_ops: ops/function callbacks for components + * @init: called by manager driver, component is expected + * to initialize itself in this callback + * @deinit: called by manager driver, component should + * de-initialize itself in this callback + * @event_handler: Event handler for each component, called + * by the manager as per sequence + */ +struct wdsp_cmpnt_ops { + int (*init)(struct device *, void *priv_data); + int (*deinit)(struct device *, void *priv_data); + int (*event_handler)(struct device *, void *priv_data, + enum wdsp_event_type, void *data); +}; + +struct wdsp_img_section { + u32 addr; + size_t size; + u8 *data; +}; + +struct wdsp_err_signal_arg { + bool mem_dumps_enabled; + u32 remote_start_addr; + size_t dump_size; +}; + +/* + * wdsp_ops: ops/function callbacks for manager driver + * @register_cmpnt_ops: components will use this to register + * their own ops to manager driver + * @get_dev_for_cmpnt: components can use this to get handle + * to struct device * of any other component + * @signal_handler: callback to notify manager driver that signal + * has occurred. Cannot be called from interrupt + * context as this can sleep + * @vote_for_dsp: notifies manager that dsp should be booted up + * @suspend: notifies manager that one component wants to suspend. + * Manager will make sure to suspend all components in order + * @resume: notifies manager that one component wants to resume. + * Manager will make sure to resume all components in order + */ + +struct wdsp_mgr_ops { + int (*register_cmpnt_ops)(struct device *wdsp_dev, + struct device *cdev, + void *priv_data, + struct wdsp_cmpnt_ops *ops); + struct device *(*get_dev_for_cmpnt)(struct device *wdsp_dev, + enum wdsp_cmpnt_type type); + int (*signal_handler)(struct device *wdsp_dev, + enum wdsp_signal signal, void *arg); + int (*vote_for_dsp)(struct device *wdsp_dev, bool vote); + int (*suspend)(struct device *wdsp_dev); + int (*resume)(struct device *wdsp_dev); +}; + +#endif /* end of __WCD_DSP_MGR_H__ */ diff --git a/include/sound/wcd-spi.h b/include/sound/wcd-spi.h new file mode 100644 index 0000000000000000000000000000000000000000..1fff58d727a1d4050c146da84efc47753dd4b0b7 --- /dev/null +++ b/include/sound/wcd-spi.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD_SPI_H__ +#define __WCD_SPI_H__ + +struct wcd_spi_msg { + /* + * Caller's buffer pointer that holds data to + * be transmitted in case of data_write and + * data to be copied to in case of data_read. + */ + void *data; + + /* Length of data to write/read */ + size_t len; + + /* + * Address in remote memory to write to + * or read from. + */ + u32 remote_addr; + + /* Bitmask of flags, currently unused */ + u32 flags; +}; + +#ifdef CONFIG_SND_SOC_WCD_SPI + +int wcd_spi_data_write(struct spi_device *spi, struct wcd_spi_msg *msg); +int wcd_spi_data_read(struct spi_device *spi, struct wcd_spi_msg *msg); + +#else + +int wcd_spi_data_write(struct spi_device *spi, struct wcd_spi_msg *msg) +{ + return -ENODEV; +} + +int wcd_spi_data_read(struct spi_device *spi, struct wcd_spi_msg *msg) +{ + return -ENODEV; +} + +#endif /* End of CONFIG_SND_SOC_WCD_SPI */ + +#endif /* End of __WCD_SPI_H__ */ diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 6ce3d167c942d7c8d2f07041ea64cd4aac633d91..3e2449ba9d0e7f22694f0eb8e493b81f4e8c6694 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -22,6 +22,7 @@ header-y += netfilter_ipv4/ header-y += netfilter_ipv6/ header-y += usb/ header-y += wimax/ +header-y += mfd/ header-y += msm_ipa.h genhdr-y += version.h diff --git a/include/uapi/linux/mfd/Kbuild b/include/uapi/linux/mfd/Kbuild new file mode 100644 index 0000000000000000000000000000000000000000..b3981793c3cdbaa30bfe9af0eff4a828791d48c3 --- /dev/null +++ b/include/uapi/linux/mfd/Kbuild @@ -0,0 +1,2 @@ +header-y += msm-adie-codec.h +header-y += wcd9xxx/ diff --git a/include/uapi/linux/mfd/msm-adie-codec.h b/include/uapi/linux/mfd/msm-adie-codec.h new file mode 100644 index 0000000000000000000000000000000000000000..3f523c00d926655c39fdf18d3ae53c6ef72d6fdc --- /dev/null +++ b/include/uapi/linux/mfd/msm-adie-codec.h @@ -0,0 +1,147 @@ +#ifndef __UAPI_MFD_MSM_ADIE_CODEC_H +#define __UAPI_MFD_MSM_ADIE_CODEC_H + +#include + +/* Value Represents a entry */ +#define ADIE_CODEC_ACTION_ENTRY 0x1 +/* Value representing a delay wait */ +#define ADIE_CODEC_ACTION_DELAY_WAIT 0x2 +/* Value representing a stage reached */ +#define ADIE_CODEC_ACTION_STAGE_REACHED 0x3 + +/* This value is the state after the client sets the path */ +#define ADIE_CODEC_PATH_OFF 0x0050 + +/* State to which client asks the drv to proceed to where it can + * set up the clocks and 0-fill PCM buffers + */ +#define ADIE_CODEC_DIGITAL_READY 0x0100 + +/* State to which client asks the drv to proceed to where it can + * start sending data after internal steady state delay + */ +#define ADIE_CODEC_DIGITAL_ANALOG_READY 0x1000 + + +/* Client Asks adie to switch off the Analog portion of the + * the internal codec. After the use of this path + */ +#define ADIE_CODEC_ANALOG_OFF 0x0750 + + +/* Client Asks adie to switch off the digital portion of the + * the internal codec. After switching off the analog portion. + * + * 0-fill PCM may or maynot be sent at this point + * + */ +#define ADIE_CODEC_DIGITAL_OFF 0x0600 + +/* State to which client asks the drv to write the default values + * to the registers + */ +#define ADIE_CODEC_FLASH_IMAGE 0x0001 + +/* Path type */ +#define ADIE_CODEC_RX 0 +#define ADIE_CODEC_TX 1 +#define ADIE_CODEC_LB 3 +#define ADIE_CODEC_MAX 4 + +#define ADIE_CODEC_PACK_ENTRY(reg, mask, val) ((val)|(mask << 8)|(reg << 16)) + +#define ADIE_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0); + +struct adie_codec_action_unit { + u32 type; + u32 action; +}; + +struct adie_codec_hwsetting_entry { + struct adie_codec_action_unit *actions; + u32 action_sz; + u32 freq_plan; + u32 osr; + /* u32 VolMask; + * u32 SidetoneMask; + */ +}; + +struct adie_codec_dev_profile { + u32 path_type; /* RX or TX */ + u32 setting_sz; + struct adie_codec_hwsetting_entry *settings; +}; + +struct adie_codec_register { + u8 reg; + u8 mask; + u8 val; +}; + +struct adie_codec_register_image { + struct adie_codec_register *regs; + u32 img_sz; +}; + +struct adie_codec_path; + +struct adie_codec_anc_data { + u32 size; + u32 writes[]; +}; + +struct adie_codec_operations { + int codec_id; + int (*codec_open)(struct adie_codec_dev_profile *profile, + struct adie_codec_path **path_pptr); + int (*codec_close)(struct adie_codec_path *path_ptr); + int (*codec_setpath)(struct adie_codec_path *path_ptr, + u32 freq_plan, u32 osr); + int (*codec_proceed_stage)(struct adie_codec_path *path_ptr, + u32 state); + u32 (*codec_freq_supported)(struct adie_codec_dev_profile *profile, + u32 requested_freq); + int (*codec_enable_sidetone)(struct adie_codec_path *rx_path_ptr, + u32 enable); + int (*codec_enable_anc)(struct adie_codec_path *rx_path_ptr, + u32 enable, struct adie_codec_anc_data *calibration_writes); + int (*codec_set_device_digital_volume)( + struct adie_codec_path *path_ptr, + u32 num_channels, + u32 vol_percentage); + + int (*codec_set_device_analog_volume)(struct adie_codec_path *path_ptr, + u32 num_channels, + u32 volume); + int (*codec_set_master_mode)(struct adie_codec_path *path_ptr, + u8 master); +}; + +int adie_codec_register_codec_operations( + const struct adie_codec_operations *codec_ops); +int adie_codec_open(struct adie_codec_dev_profile *profile, + struct adie_codec_path **path_pptr); +int adie_codec_setpath(struct adie_codec_path *path_ptr, + u32 freq_plan, u32 osr); +int adie_codec_proceed_stage(struct adie_codec_path *path_ptr, u32 state); +int adie_codec_close(struct adie_codec_path *path_ptr); +u32 adie_codec_freq_supported(struct adie_codec_dev_profile *profile, + u32 requested_freq); +int adie_codec_enable_sidetone(struct adie_codec_path *rx_path_ptr, u32 enable); +int adie_codec_enable_anc(struct adie_codec_path *rx_path_ptr, u32 enable, + struct adie_codec_anc_data *calibration_writes); +int adie_codec_set_device_digital_volume(struct adie_codec_path *path_ptr, + u32 num_channels, u32 vol_percentage /* in percentage */); + +int adie_codec_set_device_analog_volume(struct adie_codec_path *path_ptr, + u32 num_channels, u32 volume /* in percentage */); + +int adie_codec_set_master_mode(struct adie_codec_path *path_ptr, u8 master); +#endif diff --git a/include/uapi/linux/mfd/wcd9xxx/Kbuild b/include/uapi/linux/mfd/wcd9xxx/Kbuild new file mode 100644 index 0000000000000000000000000000000000000000..8e55965bbe7ede83a4f163e70e1cfda693a8a4dd --- /dev/null +++ b/include/uapi/linux/mfd/wcd9xxx/Kbuild @@ -0,0 +1,2 @@ +header-y += wcd9xxx_registers.h +header-y += wcd9320_registers.h diff --git a/include/uapi/linux/mfd/wcd9xxx/wcd9320_registers.h b/include/uapi/linux/mfd/wcd9xxx/wcd9320_registers.h new file mode 100644 index 0000000000000000000000000000000000000000..63ab624195596d0ec5744223ba03504094102942 --- /dev/null +++ b/include/uapi/linux/mfd/wcd9xxx/wcd9320_registers.h @@ -0,0 +1,1399 @@ +#ifndef WCD9320_REGISTERS_H +#define WCD9320_REGISTERS_H + +#include + +#define TAIKO_A_CHIP_CTL WCD9XXX_A_CHIP_CTL +#define TAIKO_A_CHIP_CTL__POR WCD9XXX_A_CHIP_CTL__POR +#define TAIKO_A_CHIP_STATUS WCD9XXX_A_CHIP_STATUS +#define TAIKO_A_CHIP_STATUS__POR WCD9XXX_A_CHIP_STATUS__POR +#define TAIKO_A_CHIP_ID_BYTE_0 WCD9XXX_A_CHIP_ID_BYTE_0 +#define TAIKO_A_CHIP_ID_BYTE_0__POR WCD9XXX_A_CHIP_ID_BYTE_0__POR +#define TAIKO_A_CHIP_ID_BYTE_1 WCD9XXX_A_CHIP_ID_BYTE_1 +#define TAIKO_A_CHIP_ID_BYTE_1__POR WCD9XXX_A_CHIP_ID_BYTE_1__POR +#define TAIKO_A_CHIP_ID_BYTE_2 WCD9XXX_A_CHIP_ID_BYTE_2 +#define TAIKO_A_CHIP_ID_BYTE_2__POR WCD9XXX_A_CHIP_ID_BYTE_2__POR +#define TAIKO_A_CHIP_ID_BYTE_3 WCD9XXX_A_CHIP_ID_BYTE_3 +#define TAIKO_A_CHIP_ID_BYTE_3__POR WCD9XXX_A_CHIP_ID_BYTE_3__POR +#define TAIKO_A_CHIP_VERSION WCD9XXX_A_CHIP_VERSION +#define TAIKO_A_CHIP_VERSION__POR WCD9XXX_A_CHIP_VERSION__POR +#define TAIKO_A_SB_VERSION WCD9XXX_A_SB_VERSION +#define TAIKO_A_SB_VERSION__POR WCD9XXX_A_SB_VERSION__POR +#define TAIKO_A_SLAVE_ID_1 WCD9XXX_A_SLAVE_ID_1 +#define TAIKO_A_SLAVE_ID_1__POR WCD9XXX_A_SLAVE_ID_1__POR +#define TAIKO_A_SLAVE_ID_2 WCD9XXX_A_SLAVE_ID_2 +#define TAIKO_A_SLAVE_ID_2__POR WCD9XXX_A_SLAVE_ID_2__POR +#define TAIKO_A_SLAVE_ID_3 WCD9XXX_A_SLAVE_ID_3 +#define TAIKO_A_SLAVE_ID_3__POR WCD9XXX_A_SLAVE_ID_3__POR +#define TAIKO_A_PIN_CTL_OE0 (0x010) +#define TAIKO_A_PIN_CTL_OE0__POR (0x00) +#define TAIKO_A_PIN_CTL_OE1 (0x011) +#define TAIKO_A_PIN_CTL_OE1__POR (0x00) +#define TAIKO_A_PIN_CTL_DATA0 (0x012) +#define TAIKO_A_PIN_CTL_DATA0__POR (0x00) +#define TAIKO_A_PIN_CTL_DATA1 (0x013) +#define TAIKO_A_PIN_CTL_DATA1__POR (0x00) +#define TAIKO_A_HDRIVE_GENERIC (0x018) +#define TAIKO_A_HDRIVE_GENERIC__POR (0x00) +#define TAIKO_A_HDRIVE_OVERRIDE (0x019) +#define TAIKO_A_HDRIVE_OVERRIDE__POR (0x08) +#define TAIKO_A_ANA_CSR_WAIT_STATE (0x020) +#define TAIKO_A_ANA_CSR_WAIT_STATE__POR (0x44) +#define TAIKO_A_PROCESS_MONITOR_CTL0 (0x040) +#define TAIKO_A_PROCESS_MONITOR_CTL0__POR (0x80) +#define TAIKO_A_PROCESS_MONITOR_CTL1 (0x041) +#define TAIKO_A_PROCESS_MONITOR_CTL1__POR (0x00) +#define TAIKO_A_PROCESS_MONITOR_CTL2 (0x042) +#define TAIKO_A_PROCESS_MONITOR_CTL2__POR (0x00) +#define TAIKO_A_PROCESS_MONITOR_CTL3 (0x043) +#define TAIKO_A_PROCESS_MONITOR_CTL3__POR (0x01) +#define TAIKO_A_QFUSE_CTL (0x048) +#define TAIKO_A_QFUSE_CTL__POR (0x00) +#define TAIKO_A_QFUSE_STATUS (0x049) +#define TAIKO_A_QFUSE_STATUS__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT0 (0x04A) +#define TAIKO_A_QFUSE_DATA_OUT0__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT1 (0x04B) +#define TAIKO_A_QFUSE_DATA_OUT1__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT2 (0x04C) +#define TAIKO_A_QFUSE_DATA_OUT2__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT3 (0x04D) +#define TAIKO_A_QFUSE_DATA_OUT3__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT4 (0x04E) +#define TAIKO_A_QFUSE_DATA_OUT4__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT5 (0x04F) +#define TAIKO_A_QFUSE_DATA_OUT5__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT6 (0x050) +#define TAIKO_A_QFUSE_DATA_OUT6__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT7 (0x051) +#define TAIKO_A_QFUSE_DATA_OUT7__POR (0x00) +#define TAIKO_A_CDC_CTL WCD9XXX_A_CDC_CTL +#define TAIKO_A_CDC_CTL__POR WCD9XXX_A_CDC_CTL__POR +#define TAIKO_A_LEAKAGE_CTL WCD9XXX_A_LEAKAGE_CTL +#define TAIKO_A_LEAKAGE_CTL__POR WCD9XXX_A_LEAKAGE_CTL__POR +#define TAIKO_A_INTR_MODE (0x090) +#define TAIKO_A_INTR_MODE__POR (0x00) +#define TAIKO_A_INTR_MASK0 (0x094) +#define TAIKO_A_INTR_MASK0__POR (0xFF) +#define TAIKO_A_INTR_MASK1 (0x095) +#define TAIKO_A_INTR_MASK1__POR (0xFF) +#define TAIKO_A_INTR_MASK2 (0x096) +#define TAIKO_A_INTR_MASK2__POR (0x3F) +#define TAIKO_A_INTR_MASK3 (0x097) +#define TAIKO_A_INTR_MASK3__POR (0x3F) +#define TAIKO_A_INTR_STATUS0 (0x098) +#define TAIKO_A_INTR_STATUS0__POR (0x00) +#define TAIKO_A_INTR_STATUS1 (0x099) +#define TAIKO_A_INTR_STATUS1__POR (0x00) +#define TAIKO_A_INTR_STATUS2 (0x09A) +#define TAIKO_A_INTR_STATUS2__POR (0x00) +#define TAIKO_A_INTR_STATUS3 (0x09B) +#define TAIKO_A_INTR_STATUS3__POR (0x00) +#define TAIKO_A_INTR_CLEAR0 (0x09C) +#define TAIKO_A_INTR_CLEAR0__POR (0x00) +#define TAIKO_A_INTR_CLEAR1 (0x09D) +#define TAIKO_A_INTR_CLEAR1__POR (0x00) +#define TAIKO_A_INTR_CLEAR2 (0x09E) +#define TAIKO_A_INTR_CLEAR2__POR (0x00) +#define TAIKO_A_INTR_CLEAR3 (0x09F) +#define TAIKO_A_INTR_CLEAR3__POR (0x00) +#define TAIKO_A_INTR_LEVEL0 (0x0A0) +#define TAIKO_A_INTR_LEVEL0__POR (0x01) +#define TAIKO_A_INTR_LEVEL1 (0x0A1) +#define TAIKO_A_INTR_LEVEL1__POR (0x00) +#define TAIKO_A_INTR_LEVEL2 (0x0A2) +#define TAIKO_A_INTR_LEVEL2__POR (0x00) +#define TAIKO_A_INTR_LEVEL3 (0x0A3) +#define TAIKO_A_INTR_LEVEL3__POR (0x00) +#define TAIKO_A_INTR_TEST0 (0x0A4) +#define TAIKO_A_INTR_TEST0__POR (0x00) +#define TAIKO_A_INTR_TEST1 (0x0A5) +#define TAIKO_A_INTR_TEST1__POR (0x00) +#define TAIKO_A_INTR_TEST2 (0x0A6) +#define TAIKO_A_INTR_TEST2__POR (0x00) +#define TAIKO_A_INTR_TEST3 (0x0A7) +#define TAIKO_A_INTR_TEST3__POR (0x00) +#define TAIKO_A_INTR_SET0 (0x0A8) +#define TAIKO_A_INTR_SET0__POR (0x00) +#define TAIKO_A_INTR_SET1 (0x0A9) +#define TAIKO_A_INTR_SET1__POR (0x00) +#define TAIKO_A_INTR_SET2 (0x0AA) +#define TAIKO_A_INTR_SET2__POR (0x00) +#define TAIKO_A_INTR_SET3 (0x0AB) +#define TAIKO_A_INTR_SET3__POR (0x00) +#define TAIKO_A_INTR_DESTN0 (0x0AC) +#define TAIKO_A_INTR_DESTN0__POR (0x00) +#define TAIKO_A_INTR_DESTN1 (0x0AD) +#define TAIKO_A_INTR_DESTN1__POR (0x00) +#define TAIKO_A_INTR_DESTN2 (0x0AE) +#define TAIKO_A_INTR_DESTN2__POR (0x00) +#define TAIKO_A_INTR_DESTN3 (0x0AF) +#define TAIKO_A_INTR_DESTN3__POR (0x00) +#define TAIKO_A_CDC_TX_I2S_SCK_MODE (0x0C0) +#define TAIKO_A_CDC_TX_I2S_SCK_MODE__POR (0x00) +#define TAIKO_A_CDC_TX_I2S_WS_MODE (0x0C1) +#define TAIKO_A_CDC_TX_I2S_WS_MODE__POR (0x00) +#define TAIKO_A_CDC_DMIC_DATA0_MODE (0x0C4) +#define TAIKO_A_CDC_DMIC_DATA0_MODE__POR (0x00) +#define TAIKO_A_CDC_DMIC_CLK0_MODE (0x0C5) +#define TAIKO_A_CDC_DMIC_CLK0_MODE__POR (0x00) +#define TAIKO_A_CDC_DMIC_DATA1_MODE (0x0C6) +#define TAIKO_A_CDC_DMIC_DATA1_MODE__POR (0x00) +#define TAIKO_A_CDC_DMIC_CLK1_MODE (0x0C7) +#define TAIKO_A_CDC_DMIC_CLK1_MODE__POR (0x00) +#define TAIKO_A_CDC_RX_I2S_SCK_MODE (0x0C8) +#define TAIKO_A_CDC_RX_I2S_SCK_MODE__POR (0x00) +#define TAIKO_A_CDC_RX_I2S_WS_MODE (0x0C9) +#define TAIKO_A_CDC_RX_I2S_WS_MODE__POR (0x00) +#define TAIKO_A_CDC_DMIC_DATA2_MODE (0x0CA) +#define TAIKO_A_CDC_DMIC_DATA2_MODE__POR (0x00) +#define TAIKO_A_CDC_DMIC_CLK2_MODE (0x0CB) +#define TAIKO_A_CDC_DMIC_CLK2_MODE__POR (0x00) +#define TAIKO_A_CDC_INTR1_MODE (0x0CC) +#define TAIKO_A_CDC_INTR1_MODE__POR (0x00) +#define TAIKO_A_CDC_SB_NRZ_SEL_MODE (0x0CD) +#define TAIKO_A_CDC_SB_NRZ_SEL_MODE__POR (0x00) +#define TAIKO_A_CDC_INTR2_MODE (0x0CE) +#define TAIKO_A_CDC_INTR2_MODE__POR (0x00) +#define TAIKO_A_CDC_RF_PA_ON_MODE (0x0CF) +#define TAIKO_A_CDC_RF_PA_ON_MODE__POR (0x00) +#define TAIKO_A_BIAS_REF_CTL (0x100) +#define TAIKO_A_BIAS_REF_CTL__POR (0x1C) +#define TAIKO_A_BIAS_CENTRAL_BG_CTL (0x101) +#define TAIKO_A_BIAS_CENTRAL_BG_CTL__POR (0x50) +#define TAIKO_A_BIAS_PRECHRG_CTL (0x102) +#define TAIKO_A_BIAS_PRECHRG_CTL__POR (0x07) +#define TAIKO_A_BIAS_CURR_CTL_1 (0x103) +#define TAIKO_A_BIAS_CURR_CTL_1__POR (0x52) +#define TAIKO_A_BIAS_CURR_CTL_2 (0x104) +#define TAIKO_A_BIAS_CURR_CTL_2__POR (0x00) +#define TAIKO_A_BIAS_OSC_BG_CTL (0x105) +#define TAIKO_A_BIAS_OSC_BG_CTL__POR (0x16) +#define TAIKO_A_CLK_BUFF_EN1 (0x108) +#define TAIKO_A_CLK_BUFF_EN1__POR (0x04) +#define TAIKO_A_CLK_BUFF_EN2 (0x109) +#define TAIKO_A_CLK_BUFF_EN2__POR (0x02) +#define TAIKO_A_LDO_H_MODE_1 (0x110) +#define TAIKO_A_LDO_H_MODE_1__POR (0x65) +#define TAIKO_A_LDO_H_MODE_2 (0x111) +#define TAIKO_A_LDO_H_MODE_2__POR (0xA8) +#define TAIKO_A_LDO_H_LOOP_CTL (0x112) +#define TAIKO_A_LDO_H_LOOP_CTL__POR (0x6B) +#define TAIKO_A_LDO_H_COMP_1 (0x113) +#define TAIKO_A_LDO_H_COMP_1__POR (0x84) +#define TAIKO_A_LDO_H_COMP_2 (0x114) +#define TAIKO_A_LDO_H_COMP_2__POR (0xE0) +#define TAIKO_A_LDO_H_BIAS_1 (0x115) +#define TAIKO_A_LDO_H_BIAS_1__POR (0x6D) +#define TAIKO_A_LDO_H_BIAS_2 (0x116) +#define TAIKO_A_LDO_H_BIAS_2__POR (0xA5) +#define TAIKO_A_LDO_H_BIAS_3 (0x117) +#define TAIKO_A_LDO_H_BIAS_3__POR (0x60) +#define TAIKO_A_VBAT_CLK (0x118) +#define TAIKO_A_VBAT_CLK__POR (0x03) +#define TAIKO_A_VBAT_LOOP (0x119) +#define TAIKO_A_VBAT_LOOP__POR (0x02) +#define TAIKO_A_VBAT_REF (0x11A) +#define TAIKO_A_VBAT_REF__POR (0x20) +#define TAIKO_A_VBAT_ADC_TEST (0x11B) +#define TAIKO_A_VBAT_ADC_TEST__POR (0x00) +#define TAIKO_A_VBAT_FE (0x11C) +#define TAIKO_A_VBAT_FE__POR (0x48) +#define TAIKO_A_VBAT_BIAS_1 (0x11D) +#define TAIKO_A_VBAT_BIAS_1__POR (0x03) +#define TAIKO_A_VBAT_BIAS_2 (0x11E) +#define TAIKO_A_VBAT_BIAS_2__POR (0x00) +#define TAIKO_A_VBAT_ADC_DATA_MSB (0x11F) +#define TAIKO_A_VBAT_ADC_DATA_MSB__POR (0x00) +#define TAIKO_A_VBAT_ADC_DATA_LSB (0x120) +#define TAIKO_A_VBAT_ADC_DATA_LSB__POR (0x00) +#define TAIKO_A_MICB_CFILT_1_CTL (0x128) +#define TAIKO_A_MICB_CFILT_1_CTL__POR (0x40) +#define TAIKO_A_MICB_CFILT_1_VAL (0x129) +#define TAIKO_A_MICB_CFILT_1_VAL__POR (0x80) +#define TAIKO_A_MICB_CFILT_1_PRECHRG (0x12A) +#define TAIKO_A_MICB_CFILT_1_PRECHRG__POR (0x38) +#define TAIKO_A_MICB_1_CTL (0x12B) +#define TAIKO_A_MICB_1_CTL__POR (0x16) +#define TAIKO_A_MICB_1_INT_RBIAS (0x12C) +#define TAIKO_A_MICB_1_INT_RBIAS__POR (0x24) +#define TAIKO_A_MICB_1_MBHC (0x12D) +#define TAIKO_A_MICB_1_MBHC__POR (0x01) +#define TAIKO_A_MICB_CFILT_2_CTL (0x12E) +#define TAIKO_A_MICB_CFILT_2_CTL__POR (0x40) +#define TAIKO_A_MICB_CFILT_2_VAL (0x12F) +#define TAIKO_A_MICB_CFILT_2_VAL__POR (0x80) +#define TAIKO_A_MICB_CFILT_2_PRECHRG (0x130) +#define TAIKO_A_MICB_CFILT_2_PRECHRG__POR (0x38) +#define TAIKO_A_MICB_2_CTL (0x131) +#define TAIKO_A_MICB_2_CTL__POR (0x16) +#define TAIKO_A_MICB_2_INT_RBIAS (0x132) +#define TAIKO_A_MICB_2_INT_RBIAS__POR (0x24) +#define TAIKO_A_MICB_2_MBHC (0x133) +#define TAIKO_A_MICB_2_MBHC__POR (0x02) +#define TAIKO_A_MICB_CFILT_3_CTL (0x134) +#define TAIKO_A_MICB_CFILT_3_CTL__POR (0x40) +#define TAIKO_A_MICB_CFILT_3_VAL (0x135) +#define TAIKO_A_MICB_CFILT_3_VAL__POR (0x80) +#define TAIKO_A_MICB_CFILT_3_PRECHRG (0x136) +#define TAIKO_A_MICB_CFILT_3_PRECHRG__POR (0x38) +#define TAIKO_A_MICB_3_CTL (0x137) +#define TAIKO_A_MICB_3_CTL__POR (0x16) +#define TAIKO_A_MICB_3_INT_RBIAS (0x138) +#define TAIKO_A_MICB_3_INT_RBIAS__POR (0x24) +#define TAIKO_A_MICB_3_MBHC (0x139) +#define TAIKO_A_MICB_3_MBHC__POR (0x00) +#define TAIKO_A_MICB_4_CTL (0x13D) +#define TAIKO_A_MICB_4_CTL__POR (0x16) +#define TAIKO_A_MICB_4_INT_RBIAS (0x13E) +#define TAIKO_A_MICB_4_INT_RBIAS__POR (0x24) +#define TAIKO_A_MICB_4_MBHC (0x13F) +#define TAIKO_A_MICB_4_MBHC__POR (0x01) +#define TAIKO_A_MBHC_INSERT_DETECT (0x14A) +#define TAIKO_A_MBHC_INSERT_DETECT__POR (0x00) +#define TAIKO_A_MBHC_INSERT_DET_STATUS (0x14B) +#define TAIKO_A_MBHC_INSERT_DET_STATUS__POR (0x00) +#define TAIKO_A_TX_COM_BIAS (0x14C) +#define TAIKO_A_TX_COM_BIAS__POR (0xF0) +#define TAIKO_A_MBHC_SCALING_MUX_1 (0x14E) +#define TAIKO_A_MBHC_SCALING_MUX_1__POR (0x00) +#define TAIKO_A_MBHC_SCALING_MUX_2 (0x14F) +#define TAIKO_A_MBHC_SCALING_MUX_2__POR (0x80) +#define TAIKO_A_MAD_ANA_CTRL (0x150) +#define TAIKO_A_MAD_ANA_CTRL__POR (0xF1) +#define TAIKO_A_TX_SUP_SWITCH_CTRL_1 (0x151) +#define TAIKO_A_TX_SUP_SWITCH_CTRL_1__POR (0x00) +#define TAIKO_A_TX_SUP_SWITCH_CTRL_2 (0x152) +#define TAIKO_A_TX_SUP_SWITCH_CTRL_2__POR (0x80) +#define TAIKO_A_TX_1_2_EN (0x153) +#define TAIKO_A_TX_1_2_EN__POR (0x00) +#define TAIKO_A_TX_1_2_TEST_EN (0x154) +#define TAIKO_A_TX_1_2_TEST_EN__POR (0xCC) +#define TAIKO_A_TX_1_2_ADC_CH1 (0x155) +#define TAIKO_A_TX_1_2_ADC_CH1__POR (0x44) +#define TAIKO_A_TX_1_2_ADC_CH2 (0x156) +#define TAIKO_A_TX_1_2_ADC_CH2__POR (0x44) +#define TAIKO_A_TX_1_2_ATEST_REFCTRL (0x157) +#define TAIKO_A_TX_1_2_ATEST_REFCTRL__POR (0x00) +#define TAIKO_A_TX_1_2_TEST_CTL (0x158) +#define TAIKO_A_TX_1_2_TEST_CTL__POR (0x38) +#define TAIKO_A_TX_1_2_TEST_BLOCK_EN (0x159) +#define TAIKO_A_TX_1_2_TEST_BLOCK_EN__POR (0xFC) +#define TAIKO_A_TX_1_2_TXFE_CLKDIV (0x15A) +#define TAIKO_A_TX_1_2_TXFE_CLKDIV__POR (0x55) +#define TAIKO_A_TX_1_2_SAR_ERR_CH1 (0x15B) +#define TAIKO_A_TX_1_2_SAR_ERR_CH1__POR (0x00) +#define TAIKO_A_TX_1_2_SAR_ERR_CH2 (0x15C) +#define TAIKO_A_TX_1_2_SAR_ERR_CH2__POR (0x00) +#define TAIKO_A_TX_3_4_EN (0x15D) +#define TAIKO_A_TX_3_4_EN__POR (0x00) +#define TAIKO_A_TX_3_4_TEST_EN (0x15E) +#define TAIKO_A_TX_3_4_TEST_EN__POR (0xCC) +#define TAIKO_A_TX_3_4_ADC_CH3 (0x15F) +#define TAIKO_A_TX_3_4_ADC_CH3__POR (0x44) +#define TAIKO_A_TX_3_4_ADC_CH4 (0x160) +#define TAIKO_A_TX_3_4_ADC_CH4__POR (0x44) +#define TAIKO_A_TX_3_4_ATEST_REFCTRL (0x161) +#define TAIKO_A_TX_3_4_ATEST_REFCTRL__POR (0x00) +#define TAIKO_A_TX_3_4_TEST_CTL (0x162) +#define TAIKO_A_TX_3_4_TEST_CTL__POR (0x38) +#define TAIKO_A_TX_3_4_TEST_BLOCK_EN (0x163) +#define TAIKO_A_TX_3_4_TEST_BLOCK_EN__POR (0xFC) +#define TAIKO_A_TX_3_4_TXFE_CKDIV (0x164) +#define TAIKO_A_TX_3_4_TXFE_CKDIV__POR (0x55) +#define TAIKO_A_TX_3_4_SAR_ERR_CH3 (0x165) +#define TAIKO_A_TX_3_4_SAR_ERR_CH3__POR (0x00) +#define TAIKO_A_TX_3_4_SAR_ERR_CH4 (0x166) +#define TAIKO_A_TX_3_4_SAR_ERR_CH4__POR (0x00) +#define TAIKO_A_TX_5_6_EN (0x167) +#define TAIKO_A_TX_5_6_EN__POR (0x11) +#define TAIKO_A_TX_5_6_TEST_EN (0x168) +#define TAIKO_A_TX_5_6_TEST_EN__POR (0xCC) +#define TAIKO_A_TX_5_6_ADC_CH5 (0x169) +#define TAIKO_A_TX_5_6_ADC_CH5__POR (0x44) +#define TAIKO_A_TX_5_6_ADC_CH6 (0x16A) +#define TAIKO_A_TX_5_6_ADC_CH6__POR (0x44) +#define TAIKO_A_TX_5_6_ATEST_REFCTRL (0x16B) +#define TAIKO_A_TX_5_6_ATEST_REFCTRL__POR (0x00) +#define TAIKO_A_TX_5_6_TEST_CTL (0x16C) +#define TAIKO_A_TX_5_6_TEST_CTL__POR (0x38) +#define TAIKO_A_TX_5_6_TEST_BLOCK_EN (0x16D) +#define TAIKO_A_TX_5_6_TEST_BLOCK_EN__POR (0xFC) +#define TAIKO_A_TX_5_6_TXFE_CKDIV (0x16E) +#define TAIKO_A_TX_5_6_TXFE_CKDIV__POR (0x55) +#define TAIKO_A_TX_5_6_SAR_ERR_CH5 (0x16F) +#define TAIKO_A_TX_5_6_SAR_ERR_CH5__POR (0x00) +#define TAIKO_A_TX_5_6_SAR_ERR_CH6 (0x170) +#define TAIKO_A_TX_5_6_SAR_ERR_CH6__POR (0x00) +#define TAIKO_A_TX_7_MBHC_EN (0x171) +#define TAIKO_A_TX_7_MBHC_EN__POR (0x0C) +#define TAIKO_A_TX_7_MBHC_ATEST_REFCTRL (0x172) +#define TAIKO_A_TX_7_MBHC_ATEST_REFCTRL__POR (0x00) +#define TAIKO_A_TX_7_MBHC_ADC (0x173) +#define TAIKO_A_TX_7_MBHC_ADC__POR (0x44) +#define TAIKO_A_TX_7_MBHC_TEST_CTL (0x174) +#define TAIKO_A_TX_7_MBHC_TEST_CTL__POR (0x38) +#define TAIKO_A_TX_7_MBHC_SAR_ERR (0x175) +#define TAIKO_A_TX_7_MBHC_SAR_ERR__POR (0x00) +#define TAIKO_A_TX_7_TXFE_CLKDIV (0x176) +#define TAIKO_A_TX_7_TXFE_CLKDIV__POR (0x0B) +#define TAIKO_A_BUCK_MODE_1 (0x181) +#define TAIKO_A_BUCK_MODE_1__POR (0x21) +#define TAIKO_A_BUCK_MODE_2 (0x182) +#define TAIKO_A_BUCK_MODE_2__POR (0xFF) +#define TAIKO_A_BUCK_MODE_3 (0x183) +#define TAIKO_A_BUCK_MODE_3__POR (0xCC) +#define TAIKO_A_BUCK_MODE_4 (0x184) +#define TAIKO_A_BUCK_MODE_4__POR (0x3A) +#define TAIKO_A_BUCK_MODE_5 (0x185) +#define TAIKO_A_BUCK_MODE_5__POR (0x00) +#define TAIKO_A_BUCK_CTRL_VCL_1 (0x186) +#define TAIKO_A_BUCK_CTRL_VCL_1__POR (0x48) +#define TAIKO_A_BUCK_CTRL_VCL_2 (0x187) +#define TAIKO_A_BUCK_CTRL_VCL_2__POR (0xA3) +#define TAIKO_A_BUCK_CTRL_VCL_3 (0x188) +#define TAIKO_A_BUCK_CTRL_VCL_3__POR (0x82) +#define TAIKO_A_BUCK_CTRL_CCL_1 (0x189) +#define TAIKO_A_BUCK_CTRL_CCL_1__POR (0xAB) +#define TAIKO_A_BUCK_CTRL_CCL_2 (0x18A) +#define TAIKO_A_BUCK_CTRL_CCL_2__POR (0xDC) +#define TAIKO_A_BUCK_CTRL_CCL_3 (0x18B) +#define TAIKO_A_BUCK_CTRL_CCL_3__POR (0x6A) +#define TAIKO_A_BUCK_CTRL_CCL_4 (0x18C) +#define TAIKO_A_BUCK_CTRL_CCL_4__POR (0x58) +#define TAIKO_A_BUCK_CTRL_PWM_DRVR_1 (0x18D) +#define TAIKO_A_BUCK_CTRL_PWM_DRVR_1__POR (0x50) +#define TAIKO_A_BUCK_CTRL_PWM_DRVR_2 (0x18E) +#define TAIKO_A_BUCK_CTRL_PWM_DRVR_2__POR (0x64) +#define TAIKO_A_BUCK_CTRL_PWM_DRVR_3 (0x18F) +#define TAIKO_A_BUCK_CTRL_PWM_DRVR_3__POR (0x77) +#define TAIKO_A_BUCK_TMUX_A_D (0x190) +#define TAIKO_A_BUCK_TMUX_A_D__POR (0x00) +#define TAIKO_A_NCP_BUCKREF (0x191) +#define TAIKO_A_NCP_BUCKREF__POR (0x00) +#define TAIKO_A_NCP_EN (0x192) +#define TAIKO_A_NCP_EN__POR (0xFE) +#define TAIKO_A_NCP_CLK (0x193) +#define TAIKO_A_NCP_CLK__POR (0x94) +#define TAIKO_A_NCP_STATIC (0x194) +#define TAIKO_A_NCP_STATIC__POR (0x28) +#define TAIKO_A_NCP_VTH_LOW (0x195) +#define TAIKO_A_NCP_VTH_LOW__POR (0x88) +#define TAIKO_A_NCP_VTH_HIGH (0x196) +#define TAIKO_A_NCP_VTH_HIGH__POR (0xA0) +#define TAIKO_A_NCP_ATEST (0x197) +#define TAIKO_A_NCP_ATEST__POR (0x00) +#define TAIKO_A_NCP_DTEST (0x198) +#define TAIKO_A_NCP_DTEST__POR (0x00) +#define TAIKO_A_NCP_DLY1 (0x199) +#define TAIKO_A_NCP_DLY1__POR (0x06) +#define TAIKO_A_NCP_DLY2 (0x19A) +#define TAIKO_A_NCP_DLY2__POR (0x06) +#define TAIKO_A_RX_AUX_SW_CTL (0x19B) +#define TAIKO_A_RX_AUX_SW_CTL__POR (0x00) +#define TAIKO_A_RX_PA_AUX_IN_CONN (0x19C) +#define TAIKO_A_RX_PA_AUX_IN_CONN__POR (0x00) +#define TAIKO_A_RX_COM_TIMER_DIV (0x19E) +#define TAIKO_A_RX_COM_TIMER_DIV__POR (0xE8) +#define TAIKO_A_RX_COM_OCP_CTL (0x19F) +#define TAIKO_A_RX_COM_OCP_CTL__POR (0x1F) +#define TAIKO_A_RX_COM_OCP_COUNT (0x1A0) +#define TAIKO_A_RX_COM_OCP_COUNT__POR (0x77) +#define TAIKO_A_RX_COM_DAC_CTL (0x1A1) +#define TAIKO_A_RX_COM_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_COM_BIAS (0x1A2) +#define TAIKO_A_RX_COM_BIAS__POR (0x00) +#define TAIKO_A_RX_HPH_AUTO_CHOP (0x1A4) +#define TAIKO_A_RX_HPH_AUTO_CHOP__POR (0x38) +#define TAIKO_A_RX_HPH_CHOP_CTL (0x1A5) +#define TAIKO_A_RX_HPH_CHOP_CTL__POR (0xB4) +#define TAIKO_A_RX_HPH_BIAS_PA (0x1A6) +#define TAIKO_A_RX_HPH_BIAS_PA__POR (0xAA) +#define TAIKO_A_RX_HPH_BIAS_LDO (0x1A7) +#define TAIKO_A_RX_HPH_BIAS_LDO__POR (0x87) +#define TAIKO_A_RX_HPH_BIAS_CNP (0x1A8) +#define TAIKO_A_RX_HPH_BIAS_CNP__POR (0x8A) +#define TAIKO_A_RX_HPH_BIAS_WG_OCP (0x1A9) +#define TAIKO_A_RX_HPH_BIAS_WG_OCP__POR (0x2A) +#define TAIKO_A_RX_HPH_OCP_CTL (0x1AA) +#define TAIKO_A_RX_HPH_OCP_CTL__POR (0x68) +#define TAIKO_A_RX_HPH_CNP_EN (0x1AB) +#define TAIKO_A_RX_HPH_CNP_EN__POR (0x80) +#define TAIKO_A_RX_HPH_CNP_WG_CTL (0x1AC) +#define TAIKO_A_RX_HPH_CNP_WG_CTL__POR (0xDE) +#define TAIKO_A_RX_HPH_CNP_WG_TIME (0x1AD) +#define TAIKO_A_RX_HPH_CNP_WG_TIME__POR (0x2A) +#define TAIKO_A_RX_HPH_L_GAIN (0x1AE) +#define TAIKO_A_RX_HPH_L_GAIN__POR (0x00) +#define TAIKO_A_RX_HPH_L_TEST (0x1AF) +#define TAIKO_A_RX_HPH_L_TEST__POR (0x00) +#define TAIKO_A_RX_HPH_L_PA_CTL (0x1B0) +#define TAIKO_A_RX_HPH_L_PA_CTL__POR (0x40) +#define TAIKO_A_RX_HPH_L_DAC_CTL (0x1B1) +#define TAIKO_A_RX_HPH_L_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_HPH_L_ATEST (0x1B2) +#define TAIKO_A_RX_HPH_L_ATEST__POR (0x00) +#define TAIKO_A_RX_HPH_L_STATUS (0x1B3) +#define TAIKO_A_RX_HPH_L_STATUS__POR (0x00) +#define TAIKO_A_RX_HPH_R_GAIN (0x1B4) +#define TAIKO_A_RX_HPH_R_GAIN__POR (0x00) +#define TAIKO_A_RX_HPH_R_TEST (0x1B5) +#define TAIKO_A_RX_HPH_R_TEST__POR (0x00) +#define TAIKO_A_RX_HPH_R_PA_CTL (0x1B6) +#define TAIKO_A_RX_HPH_R_PA_CTL__POR (0x40) +#define TAIKO_A_RX_HPH_R_DAC_CTL (0x1B7) +#define TAIKO_A_RX_HPH_R_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_HPH_R_ATEST (0x1B8) +#define TAIKO_A_RX_HPH_R_ATEST__POR (0x00) +#define TAIKO_A_RX_HPH_R_STATUS (0x1B9) +#define TAIKO_A_RX_HPH_R_STATUS__POR (0x00) +#define TAIKO_A_RX_EAR_BIAS_PA (0x1BA) +#define TAIKO_A_RX_EAR_BIAS_PA__POR (0xA6) +#define TAIKO_A_RX_EAR_BIAS_CMBUFF (0x1BB) +#define TAIKO_A_RX_EAR_BIAS_CMBUFF__POR (0xA0) +#define TAIKO_A_RX_EAR_EN (0x1BC) +#define TAIKO_A_RX_EAR_EN__POR (0x00) +#define TAIKO_A_RX_EAR_GAIN (0x1BD) +#define TAIKO_A_RX_EAR_GAIN__POR (0x02) +#define TAIKO_A_RX_EAR_CMBUFF (0x1BE) +#define TAIKO_A_RX_EAR_CMBUFF__POR (0x04) +#define TAIKO_A_RX_EAR_ICTL (0x1BF) +#define TAIKO_A_RX_EAR_ICTL__POR (0x40) +#define TAIKO_A_RX_EAR_CCOMP (0x1C0) +#define TAIKO_A_RX_EAR_CCOMP__POR (0x08) +#define TAIKO_A_RX_EAR_VCM (0x1C1) +#define TAIKO_A_RX_EAR_VCM__POR (0x03) +#define TAIKO_A_RX_EAR_CNP (0x1C2) +#define TAIKO_A_RX_EAR_CNP__POR (0xF2) +#define TAIKO_A_RX_EAR_DAC_CTL_ATEST (0x1C3) +#define TAIKO_A_RX_EAR_DAC_CTL_ATEST__POR (0x00) +#define TAIKO_A_RX_EAR_STATUS (0x1C5) +#define TAIKO_A_RX_EAR_STATUS__POR (0x04) +#define TAIKO_A_RX_LINE_BIAS_PA (0x1C6) +#define TAIKO_A_RX_LINE_BIAS_PA__POR (0xA8) +#define TAIKO_A_RX_BUCK_BIAS1 (0x1C7) +#define TAIKO_A_RX_BUCK_BIAS1__POR (0x42) +#define TAIKO_A_RX_BUCK_BIAS2 (0x1C8) +#define TAIKO_A_RX_BUCK_BIAS2__POR (0x84) +#define TAIKO_A_RX_LINE_COM (0x1C9) +#define TAIKO_A_RX_LINE_COM__POR (0x80) +#define TAIKO_A_RX_LINE_CNP_EN (0x1CA) +#define TAIKO_A_RX_LINE_CNP_EN__POR (0x00) +#define TAIKO_A_RX_LINE_CNP_WG_CTL (0x1CB) +#define TAIKO_A_RX_LINE_CNP_WG_CTL__POR (0x00) +#define TAIKO_A_RX_LINE_CNP_WG_TIME (0x1CC) +#define TAIKO_A_RX_LINE_CNP_WG_TIME__POR (0x04) +#define TAIKO_A_RX_LINE_1_GAIN (0x1CD) +#define TAIKO_A_RX_LINE_1_GAIN__POR (0x00) +#define TAIKO_A_RX_LINE_1_TEST (0x1CE) +#define TAIKO_A_RX_LINE_1_TEST__POR (0x00) +#define TAIKO_A_RX_LINE_1_DAC_CTL (0x1CF) +#define TAIKO_A_RX_LINE_1_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_LINE_1_STATUS (0x1D0) +#define TAIKO_A_RX_LINE_1_STATUS__POR (0x00) +#define TAIKO_A_RX_LINE_2_GAIN (0x1D1) +#define TAIKO_A_RX_LINE_2_GAIN__POR (0x00) +#define TAIKO_A_RX_LINE_2_TEST (0x1D2) +#define TAIKO_A_RX_LINE_2_TEST__POR (0x00) +#define TAIKO_A_RX_LINE_2_DAC_CTL (0x1D3) +#define TAIKO_A_RX_LINE_2_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_LINE_2_STATUS (0x1D4) +#define TAIKO_A_RX_LINE_2_STATUS__POR (0x00) +#define TAIKO_A_RX_LINE_3_GAIN (0x1D5) +#define TAIKO_A_RX_LINE_3_GAIN__POR (0x00) +#define TAIKO_A_RX_LINE_3_TEST (0x1D6) +#define TAIKO_A_RX_LINE_3_TEST__POR (0x00) +#define TAIKO_A_RX_LINE_3_DAC_CTL (0x1D7) +#define TAIKO_A_RX_LINE_3_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_LINE_3_STATUS (0x1D8) +#define TAIKO_A_RX_LINE_3_STATUS__POR (0x00) +#define TAIKO_A_RX_LINE_4_GAIN (0x1D9) +#define TAIKO_A_RX_LINE_4_GAIN__POR (0x00) +#define TAIKO_A_RX_LINE_4_TEST (0x1DA) +#define TAIKO_A_RX_LINE_4_TEST__POR (0x00) +#define TAIKO_A_RX_LINE_4_DAC_CTL (0x1DB) +#define TAIKO_A_RX_LINE_4_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_LINE_4_STATUS (0x1DC) +#define TAIKO_A_RX_LINE_4_STATUS__POR (0x00) +#define TAIKO_A_RX_LINE_CNP_DBG (0x1DD) +#define TAIKO_A_RX_LINE_CNP_DBG__POR (0x00) +#define TAIKO_A_SPKR_DRV_EN (0x1DF) +#define TAIKO_A_SPKR_DRV_EN__POR (0x6F) +#define TAIKO_A_SPKR_DRV_GAIN (0x1E0) +#define TAIKO_A_SPKR_DRV_GAIN__POR (0x00) +#define TAIKO_A_SPKR_DRV_DAC_CTL (0x1E1) +#define TAIKO_A_SPKR_DRV_DAC_CTL__POR (0x04) +#define TAIKO_A_SPKR_DRV_OCP_CTL (0x1E2) +#define TAIKO_A_SPKR_DRV_OCP_CTL__POR (0x98) +#define TAIKO_A_SPKR_DRV_CLIP_DET (0x1E3) +#define TAIKO_A_SPKR_DRV_CLIP_DET__POR (0x48) +#define TAIKO_A_SPKR_DRV_IEC (0x1E4) +#define TAIKO_A_SPKR_DRV_IEC__POR (0x20) +#define TAIKO_A_SPKR_DRV_DBG_DAC (0x1E5) +#define TAIKO_A_SPKR_DRV_DBG_DAC__POR (0x05) +#define TAIKO_A_SPKR_DRV_DBG_PA (0x1E6) +#define TAIKO_A_SPKR_DRV_DBG_PA__POR (0x18) +#define TAIKO_A_SPKR_DRV_DBG_PWRSTG (0x1E7) +#define TAIKO_A_SPKR_DRV_DBG_PWRSTG__POR (0x00) +#define TAIKO_A_SPKR_DRV_BIAS_LDO (0x1E8) +#define TAIKO_A_SPKR_DRV_BIAS_LDO__POR (0x45) +#define TAIKO_A_SPKR_DRV_BIAS_INT (0x1E9) +#define TAIKO_A_SPKR_DRV_BIAS_INT__POR (0xA5) +#define TAIKO_A_SPKR_DRV_BIAS_PA (0x1EA) +#define TAIKO_A_SPKR_DRV_BIAS_PA__POR (0x55) +#define TAIKO_A_SPKR_DRV_STATUS_OCP (0x1EB) +#define TAIKO_A_SPKR_DRV_STATUS_OCP__POR (0x00) +#define TAIKO_A_SPKR_DRV_STATUS_PA (0x1EC) +#define TAIKO_A_SPKR_DRV_STATUS_PA__POR (0x00) +#define TAIKO_A_SPKR_PROT_EN (0x1ED) +#define TAIKO_A_SPKR_PROT_EN__POR (0x00) +#define TAIKO_A_SPKR_PROT_ADC_EN (0x1EE) +#define TAIKO_A_SPKR_PROT_ADC_EN__POR (0x44) +#define TAIKO_A_SPKR_PROT_ISENSE_BIAS (0x1EF) +#define TAIKO_A_SPKR_PROT_ISENSE_BIAS__POR (0x44) +#define TAIKO_A_SPKR_PROT_VSENSE_BIAS (0x1F0) +#define TAIKO_A_SPKR_PROT_VSENSE_BIAS__POR (0x44) +#define TAIKO_A_SPKR_PROT_ADC_ATEST_REFCTRL (0x1F1) +#define TAIKO_A_SPKR_PROT_ADC_ATEST_REFCTRL__POR (0x00) +#define TAIKO_A_SPKR_PROT_ADC_TEST_CTL (0x1F2) +#define TAIKO_A_SPKR_PROT_ADC_TEST_CTL__POR (0x38) +#define TAIKO_A_SPKR_PROT_TEST_BLOCK_EN (0x1F3) +#define TAIKO_A_SPKR_PROT_TEST_BLOCK_EN__POR (0xFC) +#define TAIKO_A_SPKR_PROT_ATEST (0x1F4) +#define TAIKO_A_SPKR_PROT_ATEST__POR (0x00) +#define TAIKO_A_SPKR_PROT_V_SAR_ERR (0x1F5) +#define TAIKO_A_SPKR_PROT_V_SAR_ERR__POR (0x00) +#define TAIKO_A_SPKR_PROT_I_SAR_ERR (0x1F6) +#define TAIKO_A_SPKR_PROT_I_SAR_ERR__POR (0x00) +#define TAIKO_A_SPKR_PROT_LDO_CTRL (0x1F7) +#define TAIKO_A_SPKR_PROT_LDO_CTRL__POR (0x00) +#define TAIKO_A_SPKR_PROT_ISENSE_CTRL (0x1F8) +#define TAIKO_A_SPKR_PROT_ISENSE_CTRL__POR (0x00) +#define TAIKO_A_SPKR_PROT_VSENSE_CTRL (0x1F9) +#define TAIKO_A_SPKR_PROT_VSENSE_CTRL__POR (0x00) +#define TAIKO_A_RC_OSC_FREQ (0x1FA) +#define TAIKO_A_RC_OSC_FREQ__POR (0x46) +#define TAIKO_A_RC_OSC_TEST (0x1FB) +#define TAIKO_A_RC_OSC_TEST__POR (0x0A) +#define TAIKO_A_RC_OSC_STATUS (0x1FC) +#define TAIKO_A_RC_OSC_STATUS__POR (0x18) +#define TAIKO_A_RC_OSC_TUNER (0x1FD) +#define TAIKO_A_RC_OSC_TUNER__POR (0x00) +#define TAIKO_A_MBHC_HPH (0x1FE) +#define TAIKO_A_MBHC_HPH__POR (0x44) +#define TAIKO_A_CDC_ANC1_B1_CTL (0x200) +#define TAIKO_A_CDC_ANC1_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_B1_CTL (0x280) +#define TAIKO_A_CDC_ANC2_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_SHIFT (0x201) +#define TAIKO_A_CDC_ANC1_SHIFT__POR (0x00) +#define TAIKO_A_CDC_ANC2_SHIFT (0x281) +#define TAIKO_A_CDC_ANC2_SHIFT__POR (0x00) +#define TAIKO_A_CDC_ANC1_IIR_B1_CTL (0x202) +#define TAIKO_A_CDC_ANC1_IIR_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_IIR_B1_CTL (0x282) +#define TAIKO_A_CDC_ANC2_IIR_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_IIR_B2_CTL (0x203) +#define TAIKO_A_CDC_ANC1_IIR_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_IIR_B2_CTL (0x283) +#define TAIKO_A_CDC_ANC2_IIR_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_IIR_B3_CTL (0x204) +#define TAIKO_A_CDC_ANC1_IIR_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_IIR_B3_CTL (0x284) +#define TAIKO_A_CDC_ANC2_IIR_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_LPF_B1_CTL (0x206) +#define TAIKO_A_CDC_ANC1_LPF_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_LPF_B1_CTL (0x286) +#define TAIKO_A_CDC_ANC2_LPF_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_LPF_B2_CTL (0x207) +#define TAIKO_A_CDC_ANC1_LPF_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_LPF_B2_CTL (0x287) +#define TAIKO_A_CDC_ANC2_LPF_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_SPARE (0x209) +#define TAIKO_A_CDC_ANC1_SPARE__POR (0x00) +#define TAIKO_A_CDC_ANC2_SPARE (0x289) +#define TAIKO_A_CDC_ANC2_SPARE__POR (0x00) +#define TAIKO_A_CDC_ANC1_SMLPF_CTL (0x20A) +#define TAIKO_A_CDC_ANC1_SMLPF_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_SMLPF_CTL (0x28A) +#define TAIKO_A_CDC_ANC2_SMLPF_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_DCFLT_CTL (0x20B) +#define TAIKO_A_CDC_ANC1_DCFLT_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_DCFLT_CTL (0x28B) +#define TAIKO_A_CDC_ANC2_DCFLT_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_GAIN_CTL (0x20C) +#define TAIKO_A_CDC_ANC1_GAIN_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_GAIN_CTL (0x28C) +#define TAIKO_A_CDC_ANC2_GAIN_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_B2_CTL (0x20D) +#define TAIKO_A_CDC_ANC1_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_B2_CTL (0x28D) +#define TAIKO_A_CDC_ANC2_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_TX1_VOL_CTL_TIMER (0x220) +#define TAIKO_A_CDC_TX1_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX2_VOL_CTL_TIMER (0x228) +#define TAIKO_A_CDC_TX2_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX3_VOL_CTL_TIMER (0x230) +#define TAIKO_A_CDC_TX3_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX4_VOL_CTL_TIMER (0x238) +#define TAIKO_A_CDC_TX4_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX5_VOL_CTL_TIMER (0x240) +#define TAIKO_A_CDC_TX5_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX6_VOL_CTL_TIMER (0x248) +#define TAIKO_A_CDC_TX6_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX7_VOL_CTL_TIMER (0x250) +#define TAIKO_A_CDC_TX7_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX8_VOL_CTL_TIMER (0x258) +#define TAIKO_A_CDC_TX8_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX9_VOL_CTL_TIMER (0x260) +#define TAIKO_A_CDC_TX9_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX10_VOL_CTL_TIMER (0x268) +#define TAIKO_A_CDC_TX10_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX1_VOL_CTL_GAIN (0x221) +#define TAIKO_A_CDC_TX1_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX2_VOL_CTL_GAIN (0x229) +#define TAIKO_A_CDC_TX2_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX3_VOL_CTL_GAIN (0x231) +#define TAIKO_A_CDC_TX3_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX4_VOL_CTL_GAIN (0x239) +#define TAIKO_A_CDC_TX4_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX5_VOL_CTL_GAIN (0x241) +#define TAIKO_A_CDC_TX5_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX6_VOL_CTL_GAIN (0x249) +#define TAIKO_A_CDC_TX6_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX7_VOL_CTL_GAIN (0x251) +#define TAIKO_A_CDC_TX7_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX8_VOL_CTL_GAIN (0x259) +#define TAIKO_A_CDC_TX8_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX9_VOL_CTL_GAIN (0x261) +#define TAIKO_A_CDC_TX9_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX10_VOL_CTL_GAIN (0x269) +#define TAIKO_A_CDC_TX10_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX1_VOL_CTL_CFG (0x222) +#define TAIKO_A_CDC_TX1_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX2_VOL_CTL_CFG (0x22A) +#define TAIKO_A_CDC_TX2_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX3_VOL_CTL_CFG (0x232) +#define TAIKO_A_CDC_TX3_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX4_VOL_CTL_CFG (0x23A) +#define TAIKO_A_CDC_TX4_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX5_VOL_CTL_CFG (0x242) +#define TAIKO_A_CDC_TX5_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX6_VOL_CTL_CFG (0x24A) +#define TAIKO_A_CDC_TX6_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX7_VOL_CTL_CFG (0x252) +#define TAIKO_A_CDC_TX7_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX8_VOL_CTL_CFG (0x25A) +#define TAIKO_A_CDC_TX8_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX9_VOL_CTL_CFG (0x262) +#define TAIKO_A_CDC_TX9_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX10_VOL_CTL_CFG (0x26A) +#define TAIKO_A_CDC_TX10_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX1_MUX_CTL (0x223) +#define TAIKO_A_CDC_TX1_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX2_MUX_CTL (0x22B) +#define TAIKO_A_CDC_TX2_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX3_MUX_CTL (0x233) +#define TAIKO_A_CDC_TX3_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX4_MUX_CTL (0x23B) +#define TAIKO_A_CDC_TX4_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX5_MUX_CTL (0x243) +#define TAIKO_A_CDC_TX5_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX6_MUX_CTL (0x24B) +#define TAIKO_A_CDC_TX6_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX7_MUX_CTL (0x253) +#define TAIKO_A_CDC_TX7_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX8_MUX_CTL (0x25B) +#define TAIKO_A_CDC_TX8_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX9_MUX_CTL (0x263) +#define TAIKO_A_CDC_TX9_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX10_MUX_CTL (0x26B) +#define TAIKO_A_CDC_TX10_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX1_CLK_FS_CTL (0x224) +#define TAIKO_A_CDC_TX1_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX2_CLK_FS_CTL (0x22C) +#define TAIKO_A_CDC_TX2_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX3_CLK_FS_CTL (0x234) +#define TAIKO_A_CDC_TX3_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX4_CLK_FS_CTL (0x23C) +#define TAIKO_A_CDC_TX4_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX5_CLK_FS_CTL (0x244) +#define TAIKO_A_CDC_TX5_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX6_CLK_FS_CTL (0x24C) +#define TAIKO_A_CDC_TX6_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX7_CLK_FS_CTL (0x254) +#define TAIKO_A_CDC_TX7_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX8_CLK_FS_CTL (0x25C) +#define TAIKO_A_CDC_TX8_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX9_CLK_FS_CTL (0x264) +#define TAIKO_A_CDC_TX9_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX10_CLK_FS_CTL (0x26C) +#define TAIKO_A_CDC_TX10_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX1_DMIC_CTL (0x225) +#define TAIKO_A_CDC_TX1_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX2_DMIC_CTL (0x22D) +#define TAIKO_A_CDC_TX2_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX3_DMIC_CTL (0x235) +#define TAIKO_A_CDC_TX3_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX4_DMIC_CTL (0x23D) +#define TAIKO_A_CDC_TX4_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX5_DMIC_CTL (0x245) +#define TAIKO_A_CDC_TX5_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX6_DMIC_CTL (0x24D) +#define TAIKO_A_CDC_TX6_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX7_DMIC_CTL (0x255) +#define TAIKO_A_CDC_TX7_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX8_DMIC_CTL (0x25D) +#define TAIKO_A_CDC_TX8_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX9_DMIC_CTL (0x265) +#define TAIKO_A_CDC_TX9_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX10_DMIC_CTL (0x26D) +#define TAIKO_A_CDC_TX10_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B1_CTL (0x278) +#define TAIKO_A_CDC_DEBUG_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B2_CTL (0x279) +#define TAIKO_A_CDC_DEBUG_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B3_CTL (0x27A) +#define TAIKO_A_CDC_DEBUG_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B4_CTL (0x27B) +#define TAIKO_A_CDC_DEBUG_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B5_CTL (0x27C) +#define TAIKO_A_CDC_DEBUG_B5_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B6_CTL (0x27D) +#define TAIKO_A_CDC_DEBUG_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B7_CTL (0x27E) +#define TAIKO_A_CDC_DEBUG_B7_CTL__POR (0x00) +#define TAIKO_A_CDC_SRC1_PDA_CFG (0x2A0) +#define TAIKO_A_CDC_SRC1_PDA_CFG__POR (0x00) +#define TAIKO_A_CDC_SRC2_PDA_CFG (0x2A8) +#define TAIKO_A_CDC_SRC2_PDA_CFG__POR (0x00) +#define TAIKO_A_CDC_SRC1_FS_CTL (0x2A1) +#define TAIKO_A_CDC_SRC1_FS_CTL__POR (0x1B) +#define TAIKO_A_CDC_SRC2_FS_CTL (0x2A9) +#define TAIKO_A_CDC_SRC2_FS_CTL__POR (0x1B) +#define TAIKO_A_CDC_RX1_B1_CTL (0x2B0) +#define TAIKO_A_CDC_RX1_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX2_B1_CTL (0x2B8) +#define TAIKO_A_CDC_RX2_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX3_B1_CTL (0x2C0) +#define TAIKO_A_CDC_RX3_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX4_B1_CTL (0x2C8) +#define TAIKO_A_CDC_RX4_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX5_B1_CTL (0x2D0) +#define TAIKO_A_CDC_RX5_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX6_B1_CTL (0x2D8) +#define TAIKO_A_CDC_RX6_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX7_B1_CTL (0x2E0) +#define TAIKO_A_CDC_RX7_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX1_B2_CTL (0x2B1) +#define TAIKO_A_CDC_RX1_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX2_B2_CTL (0x2B9) +#define TAIKO_A_CDC_RX2_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX3_B2_CTL (0x2C1) +#define TAIKO_A_CDC_RX3_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX4_B2_CTL (0x2C9) +#define TAIKO_A_CDC_RX4_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX5_B2_CTL (0x2D1) +#define TAIKO_A_CDC_RX5_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX6_B2_CTL (0x2D9) +#define TAIKO_A_CDC_RX6_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX7_B2_CTL (0x2E1) +#define TAIKO_A_CDC_RX7_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX1_B3_CTL (0x2B2) +#define TAIKO_A_CDC_RX1_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX2_B3_CTL (0x2BA) +#define TAIKO_A_CDC_RX2_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX3_B3_CTL (0x2C2) +#define TAIKO_A_CDC_RX3_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX4_B3_CTL (0x2CA) +#define TAIKO_A_CDC_RX4_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX5_B3_CTL (0x2D2) +#define TAIKO_A_CDC_RX5_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX6_B3_CTL (0x2DA) +#define TAIKO_A_CDC_RX6_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX7_B3_CTL (0x2E2) +#define TAIKO_A_CDC_RX7_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX1_B4_CTL (0x2B3) +#define TAIKO_A_CDC_RX1_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX2_B4_CTL (0x2BB) +#define TAIKO_A_CDC_RX2_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX3_B4_CTL (0x2C3) +#define TAIKO_A_CDC_RX3_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX4_B4_CTL (0x2CB) +#define TAIKO_A_CDC_RX4_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX5_B4_CTL (0x2D3) +#define TAIKO_A_CDC_RX5_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX6_B4_CTL (0x2DB) +#define TAIKO_A_CDC_RX6_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX7_B4_CTL (0x2E3) +#define TAIKO_A_CDC_RX7_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX1_B5_CTL (0x2B4) +#define TAIKO_A_CDC_RX1_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX2_B5_CTL (0x2BC) +#define TAIKO_A_CDC_RX2_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX3_B5_CTL (0x2C4) +#define TAIKO_A_CDC_RX3_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX4_B5_CTL (0x2CC) +#define TAIKO_A_CDC_RX4_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX5_B5_CTL (0x2D4) +#define TAIKO_A_CDC_RX5_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX6_B5_CTL (0x2DC) +#define TAIKO_A_CDC_RX6_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX7_B5_CTL (0x2E4) +#define TAIKO_A_CDC_RX7_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX1_B6_CTL (0x2B5) +#define TAIKO_A_CDC_RX1_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX2_B6_CTL (0x2BD) +#define TAIKO_A_CDC_RX2_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX3_B6_CTL (0x2C5) +#define TAIKO_A_CDC_RX3_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX4_B6_CTL (0x2CD) +#define TAIKO_A_CDC_RX4_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX5_B6_CTL (0x2D5) +#define TAIKO_A_CDC_RX5_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX6_B6_CTL (0x2DD) +#define TAIKO_A_CDC_RX6_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX7_B6_CTL (0x2E5) +#define TAIKO_A_CDC_RX7_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX1_VOL_CTL_B1_CTL (0x2B6) +#define TAIKO_A_CDC_RX1_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX2_VOL_CTL_B1_CTL (0x2BE) +#define TAIKO_A_CDC_RX2_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX3_VOL_CTL_B1_CTL (0x2C6) +#define TAIKO_A_CDC_RX3_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX4_VOL_CTL_B1_CTL (0x2CE) +#define TAIKO_A_CDC_RX4_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX5_VOL_CTL_B1_CTL (0x2D6) +#define TAIKO_A_CDC_RX5_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX6_VOL_CTL_B1_CTL (0x2DE) +#define TAIKO_A_CDC_RX6_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX7_VOL_CTL_B1_CTL (0x2E6) +#define TAIKO_A_CDC_RX7_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL (0x2B7) +#define TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL (0x2BF) +#define TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL (0x2C7) +#define TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL (0x2CF) +#define TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL (0x2D7) +#define TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL (0x2DF) +#define TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL (0x2E7) +#define TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_VBAT_CFG (0x2E8) +#define TAIKO_A_CDC_VBAT_CFG__POR (0x1A) +#define TAIKO_A_CDC_VBAT_ADC_CAL1 (0x2E9) +#define TAIKO_A_CDC_VBAT_ADC_CAL1__POR (0x00) +#define TAIKO_A_CDC_VBAT_ADC_CAL2 (0x2EA) +#define TAIKO_A_CDC_VBAT_ADC_CAL2__POR (0x00) +#define TAIKO_A_CDC_VBAT_ADC_CAL3 (0x2EB) +#define TAIKO_A_CDC_VBAT_ADC_CAL3__POR (0x04) +#define TAIKO_A_CDC_VBAT_PK_EST1 (0x2EC) +#define TAIKO_A_CDC_VBAT_PK_EST1__POR (0xE0) +#define TAIKO_A_CDC_VBAT_PK_EST2 (0x2ED) +#define TAIKO_A_CDC_VBAT_PK_EST2__POR (0x01) +#define TAIKO_A_CDC_VBAT_PK_EST3 (0x2EE) +#define TAIKO_A_CDC_VBAT_PK_EST3__POR (0x40) +#define TAIKO_A_CDC_VBAT_RF_PROC1 (0x2EF) +#define TAIKO_A_CDC_VBAT_RF_PROC1__POR (0x2A) +#define TAIKO_A_CDC_VBAT_RF_PROC2 (0x2F0) +#define TAIKO_A_CDC_VBAT_RF_PROC2__POR (0x86) +#define TAIKO_A_CDC_VBAT_TAC1 (0x2F1) +#define TAIKO_A_CDC_VBAT_TAC1__POR (0x70) +#define TAIKO_A_CDC_VBAT_TAC2 (0x2F2) +#define TAIKO_A_CDC_VBAT_TAC2__POR (0x18) +#define TAIKO_A_CDC_VBAT_TAC3 (0x2F3) +#define TAIKO_A_CDC_VBAT_TAC3__POR (0x18) +#define TAIKO_A_CDC_VBAT_TAC4 (0x2F4) +#define TAIKO_A_CDC_VBAT_TAC4__POR (0x03) +#define TAIKO_A_CDC_VBAT_GAIN_UPD1 (0x2F5) +#define TAIKO_A_CDC_VBAT_GAIN_UPD1__POR (0x01) +#define TAIKO_A_CDC_VBAT_GAIN_UPD2 (0x2F6) +#define TAIKO_A_CDC_VBAT_GAIN_UPD2__POR (0x00) +#define TAIKO_A_CDC_VBAT_GAIN_UPD3 (0x2F7) +#define TAIKO_A_CDC_VBAT_GAIN_UPD3__POR (0x64) +#define TAIKO_A_CDC_VBAT_GAIN_UPD4 (0x2F8) +#define TAIKO_A_CDC_VBAT_GAIN_UPD4__POR (0x01) +#define TAIKO_A_CDC_VBAT_DEBUG1 (0x2F9) +#define TAIKO_A_CDC_VBAT_DEBUG1__POR (0x00) +#define TAIKO_A_CDC_CLK_ANC_RESET_CTL (0x300) +#define TAIKO_A_CDC_CLK_ANC_RESET_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_RX_RESET_CTL (0x301) +#define TAIKO_A_CDC_CLK_RX_RESET_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_TX_RESET_B1_CTL (0x302) +#define TAIKO_A_CDC_CLK_TX_RESET_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_TX_RESET_B2_CTL (0x303) +#define TAIKO_A_CDC_CLK_TX_RESET_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_DMIC_B1_CTL (0x304) +#define TAIKO_A_CDC_CLK_DMIC_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_DMIC_B2_CTL (0x305) +#define TAIKO_A_CDC_CLK_DMIC_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_RX_I2S_CTL (0x306) +#define TAIKO_A_CDC_CLK_RX_I2S_CTL__POR (0x03) +#define TAIKO_A_CDC_CLK_TX_I2S_CTL (0x307) +#define TAIKO_A_CDC_CLK_TX_I2S_CTL__POR (0x03) +#define TAIKO_A_CDC_CLK_OTHR_RESET_B1_CTL (0x308) +#define TAIKO_A_CDC_CLK_OTHR_RESET_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL (0x309) +#define TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_TX_CLK_EN_B1_CTL (0x30A) +#define TAIKO_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_TX_CLK_EN_B2_CTL (0x30B) +#define TAIKO_A_CDC_CLK_TX_CLK_EN_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_OTHR_CTL (0x30C) +#define TAIKO_A_CDC_CLK_OTHR_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_RDAC_CLK_EN_CTL (0x30D) +#define TAIKO_A_CDC_CLK_RDAC_CLK_EN_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_ANC_CLK_EN_CTL (0x30E) +#define TAIKO_A_CDC_CLK_ANC_CLK_EN_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_RX_B1_CTL (0x30F) +#define TAIKO_A_CDC_CLK_RX_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_RX_B2_CTL (0x310) +#define TAIKO_A_CDC_CLK_RX_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_MCLK_CTL (0x311) +#define TAIKO_A_CDC_CLK_MCLK_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_PDM_CTL (0x312) +#define TAIKO_A_CDC_CLK_PDM_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_SD_CTL (0x313) +#define TAIKO_A_CDC_CLK_SD_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_POWER_CTL (0x314) +#define TAIKO_A_CDC_CLK_POWER_CTL__POR (0x00) +#define TAIKO_A_CDC_CLSH_B1_CTL (0x320) +#define TAIKO_A_CDC_CLSH_B1_CTL__POR (0xE4) +#define TAIKO_A_CDC_CLSH_B2_CTL (0x321) +#define TAIKO_A_CDC_CLSH_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CLSH_B3_CTL (0x322) +#define TAIKO_A_CDC_CLSH_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CLSH_BUCK_NCP_VARS (0x323) +#define TAIKO_A_CDC_CLSH_BUCK_NCP_VARS__POR (0x00) +#define TAIKO_A_CDC_CLSH_IDLE_HPH_THSD (0x324) +#define TAIKO_A_CDC_CLSH_IDLE_HPH_THSD__POR (0x12) +#define TAIKO_A_CDC_CLSH_IDLE_EAR_THSD (0x325) +#define TAIKO_A_CDC_CLSH_IDLE_EAR_THSD__POR (0x0C) +#define TAIKO_A_CDC_CLSH_FCLKONLY_HPH_THSD (0x326) +#define TAIKO_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR (0x18) +#define TAIKO_A_CDC_CLSH_FCLKONLY_EAR_THSD (0x327) +#define TAIKO_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR (0x23) +#define TAIKO_A_CDC_CLSH_K_ADDR (0x328) +#define TAIKO_A_CDC_CLSH_K_ADDR__POR (0x00) +#define TAIKO_A_CDC_CLSH_K_DATA (0x329) +#define TAIKO_A_CDC_CLSH_K_DATA__POR (0xA4) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_L (0x32A) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_L__POR (0xD7) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_U (0x32B) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_U__POR (0x05) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_L (0x32C) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_L__POR (0x60) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_U (0x32D) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_U__POR (0x09) +#define TAIKO_A_CDC_CLSH_V_PA_HD_EAR (0x32E) +#define TAIKO_A_CDC_CLSH_V_PA_HD_EAR__POR (0x00) +#define TAIKO_A_CDC_CLSH_V_PA_HD_HPH (0x32F) +#define TAIKO_A_CDC_CLSH_V_PA_HD_HPH__POR (0x00) +#define TAIKO_A_CDC_CLSH_V_PA_MIN_EAR (0x330) +#define TAIKO_A_CDC_CLSH_V_PA_MIN_EAR__POR (0x00) +#define TAIKO_A_CDC_CLSH_V_PA_MIN_HPH (0x331) +#define TAIKO_A_CDC_CLSH_V_PA_MIN_HPH__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B1_CTL (0x340) +#define TAIKO_A_CDC_IIR1_GAIN_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B1_CTL (0x350) +#define TAIKO_A_CDC_IIR2_GAIN_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B2_CTL (0x341) +#define TAIKO_A_CDC_IIR1_GAIN_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B2_CTL (0x351) +#define TAIKO_A_CDC_IIR2_GAIN_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B3_CTL (0x342) +#define TAIKO_A_CDC_IIR1_GAIN_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B3_CTL (0x352) +#define TAIKO_A_CDC_IIR2_GAIN_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B4_CTL (0x343) +#define TAIKO_A_CDC_IIR1_GAIN_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B4_CTL (0x353) +#define TAIKO_A_CDC_IIR2_GAIN_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B5_CTL (0x344) +#define TAIKO_A_CDC_IIR1_GAIN_B5_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B5_CTL (0x354) +#define TAIKO_A_CDC_IIR2_GAIN_B5_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B6_CTL (0x345) +#define TAIKO_A_CDC_IIR1_GAIN_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B6_CTL (0x355) +#define TAIKO_A_CDC_IIR2_GAIN_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B7_CTL (0x346) +#define TAIKO_A_CDC_IIR1_GAIN_B7_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B7_CTL (0x356) +#define TAIKO_A_CDC_IIR2_GAIN_B7_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B8_CTL (0x347) +#define TAIKO_A_CDC_IIR1_GAIN_B8_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B8_CTL (0x357) +#define TAIKO_A_CDC_IIR2_GAIN_B8_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_CTL (0x348) +#define TAIKO_A_CDC_IIR1_CTL__POR (0x40) +#define TAIKO_A_CDC_IIR2_CTL (0x358) +#define TAIKO_A_CDC_IIR2_CTL__POR (0x40) +#define TAIKO_A_CDC_IIR1_GAIN_TIMER_CTL (0x349) +#define TAIKO_A_CDC_IIR1_GAIN_TIMER_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_TIMER_CTL (0x359) +#define TAIKO_A_CDC_IIR2_GAIN_TIMER_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_COEF_B1_CTL (0x34A) +#define TAIKO_A_CDC_IIR1_COEF_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_COEF_B1_CTL (0x35A) +#define TAIKO_A_CDC_IIR2_COEF_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_COEF_B2_CTL (0x34B) +#define TAIKO_A_CDC_IIR1_COEF_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_COEF_B2_CTL (0x35B) +#define TAIKO_A_CDC_IIR2_COEF_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_TOP_GAIN_UPDATE (0x360) +#define TAIKO_A_CDC_TOP_GAIN_UPDATE__POR (0x00) +#define TAIKO_A_CDC_COMP0_B1_CTL (0x368) +#define TAIKO_A_CDC_COMP0_B1_CTL__POR (0x30) +#define TAIKO_A_CDC_COMP1_B1_CTL (0x370) +#define TAIKO_A_CDC_COMP1_B1_CTL__POR (0x30) +#define TAIKO_A_CDC_COMP2_B1_CTL (0x378) +#define TAIKO_A_CDC_COMP2_B1_CTL__POR (0x30) +#define TAIKO_A_CDC_COMP0_B2_CTL (0x369) +#define TAIKO_A_CDC_COMP0_B2_CTL__POR (0xB5) +#define TAIKO_A_CDC_COMP1_B2_CTL (0x371) +#define TAIKO_A_CDC_COMP1_B2_CTL__POR (0xB5) +#define TAIKO_A_CDC_COMP2_B2_CTL (0x379) +#define TAIKO_A_CDC_COMP2_B2_CTL__POR (0xB5) +#define TAIKO_A_CDC_COMP0_B3_CTL (0x36A) +#define TAIKO_A_CDC_COMP0_B3_CTL__POR (0x28) +#define TAIKO_A_CDC_COMP1_B3_CTL (0x372) +#define TAIKO_A_CDC_COMP1_B3_CTL__POR (0x28) +#define TAIKO_A_CDC_COMP2_B3_CTL (0x37A) +#define TAIKO_A_CDC_COMP2_B3_CTL__POR (0x28) +#define TAIKO_A_CDC_COMP0_B4_CTL (0x36B) +#define TAIKO_A_CDC_COMP0_B4_CTL__POR (0x3C) +#define TAIKO_A_CDC_COMP1_B4_CTL (0x373) +#define TAIKO_A_CDC_COMP1_B4_CTL__POR (0x3C) +#define TAIKO_A_CDC_COMP2_B4_CTL (0x37B) +#define TAIKO_A_CDC_COMP2_B4_CTL__POR (0x3C) +#define TAIKO_A_CDC_COMP0_B5_CTL (0x36C) +#define TAIKO_A_CDC_COMP0_B5_CTL__POR (0x1F) +#define TAIKO_A_CDC_COMP1_B5_CTL (0x374) +#define TAIKO_A_CDC_COMP1_B5_CTL__POR (0x1F) +#define TAIKO_A_CDC_COMP2_B5_CTL (0x37C) +#define TAIKO_A_CDC_COMP2_B5_CTL__POR (0x1F) +#define TAIKO_A_CDC_COMP0_B6_CTL (0x36D) +#define TAIKO_A_CDC_COMP0_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_COMP1_B6_CTL (0x375) +#define TAIKO_A_CDC_COMP1_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_COMP2_B6_CTL (0x37D) +#define TAIKO_A_CDC_COMP2_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_COMP0_SHUT_DOWN_STATUS (0x36E) +#define TAIKO_A_CDC_COMP0_SHUT_DOWN_STATUS__POR (0x03) +#define TAIKO_A_CDC_COMP1_SHUT_DOWN_STATUS (0x376) +#define TAIKO_A_CDC_COMP1_SHUT_DOWN_STATUS__POR (0x03) +#define TAIKO_A_CDC_COMP2_SHUT_DOWN_STATUS (0x37E) +#define TAIKO_A_CDC_COMP2_SHUT_DOWN_STATUS__POR (0x03) +#define TAIKO_A_CDC_COMP0_FS_CFG (0x36F) +#define TAIKO_A_CDC_COMP0_FS_CFG__POR (0x03) +#define TAIKO_A_CDC_COMP1_FS_CFG (0x377) +#define TAIKO_A_CDC_COMP1_FS_CFG__POR (0x03) +#define TAIKO_A_CDC_COMP2_FS_CFG (0x37F) +#define TAIKO_A_CDC_COMP2_FS_CFG__POR (0x03) +#define TAIKO_A_CDC_CONN_RX1_B1_CTL (0x380) +#define TAIKO_A_CDC_CONN_RX1_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX1_B2_CTL (0x381) +#define TAIKO_A_CDC_CONN_RX1_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX1_B3_CTL (0x382) +#define TAIKO_A_CDC_CONN_RX1_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX2_B1_CTL (0x383) +#define TAIKO_A_CDC_CONN_RX2_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX2_B2_CTL (0x384) +#define TAIKO_A_CDC_CONN_RX2_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX2_B3_CTL (0x385) +#define TAIKO_A_CDC_CONN_RX2_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX3_B1_CTL (0x386) +#define TAIKO_A_CDC_CONN_RX3_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX3_B2_CTL (0x387) +#define TAIKO_A_CDC_CONN_RX3_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX4_B1_CTL (0x388) +#define TAIKO_A_CDC_CONN_RX4_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX4_B2_CTL (0x389) +#define TAIKO_A_CDC_CONN_RX4_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX5_B1_CTL (0x38A) +#define TAIKO_A_CDC_CONN_RX5_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX5_B2_CTL (0x38B) +#define TAIKO_A_CDC_CONN_RX5_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX6_B1_CTL (0x38C) +#define TAIKO_A_CDC_CONN_RX6_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX6_B2_CTL (0x38D) +#define TAIKO_A_CDC_CONN_RX6_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX7_B1_CTL (0x38E) +#define TAIKO_A_CDC_CONN_RX7_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX7_B2_CTL (0x38F) +#define TAIKO_A_CDC_CONN_RX7_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX7_B3_CTL (0x390) +#define TAIKO_A_CDC_CONN_RX7_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_ANC_B1_CTL (0x391) +#define TAIKO_A_CDC_CONN_ANC_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_ANC_B2_CTL (0x392) +#define TAIKO_A_CDC_CONN_ANC_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_B1_CTL (0x393) +#define TAIKO_A_CDC_CONN_TX_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_B2_CTL (0x394) +#define TAIKO_A_CDC_CONN_TX_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_B3_CTL (0x395) +#define TAIKO_A_CDC_CONN_TX_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_B4_CTL (0x396) +#define TAIKO_A_CDC_CONN_TX_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ1_B1_CTL (0x397) +#define TAIKO_A_CDC_CONN_EQ1_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ1_B2_CTL (0x398) +#define TAIKO_A_CDC_CONN_EQ1_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ1_B3_CTL (0x399) +#define TAIKO_A_CDC_CONN_EQ1_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ1_B4_CTL (0x39A) +#define TAIKO_A_CDC_CONN_EQ1_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ2_B1_CTL (0x39B) +#define TAIKO_A_CDC_CONN_EQ2_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ2_B2_CTL (0x39C) +#define TAIKO_A_CDC_CONN_EQ2_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ2_B3_CTL (0x39D) +#define TAIKO_A_CDC_CONN_EQ2_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ2_B4_CTL (0x39E) +#define TAIKO_A_CDC_CONN_EQ2_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_SRC1_B1_CTL (0x39F) +#define TAIKO_A_CDC_CONN_SRC1_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_SRC1_B2_CTL (0x3A0) +#define TAIKO_A_CDC_CONN_SRC1_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_SRC2_B1_CTL (0x3A1) +#define TAIKO_A_CDC_CONN_SRC2_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_SRC2_B2_CTL (0x3A2) +#define TAIKO_A_CDC_CONN_SRC2_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B1_CTL (0x3A3) +#define TAIKO_A_CDC_CONN_TX_SB_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B2_CTL (0x3A4) +#define TAIKO_A_CDC_CONN_TX_SB_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B3_CTL (0x3A5) +#define TAIKO_A_CDC_CONN_TX_SB_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B4_CTL (0x3A6) +#define TAIKO_A_CDC_CONN_TX_SB_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B5_CTL (0x3A7) +#define TAIKO_A_CDC_CONN_TX_SB_B5_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B6_CTL (0x3A8) +#define TAIKO_A_CDC_CONN_TX_SB_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B7_CTL (0x3A9) +#define TAIKO_A_CDC_CONN_TX_SB_B7_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B8_CTL (0x3AA) +#define TAIKO_A_CDC_CONN_TX_SB_B8_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B9_CTL (0x3AB) +#define TAIKO_A_CDC_CONN_TX_SB_B9_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B10_CTL (0x3AC) +#define TAIKO_A_CDC_CONN_TX_SB_B10_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B11_CTL (0x3AD) +#define TAIKO_A_CDC_CONN_TX_SB_B11_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX_SB_B1_CTL (0x3AE) +#define TAIKO_A_CDC_CONN_RX_SB_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX_SB_B2_CTL (0x3AF) +#define TAIKO_A_CDC_CONN_RX_SB_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_CLSH_CTL (0x3B0) +#define TAIKO_A_CDC_CONN_CLSH_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_MISC (0x3B1) +#define TAIKO_A_CDC_CONN_MISC__POR (0x01) +#define TAIKO_A_CDC_CONN_MAD (0x3B2) +#define TAIKO_A_CDC_CONN_MAD__POR (0x01) +#define TAIKO_A_CDC_MBHC_EN_CTL (0x3C0) +#define TAIKO_A_CDC_MBHC_EN_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_FIR_B1_CFG (0x3C1) +#define TAIKO_A_CDC_MBHC_FIR_B1_CFG__POR (0x00) +#define TAIKO_A_CDC_MBHC_FIR_B2_CFG (0x3C2) +#define TAIKO_A_CDC_MBHC_FIR_B2_CFG__POR (0x06) +#define TAIKO_A_CDC_MBHC_TIMER_B1_CTL (0x3C3) +#define TAIKO_A_CDC_MBHC_TIMER_B1_CTL__POR (0x03) +#define TAIKO_A_CDC_MBHC_TIMER_B2_CTL (0x3C4) +#define TAIKO_A_CDC_MBHC_TIMER_B2_CTL__POR (0x09) +#define TAIKO_A_CDC_MBHC_TIMER_B3_CTL (0x3C5) +#define TAIKO_A_CDC_MBHC_TIMER_B3_CTL__POR (0x1E) +#define TAIKO_A_CDC_MBHC_TIMER_B4_CTL (0x3C6) +#define TAIKO_A_CDC_MBHC_TIMER_B4_CTL__POR (0x45) +#define TAIKO_A_CDC_MBHC_TIMER_B5_CTL (0x3C7) +#define TAIKO_A_CDC_MBHC_TIMER_B5_CTL__POR (0x04) +#define TAIKO_A_CDC_MBHC_TIMER_B6_CTL (0x3C8) +#define TAIKO_A_CDC_MBHC_TIMER_B6_CTL__POR (0x78) +#define TAIKO_A_CDC_MBHC_B1_STATUS (0x3C9) +#define TAIKO_A_CDC_MBHC_B1_STATUS__POR (0x00) +#define TAIKO_A_CDC_MBHC_B2_STATUS (0x3CA) +#define TAIKO_A_CDC_MBHC_B2_STATUS__POR (0x00) +#define TAIKO_A_CDC_MBHC_B3_STATUS (0x3CB) +#define TAIKO_A_CDC_MBHC_B3_STATUS__POR (0x00) +#define TAIKO_A_CDC_MBHC_B4_STATUS (0x3CC) +#define TAIKO_A_CDC_MBHC_B4_STATUS__POR (0x00) +#define TAIKO_A_CDC_MBHC_B5_STATUS (0x3CD) +#define TAIKO_A_CDC_MBHC_B5_STATUS__POR (0x00) +#define TAIKO_A_CDC_MBHC_B1_CTL (0x3CE) +#define TAIKO_A_CDC_MBHC_B1_CTL__POR (0xC0) +#define TAIKO_A_CDC_MBHC_B2_CTL (0x3CF) +#define TAIKO_A_CDC_MBHC_B2_CTL__POR (0x5D) +#define TAIKO_A_CDC_MBHC_VOLT_B1_CTL (0x3D0) +#define TAIKO_A_CDC_MBHC_VOLT_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B2_CTL (0x3D1) +#define TAIKO_A_CDC_MBHC_VOLT_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B3_CTL (0x3D2) +#define TAIKO_A_CDC_MBHC_VOLT_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B4_CTL (0x3D3) +#define TAIKO_A_CDC_MBHC_VOLT_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B5_CTL (0x3D4) +#define TAIKO_A_CDC_MBHC_VOLT_B5_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B6_CTL (0x3D5) +#define TAIKO_A_CDC_MBHC_VOLT_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B7_CTL (0x3D6) +#define TAIKO_A_CDC_MBHC_VOLT_B7_CTL__POR (0xFF) +#define TAIKO_A_CDC_MBHC_VOLT_B8_CTL (0x3D7) +#define TAIKO_A_CDC_MBHC_VOLT_B8_CTL__POR (0x07) +#define TAIKO_A_CDC_MBHC_VOLT_B9_CTL (0x3D8) +#define TAIKO_A_CDC_MBHC_VOLT_B9_CTL__POR (0xFF) +#define TAIKO_A_CDC_MBHC_VOLT_B10_CTL (0x3D9) +#define TAIKO_A_CDC_MBHC_VOLT_B10_CTL__POR (0x7F) +#define TAIKO_A_CDC_MBHC_VOLT_B11_CTL (0x3DA) +#define TAIKO_A_CDC_MBHC_VOLT_B11_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B12_CTL (0x3DB) +#define TAIKO_A_CDC_MBHC_VOLT_B12_CTL__POR (0x80) +#define TAIKO_A_CDC_MBHC_CLK_CTL (0x3DC) +#define TAIKO_A_CDC_MBHC_CLK_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_INT_CTL (0x3DD) +#define TAIKO_A_CDC_MBHC_INT_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_DEBUG_CTL (0x3DE) +#define TAIKO_A_CDC_MBHC_DEBUG_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_SPARE (0x3DF) +#define TAIKO_A_CDC_MBHC_SPARE__POR (0x00) +#define TAIKO_A_CDC_MAD_MAIN_CTL_1 (0x3E0) +#define TAIKO_A_CDC_MAD_MAIN_CTL_1__POR (0x00) +#define TAIKO_A_CDC_MAD_MAIN_CTL_2 (0x3E1) +#define TAIKO_A_CDC_MAD_MAIN_CTL_2__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_1 (0x3E2) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_1__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_2 (0x3E3) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_2__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_3 (0x3E4) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_3__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_4 (0x3E5) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_4__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_5 (0x3E6) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_5__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_6 (0x3E7) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_6__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_7 (0x3E8) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_7__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_8 (0x3E9) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_8__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_PTR (0x3EA) +#define TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_PTR__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_VAL (0x3EB) +#define TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_VAL__POR (0x40) +#define TAIKO_A_CDC_MAD_ULTR_CTL_1 (0x3EC) +#define TAIKO_A_CDC_MAD_ULTR_CTL_1__POR (0x00) +#define TAIKO_A_CDC_MAD_ULTR_CTL_2 (0x3ED) +#define TAIKO_A_CDC_MAD_ULTR_CTL_2__POR (0x00) +#define TAIKO_A_CDC_MAD_ULTR_CTL_3 (0x3EE) +#define TAIKO_A_CDC_MAD_ULTR_CTL_3__POR (0x00) +#define TAIKO_A_CDC_MAD_ULTR_CTL_4 (0x3EF) +#define TAIKO_A_CDC_MAD_ULTR_CTL_4__POR (0x00) +#define TAIKO_A_CDC_MAD_ULTR_CTL_5 (0x3F0) +#define TAIKO_A_CDC_MAD_ULTR_CTL_5__POR (0x00) +#define TAIKO_A_CDC_MAD_ULTR_CTL_6 (0x3F1) +#define TAIKO_A_CDC_MAD_ULTR_CTL_6__POR (0x00) +#define TAIKO_A_CDC_MAD_ULTR_CTL_7 (0x3F2) +#define TAIKO_A_CDC_MAD_ULTR_CTL_7__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_1 (0x3F3) +#define TAIKO_A_CDC_MAD_BEACON_CTL_1__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_2 (0x3F4) +#define TAIKO_A_CDC_MAD_BEACON_CTL_2__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_3 (0x3F5) +#define TAIKO_A_CDC_MAD_BEACON_CTL_3__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_4 (0x3F6) +#define TAIKO_A_CDC_MAD_BEACON_CTL_4__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_5 (0x3F7) +#define TAIKO_A_CDC_MAD_BEACON_CTL_5__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_6 (0x3F8) +#define TAIKO_A_CDC_MAD_BEACON_CTL_6__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_7 (0x3F9) +#define TAIKO_A_CDC_MAD_BEACON_CTL_7__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_8 (0x3FA) +#define TAIKO_A_CDC_MAD_BEACON_CTL_8__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_IIR_CTL_PTR (0x3FB) +#define TAIKO_A_CDC_MAD_BEACON_IIR_CTL_PTR__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_IIR_CTL_VAL (0x3FC) +#define TAIKO_A_CDC_MAD_BEACON_IIR_CTL_VAL__POR (0x00) + +/* Taiko v2+ registers */ +#define TAIKO_A_CDC_TX_1_GAIN (0x153) +#define TAIKO_A_CDC_TX_1_GAIN__POR (0x02) +#define TAIKO_A_CDC_TX_2_GAIN (0x155) +#define TAIKO_A_CDC_TX_2_GAIN__POR (0x02) +#define TAIKO_A_CDC_TX_1_2_ADC_IB (0x156) +#define TAIKO_A_CDC_TX_1_2_ADC_IB__POR (0x44) +#define TAIKO_A_CDC_TX_3_GAIN (0x15D) +#define TAIKO_A_CDC_TX_3_GAIN__POR (0x02) +#define TAIKO_A_CDC_TX_4_GAIN (0x15F) +#define TAIKO_A_CDC_TX_4_GAIN__POR (0x02) +#define TAIKO_A_CDC_TX_3_4_ADC_IB (0x160) +#define TAIKO_A_CDC_TX_3_4_ADC_IB__POR (0x44) +#define TAIKO_A_CDC_TX_5_GAIN (0x167) +#define TAIKO_A_CDC_TX_5_GAIN__POR (0x02) +#define TAIKO_A_CDC_TX_6_GAIN (0x169) +#define TAIKO_A_CDC_TX_6_GAIN__POR (0x02) +#define TAIKO_A_CDC_TX_5_6_ADC_IB (0x16A) +#define TAIKO_A_CDC_TX_5_6_ADC_IB__POR (0x44) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL0 (0x270) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL0__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL1 (0x271) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL1__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL2 (0x272) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL2__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL3 (0x273) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL3__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL4 (0x274) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL4__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL5 (0x275) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL5__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL6 (0x276) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL6__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL7 (0x277) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL7__POR (0x00) +#define TAIKO_A_CDC_VBAT_GAIN_UPD_MON (0x2FA) +#define TAIKO_A_CDC_VBAT_GAIN_UPD_MON__POR (0x00) +#define TAIKO_A_CDC_VBAT_GAIN_MON_VAL (0x2FB) +#define TAIKO_A_CDC_VBAT_GAIN_MON_VAL__POR (0x00) +#define TAIKO_A_CDC_PA_RAMP_B1_CTL (0x361) +#define TAIKO_A_CDC_PA_RAMP_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_PA_RAMP_B2_CTL (0x362) +#define TAIKO_A_CDC_PA_RAMP_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_PA_RAMP_B3_CTL (0x363) +#define TAIKO_A_CDC_PA_RAMP_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_PA_RAMP_B4_CTL (0x364) +#define TAIKO_A_CDC_PA_RAMP_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_B1_CTL (0x365) +#define TAIKO_A_CDC_SPKR_CLIPDET_B1_CTL__POR (0x00) + +/* SLIMBUS Slave Registers */ +#define TAIKO_SLIM_PGD_PORT_INT_EN0 (0x30) +#define TAIKO_SLIM_PGD_PORT_INT_STATUS_RX_0 (0x34) +#define TAIKO_SLIM_PGD_PORT_INT_STATUS_RX_1 (0x35) +#define TAIKO_SLIM_PGD_PORT_INT_STATUS_TX_0 (0x36) +#define TAIKO_SLIM_PGD_PORT_INT_STATUS_TX_1 (0x37) +#define TAIKO_SLIM_PGD_PORT_INT_CLR_RX_0 (0x38) +#define TAIKO_SLIM_PGD_PORT_INT_CLR_RX_1 (0x39) +#define TAIKO_SLIM_PGD_PORT_INT_CLR_TX_0 (0x3A) +#define TAIKO_SLIM_PGD_PORT_INT_CLR_TX_1 (0x3B) +#define TAIKO_SLIM_PGD_PORT_INT_RX_SOURCE0 (0x60) +#define TAIKO_SLIM_PGD_PORT_INT_TX_SOURCE0 (0x70) + +/* Macros for Packing Register Writes into a U32 */ +#define TAIKO_PACKED_REG_SIZE sizeof(u32) + +#define TAIKO_CODEC_PACK_ENTRY(reg, mask, val) ((val & 0xff)|\ + ((mask & 0xff) << 8)|((reg & 0xffff) << 16)) + +#define TAIKO_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xffff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0); + +#endif diff --git a/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h new file mode 100644 index 0000000000000000000000000000000000000000..7902cfbafad8c418c0f2b0739f7e0a048f7495e7 --- /dev/null +++ b/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h @@ -0,0 +1,361 @@ +#ifndef WCD9XXX_CODEC_DIGITAL_H + +#define WCD9XXX_CODEC_DIGITAL_H + +#define WCD9XXX_A_CHIP_CTL (0x00) +#define WCD9XXX_A_CHIP_CTL__POR (0x00000000) +#define WCD9XXX_A_CHIP_STATUS (0x01) +#define WCD9XXX_A_CHIP_STATUS__POR (0x00000000) +#define WCD9XXX_A_CHIP_ID_BYTE_0 (0x04) +#define WCD9XXX_A_CHIP_ID_BYTE_0__POR (0x00000000) +#define WCD9XXX_A_CHIP_ID_BYTE_1 (0x05) +#define WCD9XXX_A_CHIP_ID_BYTE_1__POR (0x00000000) +#define WCD9XXX_A_CHIP_ID_BYTE_2 (0x06) +#define WCD9XXX_A_CHIP_ID_BYTE_2__POR (0x00000000) +#define WCD9XXX_A_CHIP_ID_BYTE_3 (0x07) +#define WCD9XXX_A_CHIP_ID_BYTE_3__POR (0x00000001) +#define WCD9XXX_A_CHIP_VERSION (0x08) +#define WCD9XXX_A_CHIP_VERSION__POR (0x00000020) +#define WCD9XXX_A_SB_VERSION (0x09) +#define WCD9XXX_A_SB_VERSION__POR (0x00000010) +#define WCD9XXX_A_SLAVE_ID_1 (0x0C) +#define WCD9XXX_A_SLAVE_ID_1__POR (0x00000077) +#define WCD9XXX_A_SLAVE_ID_2 (0x0D) +#define WCD9XXX_A_SLAVE_ID_2__POR (0x00000066) +#define WCD9XXX_A_SLAVE_ID_3 (0x0E) +#define WCD9XXX_A_SLAVE_ID_3__POR (0x00000055) +#define WCD9XXX_A_CDC_CTL (0x80) +#define WCD9XXX_A_CDC_CTL__POR (0x00000000) +#define WCD9XXX_A_LEAKAGE_CTL (0x88) +#define WCD9XXX_A_LEAKAGE_CTL__POR (0x00000004) +#define WCD9XXX_A_INTR_MODE (0x90) +#define WCD9XXX_A_INTR_MASK0 (0x94) +#define WCD9XXX_A_INTR_STATUS0 (0x98) +#define WCD9XXX_A_INTR_CLEAR0 (0x9C) +#define WCD9XXX_A_INTR_LEVEL0 (0xA0) +#define WCD9XXX_A_INTR_LEVEL1 (0xA1) +#define WCD9XXX_A_INTR_LEVEL2 (0xA2) +#define WCD9XXX_A_RX_HPH_CNP_EN (0x1AB) +#define WCD9XXX_A_RX_HPH_CNP_EN__POR (0x80) +#define WCD9XXX_A_RX_HPH_CNP_EN (0x1AB) +#define WCD9XXX_A_RX_HPH_CNP_EN__POR (0x80) +#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL (0x101) +#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL__POR (0x50) +#define WCD9XXX_A_CLK_BUFF_EN1 (0x108) +#define WCD9XXX_A_CLK_BUFF_EN1__POR (0x04) +#define WCD9XXX_A_CLK_BUFF_EN2 (0x109) +#define WCD9XXX_A_CLK_BUFF_EN2__POR (0x02) +#define WCD9XXX_A_RX_COM_BIAS (0x1A2) +#define WCD9XXX_A_RX_COM_BIAS__POR (0x00) +#define WCD9XXX_A_RC_OSC_FREQ (0x1FA) +#define WCD9XXX_A_RC_OSC_FREQ__POR (0x46) +#define WCD9XXX_A_BIAS_OSC_BG_CTL (0x105) +#define WCD9XXX_A_BIAS_OSC_BG_CTL__POR (0x16) +#define WCD9XXX_A_RC_OSC_TEST (0x1FB) +#define WCD9XXX_A_RC_OSC_TEST__POR (0x0A) +#define WCD9XXX_A_CDC_CLK_MCLK_CTL (0x311) +#define WCD9XXX_A_CDC_CLK_MCLK_CTL__POR (0x00) + +#define WCD9XXX_A_CDC_MBHC_EN_CTL (0x3C0) +#define WCD9XXX_A_CDC_MBHC_EN_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG (0x3C1) +#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG (0x3C2) +#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG__POR (0x06) +#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL (0x3C3) +#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL__POR (0x03) +#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL (0x3C4) +#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL__POR (0x09) +#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL (0x3C5) +#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL__POR (0x1E) +#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL (0x3C6) +#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL__POR (0x45) +#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL (0x3C7) +#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL__POR (0x04) +#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL (0x3C8) +#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL__POR (0x78) +#define WCD9XXX_A_CDC_MBHC_B1_STATUS (0x3C9) +#define WCD9XXX_A_CDC_MBHC_B1_STATUS__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_B2_STATUS (0x3CA) +#define WCD9XXX_A_CDC_MBHC_B2_STATUS__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_B3_STATUS (0x3CB) +#define WCD9XXX_A_CDC_MBHC_B3_STATUS__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_B4_STATUS (0x3CC) +#define WCD9XXX_A_CDC_MBHC_B4_STATUS__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_B5_STATUS (0x3CD) +#define WCD9XXX_A_CDC_MBHC_B5_STATUS__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_B1_CTL (0x3CE) +#define WCD9XXX_A_CDC_MBHC_B1_CTL__POR (0xC0) +#define WCD9XXX_A_CDC_MBHC_B2_CTL (0x3CF) +#define WCD9XXX_A_CDC_MBHC_B2_CTL__POR (0x5D) +#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL (0x3D0) +#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL (0x3D1) +#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL (0x3D2) +#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL (0x3D3) +#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL (0x3D4) +#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL (0x3D5) +#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL (0x3D6) +#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL__POR (0xFF) +#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL (0x3D7) +#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL__POR (0x07) +#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL (0x3D8) +#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL__POR (0xFF) +#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL (0x3D9) +#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL__POR (0x7F) +#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL (0x3DA) +#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL (0x3DB) +#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL__POR (0x80) +#define WCD9XXX_A_CDC_MBHC_CLK_CTL (0x3DC) +#define WCD9XXX_A_CDC_MBHC_CLK_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_INT_CTL (0x3DD) +#define WCD9XXX_A_CDC_MBHC_INT_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL (0x3DE) +#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_SPARE (0x3DF) +#define WCD9XXX_A_CDC_MBHC_SPARE__POR (0x00) +#define WCD9XXX_A_MBHC_SCALING_MUX_1 (0x14E) +#define WCD9XXX_A_MBHC_SCALING_MUX_1__POR (0x00) +#define WCD9XXX_A_RX_HPH_OCP_CTL (0x1AA) +#define WCD9XXX_A_RX_HPH_OCP_CTL__POR (0x68) +#define WCD9XXX_A_MICB_1_CTL (0x12B) +#define WCD9XXX_A_MICB_1_CTL__POR (0x16) +#define WCD9XXX_A_MICB_1_INT_RBIAS (0x12C) +#define WCD9XXX_A_MICB_1_INT_RBIAS__POR (0x24) +#define WCD9XXX_A_MICB_1_MBHC (0x12D) +#define WCD9XXX_A_MICB_1_MBHC__POR (0x01) +#define WCD9XXX_A_MICB_CFILT_2_CTL (0x12E) +#define WCD9XXX_A_MICB_CFILT_2_CTL__POR (0x40) +#define WCD9XXX_A_MICB_CFILT_2_VAL (0x12F) +#define WCD9XXX_A_MICB_CFILT_2_VAL__POR (0x80) +#define WCD9XXX_A_MICB_CFILT_2_PRECHRG (0x130) +#define WCD9XXX_A_MICB_CFILT_2_PRECHRG__POR (0x38) +#define WCD9XXX_A_MICB_2_CTL (0x131) +#define WCD9XXX_A_MICB_2_CTL__POR (0x16) +#define WCD9XXX_A_MICB_2_INT_RBIAS (0x132) +#define WCD9XXX_A_MICB_2_INT_RBIAS__POR (0x24) +#define WCD9XXX_A_MICB_2_MBHC (0x133) +#define WCD9XXX_A_MICB_2_MBHC__POR (0x02) +#define WCD9XXX_A_MICB_CFILT_3_CTL (0x134) +#define WCD9XXX_A_MICB_CFILT_3_CTL__POR (0x40) +#define WCD9XXX_A_MICB_CFILT_3_VAL (0x135) +#define WCD9XXX_A_MICB_CFILT_3_VAL__POR (0x80) +#define WCD9XXX_A_MICB_CFILT_3_PRECHRG (0x136) +#define WCD9XXX_A_MICB_CFILT_3_PRECHRG__POR (0x38) +#define WCD9XXX_A_MICB_3_CTL (0x137) +#define WCD9XXX_A_MICB_3_CTL__POR (0x16) +#define WCD9XXX_A_MICB_3_INT_RBIAS (0x138) +#define WCD9XXX_A_MICB_3_INT_RBIAS__POR (0x24) +#define WCD9XXX_A_MICB_3_MBHC (0x139) +#define WCD9XXX_A_MICB_3_MBHC__POR (0x00) +#define WCD9XXX_A_MICB_4_CTL (0x13D) +#define WCD9XXX_A_MICB_4_CTL__POR (0x16) +#define WCD9XXX_A_MICB_4_INT_RBIAS (0x13E) +#define WCD9XXX_A_MICB_4_INT_RBIAS__POR (0x24) +#define WCD9XXX_A_MICB_4_MBHC (0x13F) +#define WCD9XXX_A_MICB_4_MBHC__POR (0x01) +#define WCD9XXX_A_MICB_CFILT_1_VAL (0x129) +#define WCD9XXX_A_MICB_CFILT_1_VAL__POR (0x80) +#define WCD9XXX_A_RX_HPH_L_STATUS (0x1B3) +#define WCD9XXX_A_RX_HPH_L_STATUS__POR (0x00) +#define WCD9XXX_A_MBHC_HPH (0x1FE) +#define WCD9XXX_A_MBHC_HPH__POR (0x44) +#define WCD9XXX_A_RX_HPH_CNP_WG_TIME (0x1AD) +#define WCD9XXX_A_RX_HPH_CNP_WG_TIME__POR (0x2A) +#define WCD9XXX_A_RX_HPH_R_DAC_CTL (0x1B7) +#define WCD9XXX_A_RX_HPH_R_DAC_CTL__POR (0x00) +#define WCD9XXX_A_RX_HPH_L_DAC_CTL (0x1B1) +#define WCD9XXX_A_RX_HPH_L_DAC_CTL__POR (0x00) +#define WCD9XXX_A_TX_7_MBHC_EN (0x171) +#define WCD9XXX_A_TX_7_MBHC_EN__POR (0x0C) +#define WCD9XXX_A_PIN_CTL_OE0 (0x010) +#define WCD9XXX_A_PIN_CTL_OE0__POR (0x00) +#define WCD9XXX_A_PIN_CTL_OE1 (0x011) +#define WCD9XXX_A_PIN_CTL_OE1__POR (0x00) +#define WCD9XXX_A_MICB_CFILT_1_CTL (0x128) +#define WCD9XXX_A_LDO_H_MODE_1 (0x110) +#define WCD9XXX_A_LDO_H_MODE_1__POR (0x65) +#define WCD9XXX_A_MICB_CFILT_1_CTL__POR (0x40) +#define WCD9XXX_A_TX_7_MBHC_TEST_CTL (0x174) +#define WCD9XXX_A_TX_7_MBHC_TEST_CTL__POR (0x38) +#define WCD9XXX_A_MBHC_SCALING_MUX_2 (0x14F) +#define WCD9XXX_A_MBHC_SCALING_MUX_2__POR (0x80) +#define WCD9XXX_A_TX_COM_BIAS (0x14C) +#define WCD9XXX_A_TX_COM_BIAS__POR (0xF0) + +#define WCD9XXX_A_MBHC_INSERT_DETECT (0x14A) /* TAIKO and later */ +#define WCD9XXX_A_MBHC_INSERT_DETECT__POR (0x00) +#define WCD9XXX_A_MBHC_INSERT_DET_STATUS (0x14B) /* TAIKO and later */ +#define WCD9XXX_A_MBHC_INSERT_DET_STATUS__POR (0x00) +#define WCD9XXX_A_MAD_ANA_CTRL (0x150) +#define WCD9XXX_A_MAD_ANA_CTRL__POR (0xF1) + + +#define WCD9XXX_A_CDC_CLK_OTHR_CTL (0x30C) +#define WCD9XXX_A_CDC_CLK_OTHR_CTL__POR (0x00) + +/* Class H related common registers */ +#define WCD9XXX_A_BUCK_MODE_1 (0x181) +#define WCD9XXX_A_BUCK_MODE_1__POR (0x21) +#define WCD9XXX_A_BUCK_MODE_2 (0x182) +#define WCD9XXX_A_BUCK_MODE_2__POR (0xFF) +#define WCD9XXX_A_BUCK_MODE_3 (0x183) +#define WCD9XXX_A_BUCK_MODE_3__POR (0xCC) +#define WCD9XXX_A_BUCK_MODE_4 (0x184) +#define WCD9XXX_A_BUCK_MODE_4__POR (0x3A) +#define WCD9XXX_A_BUCK_MODE_5 (0x185) +#define WCD9XXX_A_BUCK_MODE_5__POR (0x00) +#define WCD9XXX_A_BUCK_CTRL_VCL_1 (0x186) +#define WCD9XXX_A_BUCK_CTRL_VCL_1__POR (0x48) +#define WCD9XXX_A_BUCK_CTRL_VCL_2 (0x187) +#define WCD9XXX_A_BUCK_CTRL_VCL_2__POR (0xA3) +#define WCD9XXX_A_BUCK_CTRL_VCL_3 (0x188) +#define WCD9XXX_A_BUCK_CTRL_VCL_3__POR (0x82) +#define WCD9XXX_A_BUCK_CTRL_CCL_1 (0x189) +#define WCD9XXX_A_BUCK_CTRL_CCL_1__POR (0xAB) +#define WCD9XXX_A_BUCK_CTRL_CCL_2 (0x18A) +#define WCD9XXX_A_BUCK_CTRL_CCL_2__POR (0xDC) +#define WCD9XXX_A_BUCK_CTRL_CCL_3 (0x18B) +#define WCD9XXX_A_BUCK_CTRL_CCL_3__POR (0x6A) +#define WCD9XXX_A_BUCK_CTRL_CCL_4 (0x18C) +#define WCD9XXX_A_BUCK_CTRL_CCL_4__POR (0x58) +#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_1 (0x18D) +#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_1__POR (0x50) +#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_2 (0x18E) +#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_2__POR (0x64) +#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_3 (0x18F) +#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_3__POR (0x77) +#define WCD9XXX_A_BUCK_TMUX_A_D (0x190) +#define WCD9XXX_A_BUCK_TMUX_A_D__POR (0x00) +#define WCD9XXX_A_NCP_EN (0x192) +#define WCD9XXX_A_NCP_EN__POR (0xFE) +#define WCD9XXX_A_NCP_STATIC (0x194) +#define WCD9XXX_A_NCP_STATIC__POR (0x28) +#define WCD9XXX_A_NCP_BUCKREF (0x191) +#define WCD9XXX_A_NCP_BUCKREF__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_B1_CTL (0x320) +#define WCD9XXX_A_CDC_CLSH_B1_CTL__POR (0xE4) +#define WCD9XXX_A_CDC_CLSH_B2_CTL (0x321) +#define WCD9XXX_A_CDC_CLSH_B2_CTL__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_B3_CTL (0x322) +#define WCD9XXX_A_CDC_CLSH_B3_CTL__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS (0x323) +#define WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD (0x324) +#define WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD__POR (0x12) +#define WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD (0x325) +#define WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD__POR (0x0C) +#define WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD (0x326) +#define WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR (0x18) +#define WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD (0x327) +#define WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR (0x23) +#define WCD9XXX_A_CDC_CLSH_K_ADDR (0x328) +#define WCD9XXX_A_CDC_CLSH_K_ADDR__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_K_DATA (0x329) +#define WCD9XXX_A_CDC_CLSH_K_DATA__POR (0xA4) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L (0x32A) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L__POR (0xD7) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U (0x32B) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U__POR (0x05) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L (0x32C) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L__POR (0x60) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U (0x32D) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U__POR (0x09) +#define WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR (0x32E) +#define WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH (0x32F) +#define WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR (0x330) +#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH (0x331) +#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH__POR (0x00) + +#define WCD9XXX_A_CDC_RX1_B6_CTL (0x2B5) +#define WCD9XXX_A_CDC_RX1_B6_CTL__POR (0x80) +#define WCD9XXX_A_CDC_RX2_B6_CTL (0x2BD) +#define WCD9XXX_A_CDC_RX2_B6_CTL__POR (0x80) +#define WCD9XXX_A_RX_HPH_L_GAIN (0x1AE) +#define WCD9XXX_A_RX_HPH_L_GAIN__POR (0x00) +#define WCD9XXX_A_RX_HPH_R_GAIN (0x1B4) +#define WCD9XXX_A_RX_HPH_R_GAIN__POR (0x00) +#define WCD9XXX_A_RX_HPH_CHOP_CTL (0x1A5) +#define WCD9XXX_A_RX_HPH_CHOP_CTL__POR (0xB4) +#define WCD9XXX_A_RX_HPH_BIAS_PA (0x1A6) +#define WCD9XXX_A_RX_HPH_BIAS_PA__POR (0x7A) +#define WCD9XXX_A_RX_HPH_L_TEST (0x1AF) +#define WCD9XXX_A_RX_HPH_L_TEST__POR (0x00) +#define WCD9XXX_A_RX_HPH_R_TEST (0x1B5) +#define WCD9XXX_A_RX_HPH_R_TEST__POR (0x00) +#define WCD9XXX_A_CDC_CLK_RX_B1_CTL (0x30F) +#define WCD9XXX_A_CDC_CLK_RX_B1_CTL__POR (0x00) +#define WCD9XXX_A_NCP_CLK (0x193) +#define WCD9XXX_A_NCP_CLK__POR (0x94) +#define WCD9XXX_A_RX_HPH_BIAS_WG_OCP (0x1A9) +#define WCD9XXX_A_RX_HPH_BIAS_WG_OCP__POR (0x2A) +#define WCD9XXX_A_RX_HPH_CNP_WG_CTL (0x1AC) +#define WCD9XXX_A_RX_HPH_CNP_WG_CTL__POR (0xDE) +#define WCD9XXX_A_RX_HPH_L_PA_CTL (0x1B0) +#define WCD9XXX_A_RX_HPH_L_PA_CTL__POR (0x42) +#define WCD9XXX_A_RX_HPH_R_PA_CTL (0x1B6) +#define WCD9XXX_A_RX_HPH_R_PA_CTL__POR (0x42) +#define WCD9XXX_A_CDC_CONN_RX2_B1_CTL (0x383) +#define WCD9XXX_A_CDC_CONN_RX2_B1_CTL__POR (0x00) +#define WCD9XXX_A_CDC_PA_RAMP_B1_CTL (0x361) +#define WCD9XXX_A_CDC_PA_RAMP_B1_CTL__POR (0x00) +#define WCD9XXX_A_CDC_PA_RAMP_B2_CTL (0x362) +#define WCD9XXX_A_CDC_PA_RAMP_B2_CTL__POR (0x00) +#define WCD9XXX_A_CDC_PA_RAMP_B3_CTL (0x363) +#define WCD9XXX_A_CDC_PA_RAMP_B3_CTL__POR (0x00) +#define WCD9XXX_A_CDC_PA_RAMP_B4_CTL (0x364) +#define WCD9XXX_A_CDC_PA_RAMP_B4_CTL__POR (0x00) + +#define WCD9330_A_LEAKAGE_CTL (0x03C) +#define WCD9330_A_LEAKAGE_CTL__POR (0x04) +#define WCD9330_A_CDC_CTL (0x034) +#define WCD9330_A_CDC_CTL__POR (0x00) + +/* Class-H registers for codecs from and above WCD9335 */ +#define WCD9XXX_A_CDC_RX0_RX_PATH_CFG0 (0xB42) +#define WCD9XXX_A_CDC_RX1_RX_PATH_CFG0 (0xB56) +#define WCD9XXX_A_CDC_RX2_RX_PATH_CFG0 (0xB6A) +#define WCD9XXX_A_CDC_CLSH_K1_MSB (0xC08) +#define WCD9XXX_A_CDC_CLSH_K1_LSB (0xC09) +#define WCD9XXX_A_ANA_RX_SUPPLIES (0x608) +#define WCD9XXX_A_ANA_HPH (0x609) +#define WCD9XXX_A_CDC_CLSH_CRC (0xC01) +#define WCD9XXX_FLYBACK_EN (0x6A4) +#define WCD9XXX_FLYBACK_VNEG_CTRL_1 (0x6A5) +#define WCD9XXX_FLYBACK_VNEGDAC_CTRL_2 (0x6AF) +#define WCD9XXX_RX_BIAS_FLYB_BUFF (0x6C7) +#define WCD9XXX_HPH_L_EN (0x6D3) +#define WCD9XXX_HPH_R_EN (0x6D6) +#define WCD9XXX_HPH_REFBUFF_UHQA_CTL (0x6DD) +#define WCD9XXX_CLASSH_CTRL_VCL_2 (0x69B) +#define WCD9XXX_CDC_CLSH_HPH_V_PA (0xC04) +#define WCD9XXX_CDC_RX0_RX_PATH_SEC0 (0xB49) +#define WCD9XXX_CDC_RX1_RX_PATH_CTL (0xB55) +#define WCD9XXX_CDC_RX2_RX_PATH_CTL (0xB69) +#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL (0xD41) +#define WCD9XXX_CLASSH_CTRL_CCL_1 (0x69C) + +/* RX Gain control registers of codecs from and above WCD9335 */ +#define WCD9XXX_CDC_RX1_RX_VOL_CTL (0xB59) +#define WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL (0xB5C) +#define WCD9XXX_CDC_RX1_RX_PATH_SEC1 (0xB5E) +#define WCD9XXX_CDC_RX2_RX_VOL_CTL (0xB6D) +#define WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL (0xB70) +#define WCD9XXX_CDC_RX2_RX_PATH_SEC1 (0xB72) + +/* Class-H registers for codecs from and above WCD934X */ +#define WCD9XXX_HPH_CNP_WG_CTL (0x06cc) +#define WCD9XXX_FLYBACK_VNEG_CTRL_4 (0x06a8) +#define WCD9XXX_HPH_NEW_INT_PA_MISC2 (0x0738) +#define WCD9XXX_RX_BIAS_HPH_LOWPOWER (0x06bf) +#define WCD9XXX_HPH_PA_CTL1 (0x06d1) +#endif diff --git a/include/uapi/sound/Kbuild b/include/uapi/sound/Kbuild index 5b0a9bc03b5df277cf2b6aa18da77bc753127e2f..b0350f051c0ddd72c92b0fa9e5b2c0ee534f9957 100644 --- a/include/uapi/sound/Kbuild +++ b/include/uapi/sound/Kbuild @@ -21,3 +21,4 @@ header-y += audio_effects.h header-y += voice_svc.h header-y += devdep_params.h header-y += msmcal-hwdep.h +header-y += wcd-dsp-glink.h diff --git a/include/uapi/sound/wcd-dsp-glink.h b/include/uapi/sound/wcd-dsp-glink.h new file mode 100644 index 0000000000000000000000000000000000000000..39d128d370a078b9fb24316b12c74642a26d7993 --- /dev/null +++ b/include/uapi/sound/wcd-dsp-glink.h @@ -0,0 +1,60 @@ +#ifndef _WCD_DSP_GLINK_H +#define _WCD_DSP_GLINK_H + +#include + +#define WDSP_CH_NAME_MAX_LEN 50 + +enum { + WDSP_REG_PKT = 1, + WDSP_CMD_PKT, + WDSP_READY_PKT, +}; +#define WDSP_READY_PKT WDSP_READY_PKT + +/* + * struct wdsp_reg_pkt - Glink channel information structure format + * @no_of_channels: Number of glink channels to open + * @payload[0]: Dynamic array contains all the glink channels information + */ +struct wdsp_reg_pkt { + __u8 no_of_channels; + __u8 payload[0]; +}; + +/* + * struct wdsp_cmd_pkt - WDSP command packet format + * @ch_name: Name of the glink channel + * @payload_size: Size of the payload + * @payload[0]: Actual data payload + */ +struct wdsp_cmd_pkt { + char ch_name[WDSP_CH_NAME_MAX_LEN]; + __u32 payload_size; + __u8 payload[0]; +}; + +/* + * struct wdsp_write_pkt - Format that userspace send the data to driver. + * @pkt_type: Type of the packet(REG or CMD PKT) + * @payload[0]: Payload is either cmd or reg pkt structure based on pkt type + */ +struct wdsp_write_pkt { + __u8 pkt_type; + __u8 payload[0]; +}; + +/* + * struct wdsp_glink_ch_cfg - Defines the glink channel configuration. + * @ch_name: Name of the glink channel + * @latency_in_us: Latency specified in micro seconds for QOS + * @no_of_intents: Number of intents prequeued + * @intents_size[0]: Dynamic array to specify size of each intent + */ +struct wdsp_glink_ch_cfg { + char name[WDSP_CH_NAME_MAX_LEN]; + __u32 latency_in_us; + __u32 no_of_intents; + __u32 intents_size[0]; +}; +#endif /* _WCD_DSP_GLINK_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c67667bb970f1729db65b027dd8e963b9ee95b7b..9685b0253ab9b70e154cdabc0d7f6dc058bc095a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -875,6 +875,71 @@ config SND_SOC_UDA134X config SND_SOC_UDA1380 tristate +config SND_SOC_WCD934X_DSD + tristate + +config SND_SOC_WCD9320 + tristate + +config SND_SOC_WCD9330 + tristate + depends on WCD9330_CODEC + +config SND_SOC_WCD9335 + tristate + depends on WCD9335_CODEC + +config SND_SOC_WCD934X + tristate + depends on WCD934X_CODEC + select SND_SOC_WCD9XXX_V2 + select AUDIO_EXT_CLK + select SND_SOC_WCD_DSP_MGR + select SND_SOC_WCD_SPI + select SND_SOC_WCD934X_MBHC + select SND_SOC_WCD934X_DSD + +config SND_SOC_WCD934X_MBHC + tristate + depends on SND_SOC_WCD934X + select SND_SOC_WCD_MBHC + +config SND_SOC_WSA881X + tristate + select MSM_CDC_PINCTRL + select REGMAP_SWR + +config SND_SOC_WSA881X_ANALOG + tristate + select REGMAP_I2C + +config SND_SOC_WCD9XXX + tristate + default y if SND_SOC_WCD9320=y || SND_SOC_WCD9330=y || SND_SOC_WCD9335=y + +config SND_SOC_WCD9XXX_V2 + tristate + default y if SND_SOC_WCD9335=y + +config SND_SOC_WCD_CPE + tristate + default y if SND_SOC_WCD9330=y || SND_SOC_WCD9335=y + +config AUDIO_EXT_CLK + tristate + default y if SND_SOC_WCD9335=y || SND_SOC_WCD9330=y || SND_SOC_MSM8X16_WCD=y + +config SND_SOC_WCD_MBHC + tristate + default y if (SND_SOC_MSM8909_WCD=y || SND_SOC_MSM8X16_WCD=y || SND_SOC_WCD9335=y) && SND_SOC_MDMCALIFORNIUM!=y + +config SND_SOC_WCD_DSP_MGR + tristate + +config SND_SOC_WCD_SPI + depends on CONFIG_SPI + tristate + config SND_SOC_WL1273 tristate @@ -1089,4 +1154,16 @@ config SND_SOC_TPA6130A2 tristate "Texas Instruments TPA6130A2 headphone amplifier" depends on I2C +config SND_SOC_MSM_STUB + tristate + +config SND_SOC_MSM_HDMI_CODEC_RX + bool "HDMI Audio Playback" + depends on FB_MSM_MDSS_HDMI_PANEL && (SND_SOC_APQ8084 || SND_SOC_MSM8994 || SND_SOC_MSM8996 || SND_SOC_MSM8998) + help + HDMI audio drivers should be built only if the platform + supports hdmi panel. + +source "sound/soc/codecs/msm8x16/Kconfig" + endmenu diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 958cd4912fbc9820f965c0d2f38692857f410d3a..648bd2bf8c96aebf385295764b9ff93d68fcb723 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -158,6 +158,27 @@ snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o snd-soc-uda134x-objs := uda134x.o snd-soc-uda1380-objs := uda1380.o +snd-soc-wcd9320-objs := wcd9320.o wcd9320-tables.o +snd-soc-wcd9330-objs := wcd9330.o wcd9330-tables.o +snd-soc-wcd9335-objs := wcd9335.o +snd-soc-wcd934x-objs := wcd934x.o +snd-soc-wcd9xxx-objs := wcd9xxx-resmgr.o wcd9xxx-mbhc.o wcd9xxx-common.o wcdcal-hwdep.o +snd-soc-wcd9xxx-v2-objs := wcd9xxx-common-v2.o wcd9xxx-resmgr-v2.o +ifeq ($(CONFIG_COMMON_CLK_MSM), y) + audio-ext-clock-objs := audio-ext-clk.o +endif + +ifeq ($(CONFIG_COMMON_CLK_QCOM), y) + audio-ext-clock-up-objs := audio-ext-clk-up.o +endif +snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o +snd-soc-wsa881x-objs := wsa881x.o wsa881x-tables.o wsa881x-regmap.o wsa881x-temp-sensor.o +snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o +snd-soc-wsa881x-analog-objs := wsa881x-analog.o wsa881x-tables-analog.o +snd-soc-wsa881x-analog-objs += wsa881x-regmap-analog.o wsa881x-irq.o +snd-soc-wcd-dsp-utils-objs := wcd-dsp-utils.o +snd-soc-wcd-dsp-mgr-objs := wcd-dsp-mgr.o +snd-soc-wcd-spi-objs := wcd-spi.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o snd-soc-wm0010-objs := wm0010.o @@ -214,6 +235,8 @@ snd-soc-wm9705-objs := wm9705.o snd-soc-wm9712-objs := wm9712.o snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o +snd-soc-msm-stub-objs := msm_stub.o +obj-$(CONFIG_SND_SOC_MSM_HDMI_CODEC_RX) := msm_hdmi_codec_rx.o # Amp snd-soc-max9877-objs := max9877.o @@ -379,7 +402,25 @@ obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o +obj-$(CONFIG_SND_SOC_WCD9320) += snd-soc-wcd9320.o +obj-$(CONFIG_SND_SOC_WCD9330) += snd-soc-wcd9330.o +obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o +obj-$(CONFIG_SND_SOC_WCD934X) += wcd934x/ +ifeq ($(CONFIG_COMMON_CLK_MSM), y) + obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clock.o +endif +ifeq ($(CONFIG_COMMON_CLK_QCOM), y) + obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clock-up.o +endif +obj-$(CONFIG_SND_SOC_WCD9XXX) += snd-soc-wcd9xxx.o +obj-$(CONFIG_SND_SOC_WCD9XXX_V2) += snd-soc-wcd9xxx-v2.o +obj-$(CONFIG_SND_SOC_WCD_CPE) += snd-soc-wcd-cpe.o +obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o +obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o +obj-$(CONFIG_SND_SOC_WSA881X_ANALOG) += snd-soc-wsa881x-analog.o obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o +obj-$(CONFIG_SND_SOC_WCD_DSP_MGR) += snd-soc-wcd-dsp-mgr.o snd-soc-wcd-dsp-utils.o +obj-$(CONFIG_SND_SOC_WCD_SPI) += snd-soc-wcd-spi.o obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o @@ -435,8 +476,10 @@ obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o +obj-$(CONFIG_SND_SOC_MSM_STUB) += snd-soc-msm-stub.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o +obj-y += msm8x16/ diff --git a/sound/soc/codecs/audio-ext-clk-up.c b/sound/soc/codecs/audio-ext-clk-up.c new file mode 100644 index 0000000000000000000000000000000000000000..498ef7b41e7169c1da28095599643371a83e298a --- /dev/null +++ b/sound/soc/codecs/audio-ext-clk-up.c @@ -0,0 +1,577 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../../../drivers/clk/qcom/common.h" +#include +#include +#include +#include +#include + +enum audio_clk_mux { + AP_CLK2, + LPASS_MCLK, + LPASS_MCLK2, +}; + +struct pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *sleep; + struct pinctrl_state *active; +}; + +struct audio_ext_ap_clk { + bool enabled; + int gpio; + struct clk_fixed_factor fact; +}; + +struct audio_ext_pmi_clk { + int gpio; + struct clk_fixed_factor fact; +}; + +struct audio_ext_ap_clk2 { + bool enabled; + struct pinctrl_info pnctrl_info; + struct clk_fixed_factor fact; +}; + +struct audio_ext_lpass_mclk { + struct pinctrl_info pnctrl_info; + struct clk_fixed_factor fact; +}; + +static struct afe_clk_set clk2_config = { + Q6AFE_LPASS_CLK_CONFIG_API_VERSION, + Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR, + Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static struct afe_clk_set lpass_default = { + Q6AFE_LPASS_CLK_CONFIG_API_VERSION, + Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR, + Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static struct afe_clk_set lpass_mclk = { + Q6AFE_LPASS_CLK_CONFIG_API_VERSION, + Q6AFE_LPASS_CLK_ID_MCLK_1, + Q6AFE_LPASS_OSR_CLK_11_P2896_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk_hw *hw) +{ + return container_of(hw, struct audio_ext_ap_clk, fact.hw); +} + +static int audio_ext_clk_prepare(struct clk_hw *hw) +{ + struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(hw); + + pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio); + if (gpio_is_valid(audio_clk->gpio)) + return gpio_direction_output(audio_clk->gpio, 1); + return 0; +} + +static void audio_ext_clk_unprepare(struct clk_hw *hw) +{ + struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(hw); + + pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio); + if (gpio_is_valid(audio_clk->gpio)) + gpio_direction_output(audio_clk->gpio, 0); +} + +static inline struct audio_ext_ap_clk2 *to_audio_ap_clk2(struct clk_hw *hw) +{ + return container_of(hw, struct audio_ext_ap_clk2, fact.hw); +} + +static int audio_ext_clk2_prepare(struct clk_hw *hw) +{ + struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(hw); + struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info; + int ret; + + + if (!pnctrl_info->pinctrl || !pnctrl_info->active) + return 0; + + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->active); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return -EIO; + } + + clk2_config.enable = 1; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config); + if (ret < 0) { + pr_err("%s: failed to set clock, ret = %d\n", __func__, ret); + return -EINVAL; + } + + return 0; +} + +static void audio_ext_clk2_unprepare(struct clk_hw *hw) +{ + struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(hw); + struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info; + int ret; + + if (!pnctrl_info->pinctrl || !pnctrl_info->sleep) + return; + + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) + pr_err("%s: sleep state select failed with %d\n", + __func__, ret); + + clk2_config.enable = 0; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config); + if (ret < 0) + pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret); +} + +static inline struct audio_ext_lpass_mclk *to_audio_lpass_mclk( + struct clk_hw *hw) +{ + return container_of(hw, struct audio_ext_lpass_mclk, fact.hw); +} + +static int audio_ext_lpass_mclk_prepare(struct clk_hw *hw) +{ + struct audio_ext_lpass_mclk *audio_lpass_mclk = to_audio_lpass_mclk(hw); + struct pinctrl_info *pnctrl_info = &audio_lpass_mclk->pnctrl_info; + int ret; + + if (pnctrl_info->pinctrl) { + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->active); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return -EIO; + } + } + + lpass_mclk.enable = 1; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX, + &lpass_mclk); + if (ret < 0) { + pr_err("%s afe_set_digital_codec_core_clock failed\n", + __func__); + return ret; + } + + return 0; +} + +static void audio_ext_lpass_mclk_unprepare(struct clk_hw *hw) +{ + struct audio_ext_lpass_mclk *audio_lpass_mclk = to_audio_lpass_mclk(hw); + struct pinctrl_info *pnctrl_info = &audio_lpass_mclk->pnctrl_info; + int ret; + + if (pnctrl_info->pinctrl) { + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return; + } + } + + lpass_mclk.enable = 0; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX, + &lpass_mclk); + if (ret < 0) + pr_err("%s: afe_set_digital_codec_core_clock failed, ret = %d\n", + __func__, ret); +} + +static int audio_ext_lpass_mclk2_prepare(struct clk_hw *hw) +{ + struct audio_ext_lpass_mclk *audio_lpass_mclk2 = + to_audio_lpass_mclk(hw); + struct pinctrl_info *pnctrl_info = &audio_lpass_mclk2->pnctrl_info; + int ret; + + if (pnctrl_info->pinctrl) { + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->active); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return -EIO; + } + } + + lpass_default.enable = 1; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &lpass_default); + if (ret < 0) { + pr_err("%s: failed to set clock, ret = %d\n", __func__, ret); + return -EINVAL; + } + + return 0; +} + +static void audio_ext_lpass_mclk2_unprepare(struct clk_hw *hw) +{ + struct audio_ext_lpass_mclk *audio_lpass_mclk2 = + to_audio_lpass_mclk(hw); + struct pinctrl_info *pnctrl_info = &audio_lpass_mclk2->pnctrl_info; + int ret; + + if (pnctrl_info->pinctrl) { + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) + pr_err("%s: sleep state select failed with %d\n", + __func__, ret); + } + + lpass_default.enable = 0; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &lpass_default); + if (ret < 0) + pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret); +} + +static const struct clk_ops audio_ext_ap_clk_ops = { + .prepare = audio_ext_clk_prepare, + .unprepare = audio_ext_clk_unprepare, +}; + +static const struct clk_ops audio_ext_ap_clk2_ops = { + .prepare = audio_ext_clk2_prepare, + .unprepare = audio_ext_clk2_unprepare, +}; + +static const struct clk_ops audio_ext_lpass_mclk_ops = { + .prepare = audio_ext_lpass_mclk_prepare, + .unprepare = audio_ext_lpass_mclk_unprepare, +}; + +static const struct clk_ops audio_ext_lpass_mclk2_ops = { + .prepare = audio_ext_lpass_mclk2_prepare, + .unprepare = audio_ext_lpass_mclk2_unprepare, +}; + +static struct audio_ext_pmi_clk audio_pmi_clk = { + .gpio = -EINVAL, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_ext_pmi_clk", + .ops = &clk_dummy_ops, + }, + }, +}; + +static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = { + .gpio = -EINVAL, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_ext_pmi_lnbb_clk", + .ops = &clk_dummy_ops, + }, + }, +}; + +static struct audio_ext_ap_clk audio_ap_clk = { + .gpio = -EINVAL, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_ap_clk", + .ops = &audio_ext_ap_clk_ops, + }, + }, +}; + +static struct audio_ext_ap_clk2 audio_ap_clk2 = { + .enabled = false, + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_ap_clk2", + .ops = &audio_ext_ap_clk2_ops, + }, + }, +}; + +static struct audio_ext_lpass_mclk audio_lpass_mclk = { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_lpass_mclk", + .ops = &audio_ext_lpass_mclk_ops, + }, + }, +}; + +static struct audio_ext_lpass_mclk audio_lpass_mclk2 = { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_lpass_mclk2", + .ops = &audio_ext_lpass_mclk2_ops, + }, + }, +}; + +static struct clk_hw *audio_msm_hws[] = { + &audio_pmi_clk.fact.hw, + &audio_pmi_lnbb_clk.fact.hw, + &audio_ap_clk.fact.hw, + &audio_ap_clk2.fact.hw, + &audio_lpass_mclk.fact.hw, + &audio_lpass_mclk2.fact.hw, +}; + +static int audio_get_pinctrl(struct platform_device *pdev, + enum audio_clk_mux mux) +{ + struct pinctrl_info *pnctrl_info; + struct pinctrl *pinctrl; + int ret; + + switch (mux) { + case AP_CLK2: + pnctrl_info = &audio_ap_clk2.pnctrl_info; + break; + case LPASS_MCLK: + pnctrl_info = &audio_lpass_mclk.pnctrl_info; + break; + case LPASS_MCLK2: + pnctrl_info = &audio_lpass_mclk2.pnctrl_info; + break; + default: + dev_err(&pdev->dev, "%s Not a valid MUX ID: %d\n", + __func__, mux); + return -EINVAL; + } + pnctrl_info = &audio_ap_clk2.pnctrl_info; + + if (pnctrl_info->pinctrl) { + dev_dbg(&pdev->dev, "%s: already requested before\n", + __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + dev_dbg(&pdev->dev, "%s: Unable to get pinctrl handle\n", + __func__); + return -EINVAL; + } + pnctrl_info->pinctrl = pinctrl; + /* get all state handles from Device Tree */ + pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep"); + if (IS_ERR(pnctrl_info->sleep)) { + dev_err(&pdev->dev, "%s: could not get sleep pinstate\n", + __func__); + goto err; + } + pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active"); + if (IS_ERR(pnctrl_info->active)) { + dev_err(&pdev->dev, "%s: could not get active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) { + dev_err(&pdev->dev, "%s: Disable TLMM pins failed with %d\n", + __func__, ret); + goto err; + } + return 0; + +err: + devm_pinctrl_put(pnctrl_info->pinctrl); + return -EINVAL; +} + +static int audio_ref_clk_probe(struct platform_device *pdev) +{ + int clk_gpio; + int ret; + u32 mclk_freq; + struct clk *audio_clk; + struct device *dev = &pdev->dev; + int i; + struct clk_onecell_data *clk_data; + + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,codec-mclk-clk-freq", + &mclk_freq); + if (!ret) { + lpass_mclk.clk_freq_in_hz = mclk_freq; + + ret = audio_get_pinctrl(pdev, LPASS_MCLK); + if (ret) + dev_err(&pdev->dev, "%s: Parsing pinctrl %s failed\n", + __func__, "LPASS_MCLK"); + ret = audio_get_pinctrl(pdev, LPASS_MCLK2); + if (ret) + dev_dbg(&pdev->dev, "%s: Parsing pinctrl %s failed\n", + __func__, "LPASS_MCLK2"); + } + + clk_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,audio-ref-clk-gpio", 0); + if (clk_gpio > 0) { + ret = gpio_request(clk_gpio, "EXT_CLK"); + if (ret) { + dev_err(&pdev->dev, + "Request ext clk gpio failed %d, err:%d\n", + clk_gpio, ret); + goto err; + } + if (of_property_read_bool(pdev->dev.of_node, + "qcom,node_has_rpm_clock")) { + audio_pmi_clk.gpio = clk_gpio; + } else + audio_ap_clk.gpio = clk_gpio; + + } + + ret = audio_get_pinctrl(pdev, AP_CLK2); + if (ret) + dev_dbg(&pdev->dev, "%s: Parsing pinctrl failed\n", + __func__); + + clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + goto err_gpio; + + clk_data->clk_num = ARRAY_SIZE(audio_msm_hws); + clk_data->clks = devm_kzalloc(&pdev->dev, + clk_data->clk_num * sizeof(struct clk *), + GFP_KERNEL); + if (!clk_data->clks) + goto err_clk; + + for (i = 0; i < ARRAY_SIZE(audio_msm_hws); i++) { + audio_clk = devm_clk_register(dev, audio_msm_hws[i]); + if (IS_ERR(audio_clk)) { + dev_err(&pdev->dev, + "%s: audio ref clock i = %d register failed\n", + __func__, i); + return PTR_ERR(audio_clk); + } + clk_data->clks[i] = audio_clk; + } + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, clk_data); + if (ret) { + dev_err(&pdev->dev, "%s: audio ref clock register failed\n", + __func__); + goto err_gpio; + } + + return 0; + +err_clk: + if (clk_data) + devm_kfree(&pdev->dev, clk_data->clks); + devm_kfree(&pdev->dev, clk_data); +err_gpio: + gpio_free(clk_gpio); + +err: + return ret; +} + +static int audio_ref_clk_remove(struct platform_device *pdev) +{ + struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info; + + if (audio_pmi_clk.gpio > 0) + gpio_free(audio_pmi_clk.gpio); + else if (audio_ap_clk.gpio > 0) + gpio_free(audio_ap_clk.gpio); + + if (pnctrl_info->pinctrl) { + devm_pinctrl_put(pnctrl_info->pinctrl); + pnctrl_info->pinctrl = NULL; + } + + return 0; +} + +static const struct of_device_id audio_ref_clk_match[] = { + {.compatible = "qcom,audio-ref-clk"}, + {} +}; +MODULE_DEVICE_TABLE(of, audio_ref_clk_match); + +static struct platform_driver audio_ref_clk_driver = { + .driver = { + .name = "audio-ref-clk", + .owner = THIS_MODULE, + .of_match_table = audio_ref_clk_match, + }, + .probe = audio_ref_clk_probe, + .remove = audio_ref_clk_remove, +}; + +static int __init audio_ref_clk_platform_init(void) +{ + return platform_driver_register(&audio_ref_clk_driver); +} +module_init(audio_ref_clk_platform_init); + +static void __exit audio_ref_clk_platform_exit(void) +{ + platform_driver_unregister(&audio_ref_clk_driver); +} +module_exit(audio_ref_clk_platform_exit); + +MODULE_DESCRIPTION("Audio Ref Up Clock module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/audio-ext-clk.c b/sound/soc/codecs/audio-ext-clk.c new file mode 100644 index 0000000000000000000000000000000000000000..ef795dfe99200c6074a661ae0d67b21969397e1a --- /dev/null +++ b/sound/soc/codecs/audio-ext-clk.c @@ -0,0 +1,349 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *sleep; + struct pinctrl_state *active; +}; + +struct audio_ext_ap_clk { + bool enabled; + int gpio; + struct clk c; +}; + +struct audio_ext_pmi_clk { + int gpio; + struct clk c; +}; + +struct audio_ext_ap_clk2 { + bool enabled; + struct pinctrl_info pnctrl_info; + struct clk c; +}; + +static struct afe_clk_set clk2_config = { + Q6AFE_LPASS_CLK_CONFIG_API_VERSION, + Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR, + Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk *clk) +{ + return container_of(clk, struct audio_ext_ap_clk, c); +} + +static int audio_ext_clk_prepare(struct clk *clk) +{ + struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(clk); + + pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio); + if (gpio_is_valid(audio_clk->gpio)) + return gpio_direction_output(audio_clk->gpio, 1); + return 0; +} + +static void audio_ext_clk_unprepare(struct clk *clk) +{ + struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(clk); + + pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio); + if (gpio_is_valid(audio_clk->gpio)) + gpio_direction_output(audio_clk->gpio, 0); +} + +static inline struct audio_ext_ap_clk2 *to_audio_ap_clk2(struct clk *clk) +{ + return container_of(clk, struct audio_ext_ap_clk2, c); +} + +static int audio_ext_clk2_prepare(struct clk *clk) +{ + struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(clk); + struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info; + int ret; + + + if (!pnctrl_info->pinctrl || !pnctrl_info->active) + return 0; + + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->active); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return -EIO; + } + + clk2_config.enable = 1; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config); + if (ret < 0) { + pr_err("%s: failed to set clock, ret = %d\n", __func__, ret); + return -EINVAL; + } + + return 0; +} + +static void audio_ext_clk2_unprepare(struct clk *clk) +{ + struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(clk); + struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info; + int ret; + + if (!pnctrl_info->pinctrl || !pnctrl_info->sleep) + return; + + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) + pr_err("%s: sleep state select failed with %d\n", + __func__, ret); + + clk2_config.enable = 0; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config); + if (ret < 0) + pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret); +} + +static const struct clk_ops audio_ext_ap_clk_ops = { + .prepare = audio_ext_clk_prepare, + .unprepare = audio_ext_clk_unprepare, +}; + +static const struct clk_ops audio_ext_ap_clk2_ops = { + .prepare = audio_ext_clk2_prepare, + .unprepare = audio_ext_clk2_unprepare, +}; + +static struct audio_ext_pmi_clk audio_pmi_clk = { + .gpio = -EINVAL, + .c = { + .dbg_name = "audio_ext_pmi_clk", + .ops = &clk_ops_dummy, + CLK_INIT(audio_pmi_clk.c), + }, +}; + +static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = { + .gpio = -EINVAL, + .c = { + .dbg_name = "audio_ext_pmi_lnbb_clk", + .ops = &clk_ops_dummy, + CLK_INIT(audio_pmi_lnbb_clk.c), + }, +}; + +static struct audio_ext_ap_clk audio_ap_clk = { + .gpio = -EINVAL, + .c = { + .dbg_name = "audio_ext_ap_clk", + .ops = &audio_ext_ap_clk_ops, + CLK_INIT(audio_ap_clk.c), + }, +}; + +static struct audio_ext_ap_clk2 audio_ap_clk2 = { + .c = { + .dbg_name = "audio_ext_ap_clk2", + .ops = &audio_ext_ap_clk2_ops, + CLK_INIT(audio_ap_clk2.c), + }, +}; + +static struct clk_lookup audio_ref_clock[] = { + CLK_LIST(audio_ap_clk), + CLK_LIST(audio_pmi_clk), + CLK_LIST(audio_pmi_lnbb_clk), + CLK_LIST(audio_ap_clk2), +}; + +static int audio_get_pinctrl(struct platform_device *pdev) +{ + struct pinctrl_info *pnctrl_info; + struct pinctrl *pinctrl; + int ret; + + pnctrl_info = &audio_ap_clk2.pnctrl_info; + + if (pnctrl_info->pinctrl) { + dev_dbg(&pdev->dev, "%s: already requested before\n", + __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + dev_dbg(&pdev->dev, "%s: Unable to get pinctrl handle\n", + __func__); + return -EINVAL; + } + pnctrl_info->pinctrl = pinctrl; + /* get all state handles from Device Tree */ + pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep"); + if (IS_ERR(pnctrl_info->sleep)) { + dev_err(&pdev->dev, "%s: could not get sleep pinstate\n", + __func__); + goto err; + } + pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active"); + if (IS_ERR(pnctrl_info->active)) { + dev_err(&pdev->dev, "%s: could not get active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) { + dev_err(&pdev->dev, "%s: Disable TLMM pins failed with %d\n", + __func__, ret); + goto err; + } + return 0; + +err: + devm_pinctrl_put(pnctrl_info->pinctrl); + return -EINVAL; +} + +static int audio_ref_clk_probe(struct platform_device *pdev) +{ + int clk_gpio; + int ret; + struct clk *audio_clk; + + clk_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,audio-ref-clk-gpio", 0); + if (clk_gpio > 0) { + ret = gpio_request(clk_gpio, "EXT_CLK"); + if (ret) { + dev_err(&pdev->dev, + "Request ext clk gpio failed %d, err:%d\n", + clk_gpio, ret); + goto err; + } + if (of_property_read_bool(pdev->dev.of_node, + "qcom,node_has_rpm_clock")) { + audio_clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(audio_clk)) { + dev_err(&pdev->dev, "Failed to get RPM div clk\n"); + ret = PTR_ERR(audio_clk); + goto err_gpio; + } + audio_pmi_clk.c.parent = audio_clk; + audio_pmi_clk.gpio = clk_gpio; + } else + audio_ap_clk.gpio = clk_gpio; + + } else { + if (of_property_read_bool(pdev->dev.of_node, + "qcom,node_has_rpm_clock")) { + audio_clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(audio_clk)) { + dev_err(&pdev->dev, "Failed to get lnbbclk2\n"); + ret = PTR_ERR(audio_clk); + goto err; + } + audio_pmi_lnbb_clk.c.parent = audio_clk; + audio_pmi_lnbb_clk.gpio = -EINVAL; + } + } + + ret = audio_get_pinctrl(pdev); + if (ret) + dev_dbg(&pdev->dev, "%s: Parsing pinctrl failed\n", + __func__); + + ret = of_msm_clock_register(pdev->dev.of_node, audio_ref_clock, + ARRAY_SIZE(audio_ref_clock)); + if (ret) { + dev_err(&pdev->dev, "%s: audio ref clock register failed\n", + __func__); + goto err_gpio; + } + + return 0; + +err_gpio: + gpio_free(clk_gpio); + +err: + return ret; +} + +static int audio_ref_clk_remove(struct platform_device *pdev) +{ + struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info; + + if (audio_pmi_clk.gpio > 0) + gpio_free(audio_pmi_clk.gpio); + else if (audio_ap_clk.gpio > 0) + gpio_free(audio_ap_clk.gpio); + + if (pnctrl_info->pinctrl) { + devm_pinctrl_put(pnctrl_info->pinctrl); + pnctrl_info->pinctrl = NULL; + } + + return 0; +} + +static const struct of_device_id audio_ref_clk_match[] = { + {.compatible = "qcom,audio-ref-clk"}, + {} +}; +MODULE_DEVICE_TABLE(of, audio_ref_clk_match); + +static struct platform_driver audio_ref_clk_driver = { + .driver = { + .name = "audio-ref-clk", + .owner = THIS_MODULE, + .of_match_table = audio_ref_clk_match, + }, + .probe = audio_ref_clk_probe, + .remove = audio_ref_clk_remove, +}; + +static int __init audio_ref_clk_platform_init(void) +{ + return platform_driver_register(&audio_ref_clk_driver); +} +module_init(audio_ref_clk_platform_init); + +static void __exit audio_ref_clk_platform_exit(void) +{ + platform_driver_unregister(&audio_ref_clk_driver); +} +module_exit(audio_ref_clk_platform_exit); + +MODULE_DESCRIPTION("Audio Ref Clock module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/msm8x16/Kconfig b/sound/soc/codecs/msm8x16/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..d225b7a56925ead60727d6c909a41be31652ce67 --- /dev/null +++ b/sound/soc/codecs/msm8x16/Kconfig @@ -0,0 +1,3 @@ + +config SND_SOC_MSM8X16_WCD + tristate "MSM Internal PMIC based codec" diff --git a/sound/soc/codecs/msm8x16/Makefile b/sound/soc/codecs/msm8x16/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..1e4522f2c52fb0ef6eddd319bde1798cc03bff11 --- /dev/null +++ b/sound/soc/codecs/msm8x16/Makefile @@ -0,0 +1,2 @@ +snd-soc-msm8952-wcd-objs := msm8x16-wcd.o msm8x16-wcd-tables.o msm89xx-regmap.o +obj-$(CONFIG_SND_SOC_MSM8X16_WCD) += snd-soc-msm8952-wcd.o msm8916-wcd-irq.o diff --git a/sound/soc/codecs/msm8x16/msm8916-wcd-irq.c b/sound/soc/codecs/msm8x16/msm8916-wcd-irq.c new file mode 100644 index 0000000000000000000000000000000000000000..a722842b106bd4c29b441f9809c387118edad712 --- /dev/null +++ b/sound/soc/codecs/msm8x16/msm8916-wcd-irq.c @@ -0,0 +1,441 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm8x16-wcd.h" +#include "msm8916-wcd-irq.h" +#include "msm8x16_wcd_registers.h" + +#define MAX_NUM_IRQS 14 +#define NUM_IRQ_REGS 2 +#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 700 + +#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE)) +#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE) + +static irqreturn_t wcd9xxx_spmi_irq_handler(int linux_irq, void *data); + +char *irq_names[MAX_NUM_IRQS] = { + "spk_cnp_int", + "spk_clip_int", + "spk_ocp_int", + "ins_rem_det1", + "but_rel_det", + "but_press_det", + "ins_rem_det", + "mbhc_int", + "ear_ocp_int", + "hphr_ocp_int", + "hphl_ocp_det", + "ear_cnp_int", + "hphr_cnp_int", + "hphl_cnp_int" +}; + +int order[MAX_NUM_IRQS] = { + MSM89XX_IRQ_SPKR_CNP, + MSM89XX_IRQ_SPKR_CLIP, + MSM89XX_IRQ_SPKR_OCP, + MSM89XX_IRQ_MBHC_INSREM_DET1, + MSM89XX_IRQ_MBHC_RELEASE, + MSM89XX_IRQ_MBHC_PRESS, + MSM89XX_IRQ_MBHC_INSREM_DET, + MSM89XX_IRQ_MBHC_HS_DET, + MSM89XX_IRQ_EAR_OCP, + MSM89XX_IRQ_HPHR_OCP, + MSM89XX_IRQ_HPHL_OCP, + MSM89XX_IRQ_EAR_CNP, + MSM89XX_IRQ_HPHR_CNP, + MSM89XX_IRQ_HPHL_CNP, +}; + +enum wcd9xxx_spmi_pm_state { + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE, + WCD9XXX_PM_ASLEEP, +}; + +struct wcd9xxx_spmi_map { + uint8_t handled[NUM_IRQ_REGS]; + uint8_t mask[NUM_IRQ_REGS]; + int linuxirq[MAX_NUM_IRQS]; + irq_handler_t handler[MAX_NUM_IRQS]; + struct spmi_device *spmi[NUM_IRQ_REGS]; + struct snd_soc_codec *codec; + + enum wcd9xxx_spmi_pm_state pm_state; + struct mutex pm_lock; + /* pm_wq notifies change of pm_state */ + wait_queue_head_t pm_wq; + struct pm_qos_request pm_qos_req; + int wlock_holders; +}; + +struct wcd9xxx_spmi_map map; + +void wcd9xxx_spmi_enable_irq(int irq) +{ + pr_debug("%s: irqno =%d\n", __func__, irq); + if ((irq >= 0) && (irq <= 7)) { + snd_soc_update_bits(map.codec, + MSM89XX_PMIC_DIGITAL_INT_EN_CLR, + (0x01 << irq), 0x00); + snd_soc_update_bits(map.codec, + MSM89XX_PMIC_DIGITAL_INT_EN_SET, + (0x01 << irq), (0x01 << irq)); + } + if ((irq > 7) && (irq <= 15)) { + snd_soc_update_bits(map.codec, + MSM89XX_PMIC_ANALOG_INT_EN_CLR, + (0x01 << (irq - 8)), 0x00); + snd_soc_update_bits(map.codec, + MSM89XX_PMIC_ANALOG_INT_EN_SET, + (0x01 << (irq - 8)), (0x01 << (irq - 8))); + } + + if (!(map.mask[BIT_BYTE(irq)] & (BYTE_BIT_MASK(irq)))) + return; + + map.mask[BIT_BYTE(irq)] &= + ~(BYTE_BIT_MASK(irq)); + + enable_irq(map.linuxirq[irq]); +} + +void wcd9xxx_spmi_disable_irq(int irq) +{ + pr_debug("%s: irqno =%d\n", __func__, irq); + if ((irq >= 0) && (irq <= 7)) { + snd_soc_update_bits(map.codec, + MSM89XX_PMIC_DIGITAL_INT_EN_SET, + (0x01 << (irq)), 0x00); + snd_soc_update_bits(map.codec, + MSM89XX_PMIC_DIGITAL_INT_EN_CLR, + (0x01 << irq), (0x01 << irq)); + } + + if ((irq > 7) && (irq <= 15)) { + snd_soc_update_bits(map.codec, + MSM89XX_PMIC_ANALOG_INT_EN_SET, + (0x01 << (irq - 8)), 0x00); + snd_soc_update_bits(map.codec, + MSM89XX_PMIC_ANALOG_INT_EN_CLR, + (0x01 << (irq - 8)), (0x01 << (irq - 8))); + } + + if (map.mask[BIT_BYTE(irq)] & (BYTE_BIT_MASK(irq))) + return; + + map.mask[BIT_BYTE(irq)] |= + (BYTE_BIT_MASK(irq)); + + disable_irq_nosync(map.linuxirq[irq]); +} + +int wcd9xxx_spmi_request_irq(int irq, irq_handler_t handler, + const char *name, void *priv) +{ + int rc; + unsigned long irq_flags; + + if (strcmp(name, "mbhc sw intr")) + irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT; + else + irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT | IRQF_NO_SUSPEND; + pr_debug("%s: name:%s irq_flags = %lx\n", __func__, name, irq_flags); + + rc = devm_request_threaded_irq(&map.spmi[BIT_BYTE(irq)]->dev, + map.linuxirq[irq], NULL, + wcd9xxx_spmi_irq_handler, + irq_flags, + name, priv); + if (rc < 0) { + dev_err(&map.spmi[BIT_BYTE(irq)]->dev, + "Can't request %d IRQ\n", irq); + return rc; + } + + dev_dbg(&map.spmi[BIT_BYTE(irq)]->dev, + "irq %d linuxIRQ: %d\n", irq, map.linuxirq[irq]); + map.mask[BIT_BYTE(irq)] &= ~BYTE_BIT_MASK(irq); + map.handler[irq] = handler; + enable_irq_wake(map.linuxirq[irq]); + return 0; +} + +int wcd9xxx_spmi_free_irq(int irq, void *priv) +{ + devm_free_irq(&map.spmi[BIT_BYTE(irq)]->dev, map.linuxirq[irq], + priv); + map.mask[BIT_BYTE(irq)] |= BYTE_BIT_MASK(irq); + return 0; +} + +static int get_irq_bit(int linux_irq) +{ + int i = 0; + + for (; i < MAX_NUM_IRQS; i++) + if (map.linuxirq[i] == linux_irq) + return i; + + return i; +} + +static int get_order_irq(int i) +{ + return order[i]; +} + +static irqreturn_t wcd9xxx_spmi_irq_handler(int linux_irq, void *data) +{ + int irq, i, j; + unsigned long status[NUM_IRQ_REGS] = {0}; + + if (unlikely(wcd9xxx_spmi_lock_sleep() == false)) { + pr_err("Failed to hold suspend\n"); + return IRQ_NONE; + } + + irq = get_irq_bit(linux_irq); + if (irq == MAX_NUM_IRQS) + return IRQ_HANDLED; + + status[BIT_BYTE(irq)] |= BYTE_BIT_MASK(irq); + for (i = 0; i < NUM_IRQ_REGS; i++) { + status[i] |= snd_soc_read(map.codec, + BIT_BYTE(irq) * 0x100 + + MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS); + status[i] &= ~map.mask[i]; + } + for (i = 0; i < MAX_NUM_IRQS; i++) { + j = get_order_irq(i); + if ((status[BIT_BYTE(j)] & BYTE_BIT_MASK(j)) && + ((map.handled[BIT_BYTE(j)] & + BYTE_BIT_MASK(j)) == 0)) { + map.handler[j](irq, data); + map.handled[BIT_BYTE(j)] |= + BYTE_BIT_MASK(j); + } + } + map.handled[BIT_BYTE(irq)] &= ~BYTE_BIT_MASK(irq); + wcd9xxx_spmi_unlock_sleep(); + + return IRQ_HANDLED; +} + +enum wcd9xxx_spmi_pm_state wcd9xxx_spmi_pm_cmpxchg( + enum wcd9xxx_spmi_pm_state o, + enum wcd9xxx_spmi_pm_state n) +{ + enum wcd9xxx_spmi_pm_state old; + + mutex_lock(&map.pm_lock); + old = map.pm_state; + if (old == o) + map.pm_state = n; + pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state); + mutex_unlock(&map.pm_lock); + return old; +} +EXPORT_SYMBOL(wcd9xxx_spmi_pm_cmpxchg); + +int wcd9xxx_spmi_suspend(pm_message_t pmesg) +{ + int ret = 0; + + pr_debug("%s: enter\n", __func__); + /* + * pm_qos_update_request() can be called after this suspend chain call + * started. thus suspend can be called while lock is being held + */ + mutex_lock(&map.pm_lock); + if (map.pm_state == WCD9XXX_PM_SLEEPABLE) { + pr_debug("%s: suspending system, state %d, wlock %d\n", + __func__, map.pm_state, + map.wlock_holders); + map.pm_state = WCD9XXX_PM_ASLEEP; + } else if (map.pm_state == WCD9XXX_PM_AWAKE) { + /* + * unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE + * then set to WCD9XXX_PM_ASLEEP + */ + pr_debug("%s: waiting to suspend system, state %d, wlock %d\n", + __func__, map.pm_state, + map.wlock_holders); + mutex_unlock(&map.pm_lock); + if (!(wait_event_timeout(map.pm_wq, + wcd9xxx_spmi_pm_cmpxchg( + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_ASLEEP) == + WCD9XXX_PM_SLEEPABLE, + HZ))) { + pr_debug("%s: suspend failed state %d, wlock %d\n", + __func__, map.pm_state, + map.wlock_holders); + ret = -EBUSY; + } else { + pr_debug("%s: done, state %d, wlock %d\n", __func__, + map.pm_state, + map.wlock_holders); + } + mutex_lock(&map.pm_lock); + } else if (map.pm_state == WCD9XXX_PM_ASLEEP) { + pr_warn("%s: system is already suspended, state %d, wlock %dn", + __func__, map.pm_state, + map.wlock_holders); + } + mutex_unlock(&map.pm_lock); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_spmi_suspend); + +int wcd9xxx_spmi_resume(void) +{ + int ret = 0; + + pr_debug("%s: enter\n", __func__); + mutex_lock(&map.pm_lock); + if (map.pm_state == WCD9XXX_PM_ASLEEP) { + pr_debug("%s: resuming system, state %d, wlock %d\n", __func__, + map.pm_state, + map.wlock_holders); + map.pm_state = WCD9XXX_PM_SLEEPABLE; + } else { + pr_warn("%s: system is already awake, state %d wlock %d\n", + __func__, map.pm_state, + map.wlock_holders); + } + mutex_unlock(&map.pm_lock); + wake_up_all(&map.pm_wq); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_spmi_resume); + +bool wcd9xxx_spmi_lock_sleep(void) +{ + /* + * wcd9xxx_spmi_{lock/unlock}_sleep will be called by + * wcd9xxx_spmi_irq_thread + * and its subroutines only motly. + * but btn0_lpress_fn is not wcd9xxx_spmi_irq_thread's subroutine and + * It can race with wcd9xxx_spmi_irq_thread. + * So need to embrace wlock_holders with mutex. + */ + mutex_lock(&map.pm_lock); + if (map.wlock_holders++ == 0) { + pr_debug("%s: holding wake lock\n", __func__); + pm_qos_update_request(&map.pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + pm_stay_awake(&map.spmi[0]->dev); + } + mutex_unlock(&map.pm_lock); + pr_debug("%s: wake lock counter %d\n", __func__, + map.wlock_holders); + pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state); + + if (!wait_event_timeout(map.pm_wq, + ((wcd9xxx_spmi_pm_cmpxchg( + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE)) == + WCD9XXX_PM_SLEEPABLE || + (wcd9xxx_spmi_pm_cmpxchg( + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE) == + WCD9XXX_PM_AWAKE)), + msecs_to_jiffies( + WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) { + pr_warn("%s: system didn't resume within %dms, s %d, w %d\n", + __func__, + WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, map.pm_state, + map.wlock_holders); + wcd9xxx_spmi_unlock_sleep(); + return false; + } + wake_up_all(&map.pm_wq); + pr_debug("%s: leaving pm_state = %d\n", __func__, map.pm_state); + return true; +} +EXPORT_SYMBOL(wcd9xxx_spmi_lock_sleep); + +void wcd9xxx_spmi_unlock_sleep(void) +{ + mutex_lock(&map.pm_lock); + if (--map.wlock_holders == 0) { + pr_debug("%s: releasing wake lock pm_state %d -> %d\n", + __func__, map.pm_state, WCD9XXX_PM_SLEEPABLE); + /* + * if wcd9xxx_spmi_lock_sleep failed, pm_state would be still + * WCD9XXX_PM_ASLEEP, don't overwrite + */ + if (likely(map.pm_state == WCD9XXX_PM_AWAKE)) + map.pm_state = WCD9XXX_PM_SLEEPABLE; + pm_qos_update_request(&map.pm_qos_req, + PM_QOS_DEFAULT_VALUE); + pm_relax(&map.spmi[0]->dev); + } + mutex_unlock(&map.pm_lock); + pr_debug("%s: wake lock counter %d\n", __func__, + map.wlock_holders); + pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state); + wake_up_all(&map.pm_wq); +} +EXPORT_SYMBOL(wcd9xxx_spmi_unlock_sleep); + +void wcd9xxx_spmi_set_codec(struct snd_soc_codec *codec) +{ + map.codec = codec; +} + +void wcd9xxx_spmi_set_dev(struct spmi_device *spmi, int i) +{ + if (i < NUM_IRQ_REGS) + map.spmi[i] = spmi; +} + +int wcd9xxx_spmi_irq_init(void) +{ + int i = 0; + + for (; i < MAX_NUM_IRQS; i++) + map.mask[BIT_BYTE(i)] |= BYTE_BIT_MASK(i); + mutex_init(&map.pm_lock); + map.wlock_holders = 0; + map.pm_state = WCD9XXX_PM_SLEEPABLE; + init_waitqueue_head(&map.pm_wq); + pm_qos_add_request(&map.pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + return 0; +} + +MODULE_DESCRIPTION("MSM8x16 SPMI IRQ driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/msm8x16/msm8916-wcd-irq.h b/sound/soc/codecs/msm8x16/msm8916-wcd-irq.h new file mode 100644 index 0000000000000000000000000000000000000000..38628653b9d14fee51e625ff053913e7ceec200c --- /dev/null +++ b/sound/soc/codecs/msm8x16/msm8916-wcd-irq.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __WCD9XXX_SPMI_IRQ_H__ +#define __WCD9XXX_SPMI_IRQ_H__ + +#include +#include +#include +#include +#include + +extern void wcd9xxx_spmi_enable_irq(int irq); +extern void wcd9xxx_spmi_disable_irq(int irq); +extern int wcd9xxx_spmi_request_irq(int irq, irq_handler_t handler, + const char *name, void *priv); +extern int wcd9xxx_spmi_free_irq(int irq, void *priv); +extern void wcd9xxx_spmi_set_codec(struct snd_soc_codec *codec); +extern void wcd9xxx_spmi_set_dev(struct spmi_device *spmi, int i); +extern int wcd9xxx_spmi_irq_init(void); +extern int wcd9xxx_spmi_suspend(pm_message_t pmesg); +extern int wcd9xxx_spmi_resume(void); +bool wcd9xxx_spmi_lock_sleep(void); +void wcd9xxx_spmi_unlock_sleep(void); + +#endif diff --git a/sound/soc/codecs/msm8x16/msm89xx-regmap.c b/sound/soc/codecs/msm8x16/msm89xx-regmap.c new file mode 100644 index 0000000000000000000000000000000000000000..007b74c8b8676c5f9bd98fc17955a0d79ddd0a14 --- /dev/null +++ b/sound/soc/codecs/msm8x16/msm89xx-regmap.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "msm8x16-wcd.h" + +/* + * Default register reset values that are common across different versions + * are defined here. If a register reset value is changed based on version + * then remove it from this structure and add it in version specific + * structures. + */ +static struct reg_default + msm89xx_cdc_core_defaults[MSM89XX_CDC_CORE_CACHE_SIZE] = { + {MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x13}, + {MSM89XX_CDC_CORE_CLK_TX_I2S_CTL, 0x13}, + {MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_OTHR_CTL, 0x04}, + {MSM89XX_CDC_CORE_CLK_RX_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_SD_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_WSA_VI_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B5_CTL, 0x68}, + {MSM89XX_CDC_CORE_RX2_B5_CTL, 0x68}, + {MSM89XX_CDC_CORE_RX3_B5_CTL, 0x68}, + {MSM89XX_CDC_CORE_RX1_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_TOP_GAIN_UPDATE, 0x00}, + {MSM89XX_CDC_CORE_TOP_CTL, 0x01}, + {MSM89XX_CDC_CORE_COMP0_B1_CTL, 0x30}, + {MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xB5}, + {MSM89XX_CDC_CORE_COMP0_B3_CTL, 0x28}, + {MSM89XX_CDC_CORE_COMP0_B4_CTL, 0x37}, + {MSM89XX_CDC_CORE_COMP0_B5_CTL, 0x7F}, + {MSM89XX_CDC_CORE_COMP0_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS, 0x03}, + {MSM89XX_CDC_CORE_COMP0_FS_CFG, 0x03}, + {MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL, 0x02}, + {MSM89XX_CDC_CORE_DEBUG_DESER1_CTL, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_DESER2_CTL, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_CTL, 0x40}, + {MSM89XX_CDC_CORE_IIR2_CTL, 0x40}, + {MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX1_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX1_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX2_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX2_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX3_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_TX_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX1_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX2_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX3_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX4_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX1_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX2_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX3_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX4_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX1_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX2_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX3_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX4_DMIC_CTL, 0x00}, +}; + +static struct reg_default + msm89xx_pmic_cdc_defaults[MSM89XX_PMIC_CDC_CACHE_SIZE] = { + {MSM89XX_PMIC_DIGITAL_REVISION1, 0x00}, + {MSM89XX_PMIC_DIGITAL_REVISION2, 0x00}, + {MSM89XX_PMIC_DIGITAL_PERPH_TYPE, 0x23}, + {MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE, 0x01}, + {MSM89XX_PMIC_DIGITAL_INT_RT_STS, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_SET_TYPE, 0xFF}, + {MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH, 0xFF}, + {MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_EN_SET, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_EN_CLR, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_PENDING_STS, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_MID_SEL, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_PRIORITY, 0x00}, + {MSM89XX_PMIC_DIGITAL_GPIO_MODE, 0x00}, + {MSM89XX_PMIC_DIGITAL_PIN_CTL_OE, 0x01}, + {MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA, 0x00}, + {MSM89XX_PMIC_DIGITAL_PIN_STATUS, 0x00}, + {MSM89XX_PMIC_DIGITAL_HDRIVE_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, 0x02}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, 0x02}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1, 0x7C}, + {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2, 0x7C}, + {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3, 0x7C}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0, 0x00}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1, 0x00}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2, 0x00}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3, 0x00}, + {MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN, 0x00}, + {MSM89XX_PMIC_DIGITAL_SPARE_0, 0x00}, + {MSM89XX_PMIC_DIGITAL_SPARE_1, 0x00}, + {MSM89XX_PMIC_DIGITAL_SPARE_2, 0x00}, + {MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0x00}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1, 0x00}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2, 0x02}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x05}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_TEST1, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_TEST_VAL, 0x00}, + {MSM89XX_PMIC_DIGITAL_TRIM_NUM, 0x00}, + {MSM89XX_PMIC_DIGITAL_TRIM_CTRL, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION1, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION2, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION3, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION4, 0x00}, + {MSM89XX_PMIC_ANALOG_PERPH_TYPE, 0x23}, + {MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE, 0x09}, + {MSM89XX_PMIC_ANALOG_INT_RT_STS, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_SET_TYPE, 0x3F}, + {MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH, 0x3F}, + {MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_EN_SET, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_EN_CLR, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_LATCHED_STS, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_PENDING_STS, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_MID_SEL, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_PRIORITY, 0x00}, + {MSM89XX_PMIC_ANALOG_MICB_1_EN, 0x00}, + {MSM89XX_PMIC_ANALOG_MICB_1_VAL, 0x20}, + {MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, 0x49}, + {MSM89XX_PMIC_ANALOG_MICB_2_EN, 0x20}, + {MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, 0x00}, + {MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x35}, + {MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x08}, + {MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0x98}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, 0x20}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, 0x40}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL, 0x61}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL, 0x80}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x00}, + {MSM89XX_PMIC_ANALOG_TX_1_EN, 0x03}, + {MSM89XX_PMIC_ANALOG_TX_2_EN, 0x03}, + {MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1, 0xBF}, + {MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2, 0x8C}, + {MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x6B}, + {MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV, 0x51}, + {MSM89XX_PMIC_ANALOG_TX_3_EN, 0x02}, + {MSM89XX_PMIC_ANALOG_NCP_EN, 0x26}, + {MSM89XX_PMIC_ANALOG_NCP_CLK, 0x23}, + {MSM89XX_PMIC_ANALOG_NCP_DEGLITCH, 0x5B}, + {MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x08}, + {MSM89XX_PMIC_ANALOG_NCP_BIAS, 0x29}, + {MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0x24}, + {MSM89XX_PMIC_ANALOG_NCP_TEST, 0x00}, + {MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR, 0xD5}, + {MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER, 0xE8}, + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0xCF}, + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0x6E}, + {MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x18}, + {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0x5A}, + {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP, 0x69}, + {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP, 0x29}, + {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x80}, + {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL, 0xDA}, + {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0x16}, + {MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x20}, + {MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x20}, + {MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x12}, + {MSM89XX_PMIC_ANALOG_RX_ATEST, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_HPH_STATUS, 0x0C}, + {MSM89XX_PMIC_ANALOG_RX_EAR_STATUS, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x83}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET, 0x91}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x29}, + {MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x4D}, + {MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1}, + {MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x1E}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC, 0xCB}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x00}, + {MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x02}, + {MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE, 0x14}, + {MSM89XX_PMIC_ANALOG_BYPASS_MODE, 0x00}, + {MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, 0x1F}, + {MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, 0x8C}, + {MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE, 0xC0}, + {MSM89XX_PMIC_ANALOG_BOOST_TEST1_1, 0x00}, + {MSM89XX_PMIC_ANALOG_BOOST_TEST_2, 0x00}, + {MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS, 0x00}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS, 0x00}, + {MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR, 0x00}, + {MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL, 0x00}, + {MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0x00}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1, 0x00}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2, 0x01}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x05}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_TEST1, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_TEST_VAL, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_NUM, 0x04}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL1, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL2, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL3, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL4, 0x00}, +}; + +static bool msm89xx_cdc_core_readable_reg(struct device *dev, unsigned int reg) +{ + return msm89xx_cdc_core_reg_readable[reg]; +} + +static bool msm89xx_pmic_cdc_readable_reg(struct device *dev, unsigned int reg) +{ + return msm89xx_pmic_cdc_reg_readable[reg]; +} + +static bool msm89xx_cdc_core_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MSM89XX_CDC_CORE_RX1_B1_CTL: + case MSM89XX_CDC_CORE_RX2_B1_CTL: + case MSM89XX_CDC_CORE_RX3_B1_CTL: + case MSM89XX_CDC_CORE_RX1_B6_CTL: + case MSM89XX_CDC_CORE_RX2_B6_CTL: + case MSM89XX_CDC_CORE_RX3_B6_CTL: + case MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL: + case MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL: + case MSM89XX_CDC_CORE_CLK_MCLK_CTL: + case MSM89XX_CDC_CORE_CLK_PDM_CTL: + case MSM89XX_PMIC_ANALOG_BYPASS_MODE: + case MSM89XX_PMIC_ANALOG_BOOST_EN_CTL: + case MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL: + case MSM89XX_PMIC_ANALOG_CURRENT_LIMIT: + case MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL: + case MSM89XX_PMIC_ANALOG_NCP_FBCTRL: + case MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1: + return true; + default: + return false; + } +} + +static bool msm89xx_pmic_cdc_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MSM89XX_PMIC_DIGITAL_REVISION1: + case MSM89XX_PMIC_DIGITAL_REVISION2: + case MSM89XX_PMIC_DIGITAL_PERPH_TYPE: + case MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE: + case MSM89XX_PMIC_DIGITAL_INT_RT_STS: + case MSM89XX_PMIC_DIGITAL_INT_SET_TYPE: + case MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH: + case MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW: + case MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS: + case MSM89XX_PMIC_DIGITAL_INT_PENDING_STS: + case MSM89XX_PMIC_DIGITAL_PIN_STATUS: + case MSM89XX_PMIC_DIGITAL_SEC_ACCESS: + case MSM89XX_PMIC_ANALOG_SEC_ACCESS: + case MSM89XX_PMIC_ANALOG_REVISION1: + case MSM89XX_PMIC_ANALOG_REVISION2: + case MSM89XX_PMIC_ANALOG_REVISION3: + case MSM89XX_PMIC_ANALOG_REVISION4: + case MSM89XX_PMIC_ANALOG_PERPH_TYPE: + case MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE: + case MSM89XX_PMIC_ANALOG_INT_RT_STS: + case MSM89XX_PMIC_ANALOG_INT_SET_TYPE: + case MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH: + case MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW: + case MSM89XX_PMIC_ANALOG_INT_LATCHED_STS: + case MSM89XX_PMIC_ANALOG_INT_PENDING_STS: + case MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT: + case MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT: + case MSM89XX_PMIC_ANALOG_RX_HPH_STATUS: + case MSM89XX_PMIC_ANALOG_RX_EAR_STATUS: + case MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS: + case MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS: + return true; + default: + return false; + } +} + +struct regmap_config msm89xx_pmic_cdc_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MSM89XX_PMIC_CDC_CACHE_SIZE, + .fast_io = true, + .reg_defaults = msm89xx_pmic_cdc_defaults, + .num_reg_defaults = ARRAY_SIZE(msm89xx_pmic_cdc_defaults), + .readable_reg = msm89xx_pmic_cdc_readable_reg, + .volatile_reg = msm89xx_pmic_cdc_volatile_reg, + .cache_type = REGCACHE_RBTREE, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .can_multi_write = true, + .lock = enable_digital_callback, + .unlock = disable_digital_callback, + +}; + +struct regmap_config msm89xx_cdc_core_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = MSM89XX_CDC_CORE_CACHE_SIZE, + .reg_defaults = msm89xx_cdc_core_defaults, + .num_reg_defaults = ARRAY_SIZE(msm89xx_cdc_core_defaults), + .readable_reg = msm89xx_cdc_core_readable_reg, + .volatile_reg = msm89xx_cdc_core_volatile_reg, + .cache_type = REGCACHE_RBTREE, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .can_multi_write = true, +}; diff --git a/sound/soc/codecs/msm8x16/msm8x16-wcd-tables.c b/sound/soc/codecs/msm8x16/msm8x16-wcd-tables.c new file mode 100644 index 0000000000000000000000000000000000000000..b969639b10ebb667568212661f052c295e0ae246 --- /dev/null +++ b/sound/soc/codecs/msm8x16/msm8x16-wcd-tables.c @@ -0,0 +1,263 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm8x16-wcd.h" + +const u8 msm89xx_pmic_cdc_reg_readable[MSM89XX_PMIC_CDC_CACHE_SIZE] = { + [MSM89XX_PMIC_DIGITAL_REVISION1] = 1, + [MSM89XX_PMIC_DIGITAL_REVISION2] = 1, + [MSM89XX_PMIC_DIGITAL_PERPH_TYPE] = 1, + [MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE] = 1, + [MSM89XX_PMIC_DIGITAL_INT_RT_STS] = 1, + [MSM89XX_PMIC_DIGITAL_INT_SET_TYPE] = 1, + [MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH] = 1, + [MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW] = 1, + [MSM89XX_PMIC_DIGITAL_INT_EN_SET] = 1, + [MSM89XX_PMIC_DIGITAL_INT_EN_CLR] = 1, + [MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS] = 1, + [MSM89XX_PMIC_DIGITAL_INT_PENDING_STS] = 1, + [MSM89XX_PMIC_DIGITAL_INT_MID_SEL] = 1, + [MSM89XX_PMIC_DIGITAL_INT_PRIORITY] = 1, + [MSM89XX_PMIC_DIGITAL_GPIO_MODE] = 1, + [MSM89XX_PMIC_DIGITAL_PIN_CTL_OE] = 1, + [MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA] = 1, + [MSM89XX_PMIC_DIGITAL_PIN_STATUS] = 1, + [MSM89XX_PMIC_DIGITAL_HDRIVE_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_RST_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2] = 1, + [MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3] = 1, + [MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0] = 1, + [MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1] = 1, + [MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2] = 1, + [MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3] = 1, + [MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_SPARE_0] = 1, + [MSM89XX_PMIC_DIGITAL_SPARE_1] = 1, + [MSM89XX_PMIC_DIGITAL_SPARE_2] = 1, + [MSM89XX_PMIC_ANALOG_REVISION1] = 1, + [MSM89XX_PMIC_ANALOG_REVISION2] = 1, + [MSM89XX_PMIC_ANALOG_REVISION3] = 1, + [MSM89XX_PMIC_ANALOG_REVISION4] = 1, + [MSM89XX_PMIC_ANALOG_PERPH_TYPE] = 1, + [MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE] = 1, + [MSM89XX_PMIC_ANALOG_INT_RT_STS] = 1, + [MSM89XX_PMIC_ANALOG_INT_SET_TYPE] = 1, + [MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH] = 1, + [MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW] = 1, + [MSM89XX_PMIC_ANALOG_INT_EN_SET] = 1, + [MSM89XX_PMIC_ANALOG_INT_EN_CLR] = 1, + [MSM89XX_PMIC_ANALOG_INT_LATCHED_STS] = 1, + [MSM89XX_PMIC_ANALOG_INT_PENDING_STS] = 1, + [MSM89XX_PMIC_ANALOG_INT_MID_SEL] = 1, + [MSM89XX_PMIC_ANALOG_INT_PRIORITY] = 1, + [MSM89XX_PMIC_ANALOG_MICB_1_EN] = 1, + [MSM89XX_PMIC_ANALOG_MICB_1_VAL] = 1, + [MSM89XX_PMIC_ANALOG_MICB_1_CTL] = 1, + [MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS] = 1, + [MSM89XX_PMIC_ANALOG_MICB_2_EN] = 1, + [MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2] = 1, + [MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1] = 1, + [MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2] = 1, + [MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL] = 1, + [MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER] = 1, + [MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL] = 1, + [MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL] = 1, + [MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL] = 1, + [MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL] = 1, + [MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL] = 1, + [MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT] = 1, + [MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT] = 1, + [MSM89XX_PMIC_ANALOG_TX_1_EN] = 1, + [MSM89XX_PMIC_ANALOG_TX_2_EN] = 1, + [MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1] = 1, + [MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2] = 1, + [MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL] = 1, + [MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS] = 1, + [MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV] = 1, + [MSM89XX_PMIC_ANALOG_TX_3_EN] = 1, + [MSM89XX_PMIC_ANALOG_NCP_EN] = 1, + [MSM89XX_PMIC_ANALOG_NCP_CLK] = 1, + [MSM89XX_PMIC_ANALOG_NCP_DEGLITCH] = 1, + [MSM89XX_PMIC_ANALOG_NCP_FBCTRL] = 1, + [MSM89XX_PMIC_ANALOG_NCP_BIAS] = 1, + [MSM89XX_PMIC_ANALOG_NCP_VCTRL] = 1, + [MSM89XX_PMIC_ANALOG_NCP_TEST] = 1, + [MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER] = 1, + [MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL] = 1, + [MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT] = 1, + [MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC] = 1, + [MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA] = 1, + [MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP] = 1, + [MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP] = 1, + [MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN] = 1, + [MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL] = 1, + [MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME] = 1, + [MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST] = 1, + [MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL] = 1, + [MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST] = 1, + [MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL] = 1, + [MSM89XX_PMIC_ANALOG_RX_EAR_CTL] = 1, + [MSM89XX_PMIC_ANALOG_RX_ATEST] = 1, + [MSM89XX_PMIC_ANALOG_RX_HPH_STATUS] = 1, + [MSM89XX_PMIC_ANALOG_RX_EAR_STATUS] = 1, + [MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL] = 1, + [MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET] = 1, + [MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL] = 1, + [MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET] = 1, + [MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL] = 1, + [MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL] = 1, + [MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC] = 1, + [MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG] = 1, + [MSM89XX_PMIC_ANALOG_CURRENT_LIMIT] = 1, + [MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE] = 1, + [MSM89XX_PMIC_ANALOG_BYPASS_MODE] = 1, + [MSM89XX_PMIC_ANALOG_BOOST_EN_CTL] = 1, + [MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO] = 1, + [MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE] = 1, + [MSM89XX_PMIC_ANALOG_BOOST_TEST1_1] = 1, + [MSM89XX_PMIC_ANALOG_BOOST_TEST_2] = 1, + [MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS] = 1, + [MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS] = 1, + [MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR] = 1, + [MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL] = 1, + [MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL] = 1, + [MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR] = 1, + [MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR] = 1, + [MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR] = 1, + [MSM89XX_PMIC_DIGITAL_SEC_ACCESS] = 1, + [MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3] = 1, + [MSM89XX_PMIC_ANALOG_SEC_ACCESS] = 1, +}; + +const u8 msm89xx_cdc_core_reg_readable[MSM89XX_CDC_CORE_CACHE_SIZE] = { + [MSM89XX_CDC_CORE_CLK_RX_RESET_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_MCLK_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_PDM_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_SD_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_WSA_VI_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_TOP_GAIN_UPDATE] = 1, + [MSM89XX_CDC_CORE_TOP_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER1_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER2_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX1_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_DMIC_CTL] = 1, +}; diff --git a/sound/soc/codecs/msm8x16/msm8x16-wcd.c b/sound/soc/codecs/msm8x16/msm8x16-wcd.c new file mode 100644 index 0000000000000000000000000000000000000000..bb8472568df4eb90d907e533923ae384289b7e4e --- /dev/null +++ b/sound/soc/codecs/msm8x16/msm8x16-wcd.c @@ -0,0 +1,6022 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../msm/msmfalcon-common.h" +#include "../wcd-mbhc-v2.h" +#include "msm8916-wcd-irq.h" +#include "msm8x16-wcd.h" + +#define DRV_NAME "msm-codec" +#define MSM89XX_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) +#define MSM89XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +#define NUM_INTERPOLATORS 3 +#define BITS_PER_REG 8 +#define MSM89XX_TX_PORT_NUMBER 4 + +#define MSM89XX_I2S_MASTER_MODE_MASK 0x08 +#define MSM89XX_DIGITAL_CODEC_BASE_ADDR 0x771C000 +#define PMIC_SLAVE_ID_0 0 +#define PMIC_SLAVE_ID_1 1 + +#define PMIC_MBG_OK 0x2C08 +#define PMIC_LDO7_EN_CTL 0x4646 +#define MASK_MSB_BIT 0x80 + +#define CODEC_DT_MAX_PROP_SIZE 40 +#define MSM89XX_DIGITAL_CODEC_REG_SIZE 0x400 +#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64 + +#define MCLK_RATE_9P6MHZ 9600000 +#define MCLK_RATE_12P288MHZ 12288000 + +#define BUS_DOWN 1 + +/* + *50 Milliseconds sufficient for DSP bring up in the modem + * after Sub System Restart + */ +#define ADSP_STATE_READY_TIMEOUT_MS 50 + +#define HPHL_PA_DISABLE (0x01 << 1) +#define HPHR_PA_DISABLE (0x01 << 2) +#define EAR_PA_DISABLE (0x01 << 3) +#define SPKR_PA_DISABLE (0x01 << 4) + +enum { + BOOST_SWITCH = 0, + BOOST_ALWAYS, + BYPASS_ALWAYS, + BOOST_ON_FOREVER, +}; + +#define EAR_PMD 0 +#define EAR_PMU 1 +#define SPK_PMD 2 +#define SPK_PMU 3 + +#define MICBIAS_DEFAULT_VAL 1800000 +#define MICBIAS_MIN_VAL 1600000 +#define MICBIAS_STEP_SIZE 50000 + +#define DEFAULT_BOOST_VOLTAGE 5000 +#define MIN_BOOST_VOLTAGE 4000 +#define MAX_BOOST_VOLTAGE 5550 +#define BOOST_VOLTAGE_STEP 50 + +#define MSM89XX_MBHC_BTN_COARSE_ADJ 100 /* in mV */ +#define MSM89XX_MBHC_BTN_FINE_ADJ 12 /* in mV */ + +#define VOLTAGE_CONVERTER(value, min_value, step_size)\ + ((value - min_value)/step_size) + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_VIFEED, + NUM_CODEC_DAIS, +}; + +enum { + RX_MIX1_INP_SEL_ZERO = 0, + RX_MIX1_INP_SEL_IIR1, + RX_MIX1_INP_SEL_IIR2, + RX_MIX1_INP_SEL_RX1, + RX_MIX1_INP_SEL_RX2, + RX_MIX1_INP_SEL_RX3, +}; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); +static struct snd_soc_dai_driver msm8x16_wcd_i2s_dai[]; +/* By default enable the internal speaker boost */ +static bool spkr_boost_en = true; + +#define MSM89XX_ACQUIRE_LOCK(x) \ + mutex_lock_nested(&x, SINGLE_DEPTH_NESTING) + +#define MSM89XX_RELEASE_LOCK(x) mutex_unlock(&x) + + +/* Codec supports 2 IIR filters */ +enum { + IIR1 = 0, + IIR2, + IIR_MAX, +}; + +/* Codec supports 5 bands */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +struct hpf_work { + struct msm8x16_wcd_priv *msm8x16_wcd; + u32 decimator; + u8 tx_hpf_cut_of_freq; + struct delayed_work dwork; +}; + +static struct hpf_work tx_hpf_work[NUM_DECIMATORS]; + +static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = { + "cdc-vdd-mic-bias", +}; + +static unsigned long rx_digital_gain_reg[] = { + MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, + MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, + MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, +}; + +static unsigned long tx_digital_gain_reg[] = { + MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, + MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, +}; + +enum { + MSM89XX_SPMI_DIGITAL = 0, + MSM89XX_SPMI_ANALOG, + MSM89XX_CODEC_CORE, + MAX_MSM89XX_DEVICE +}; + +static struct wcd_mbhc_register + wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = { + + WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x18, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x06, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN", + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC", + MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0xF0, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC", + MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0x0C, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF", + MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x01, + 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x02, + 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x08, + 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x04, + 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN", + MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL", + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0xFF, + 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL", + MSM89XX_PMIC_ANALOG_MICB_2_EN, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME", + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFC, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN", + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN", + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN", + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x30, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, + 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL", + MSM89XX_PMIC_ANALOG_MICB_2_EN, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", + 0, 0, 0, 0), +}; + +struct msm8x16_wcd_spmi { + struct spmi_device *spmi; + int base; +}; + +/* Multiply gain_adj and offset by 1000 and 100 to avoid float arithmetic */ +static const struct wcd_imped_i_ref imped_i_ref[] = { + {I_h4_UA, 8, 800, 9000, 10000}, + {I_pt5_UA, 10, 100, 990, 4600}, + {I_14_UA, 17, 14, 1050, 700}, + {I_l4_UA, 10, 4, 1165, 110}, + {I_1_UA, 0, 1, 1200, 65}, +}; + +static const struct wcd_mbhc_intr intr_ids = { + .mbhc_sw_intr = MSM89XX_IRQ_MBHC_HS_DET, + .mbhc_btn_press_intr = MSM89XX_IRQ_MBHC_PRESS, + .mbhc_btn_release_intr = MSM89XX_IRQ_MBHC_RELEASE, + .mbhc_hs_ins_intr = MSM89XX_IRQ_MBHC_INSREM_DET1, + .mbhc_hs_rem_intr = MSM89XX_IRQ_MBHC_INSREM_DET, + .hph_left_ocp = MSM89XX_IRQ_HPHL_OCP, + .hph_right_ocp = MSM89XX_IRQ_HPHR_OCP, +}; + +static int msm_digcdc_clock_control(bool flag); +static int msm8x16_wcd_dt_parse_vreg_info(struct device *dev, + struct msm8x16_wcd_regulator *vreg, + const char *vreg_name, bool ondemand); +static struct msm8x16_wcd_pdata *msm8x16_wcd_populate_dt_pdata( + struct device *dev); +static int msm8x16_wcd_enable_ext_mb_source(struct wcd_mbhc *mbhc, + bool turn_on); +static void msm8x16_trim_btn_reg(struct snd_soc_codec *codec); +static void msm8x16_wcd_set_micb_v(struct snd_soc_codec *codec); +static void msm8x16_wcd_set_boost_v(struct snd_soc_codec *codec); +static void msm8x16_wcd_set_auto_zeroing(struct snd_soc_codec *codec, + bool enable); +static void msm8x16_wcd_configure_cap(struct snd_soc_codec *codec, + bool micbias1, bool micbias2); +static bool msm8x16_wcd_use_mb(struct snd_soc_codec *codec); + +struct msm8x16_wcd_spmi msm8x16_wcd_modules[MAX_MSM89XX_DEVICE]; + +static void *adsp_state_notifier; + +static struct snd_soc_codec *registered_codec; +static struct snd_soc_codec *registered_digcodec; + +static int get_codec_version(struct msm8x16_wcd_priv *msm8x16_wcd) +{ + if (msm8x16_wcd->codec_version == DIANGU) + return DIANGU; + else if (msm8x16_wcd->codec_version == CAJON_2_0) + return CAJON_2_0; + else if (msm8x16_wcd->codec_version == CAJON) + return CAJON; + else if (msm8x16_wcd->codec_version == CONGA) + return CONGA; + else if (msm8x16_wcd->pmic_rev == TOMBAK_2_0) + return TOMBAK_2_0; + else if (msm8x16_wcd->pmic_rev == TOMBAK_1_0) + return TOMBAK_1_0; + + pr_err("%s: unsupported codec version\n", __func__); + return UNSUPPORTED; +} + +static int msm_digcdc_clock_control(bool flag) +{ + int ret = -EINVAL; + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(registered_codec->component.card); + + if (flag) { + mutex_lock(&pdata->cdc_int_mclk0_mutex); + if (atomic_read(&pdata->int_mclk0_enabled) == false) { + pdata->digital_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) { + pr_err("failed to enable the INT_MCLK0\n"); + goto err_mclk; + } + pr_err("enabled digital codec core clk\n"); + atomic_set(&pdata->int_mclk0_enabled, true); + schedule_delayed_work(&pdata->disable_int_mclk0_work, + 50); + } +err_mclk: + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + return ret; + } + return 0; +} + +void enable_digital_callback(void *flag) +{ + msm_digcdc_clock_control(true); +} + +void disable_digital_callback(void *flag) +{ + msm_digcdc_clock_control(false); +} + +static int snd_soc_read_wrapper(struct snd_soc_codec *codec, u16 reg) +{ + int ret = -EINVAL; + struct msm8x16_wcd *msm8x16_wcd = codec->control_data; + + pr_err("%s reg = %x\n", __func__, reg); + mutex_lock(&msm8x16_wcd->io_lock); + if (MSM89XX_IS_PMIC_CDC_REG(reg)) + ret = snd_soc_read(codec, reg); + else if (MSM89XX_IS_CDC_CORE_REG(reg)) + ret = snd_soc_read(registered_digcodec, reg); + mutex_unlock(&msm8x16_wcd->io_lock); + + return ret; +} + +static int snd_soc_write_wrapper(struct snd_soc_codec *codec, u16 reg, u8 val) +{ + int ret = -EINVAL; + struct msm8x16_wcd *msm8x16_wcd = codec->control_data; + + pr_err("%s reg = %x\n", __func__, reg); + mutex_lock(&msm8x16_wcd->io_lock); + if (MSM89XX_IS_PMIC_CDC_REG(reg)) + ret = snd_soc_write(codec, reg, val); + else if (MSM89XX_IS_CDC_CORE_REG(reg)) + ret = snd_soc_write(registered_digcodec, reg, val); + mutex_unlock(&msm8x16_wcd->io_lock); + + return ret; +} + +static int snd_soc_update_bits_wrapper(struct snd_soc_codec *codec, + u16 reg, u8 mask, u8 val) +{ + int ret = -EINVAL; + struct msm8x16_wcd *msm8x16_wcd = codec->control_data; + + pr_err("%s reg = %x\n", __func__, reg); + mutex_lock(&msm8x16_wcd->io_lock); + if (MSM89XX_IS_PMIC_CDC_REG(reg)) + ret = snd_soc_update_bits(codec, reg, mask, val); + else if (MSM89XX_IS_CDC_CORE_REG(reg)) + ret = snd_soc_update_bits(registered_digcodec, reg, mask, val); + mutex_unlock(&msm8x16_wcd->io_lock); + + return ret; +} + +static void wcd_mbhc_meas_imped(struct snd_soc_codec *codec, + s16 *impedance_l, s16 *impedance_r) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + if ((msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_BOTH) || + (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHL)) { + /* Enable ZDET_L_MEAS_EN */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x08, 0x08); + /* Wait for 2ms for measurement to complete */ + usleep_range(2000, 2100); + /* Read Left impedance value from Result1 */ + *impedance_l = snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT); + /* Enable ZDET_R_MEAS_EN */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x08, 0x00); + } + if ((msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_BOTH) || + (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHR)) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x04, 0x04); + /* Wait for 2ms for measurement to complete */ + usleep_range(2000, 2100); + /* Read Right impedance value from Result1 */ + *impedance_r = snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x04, 0x00); + } +} + +static void msm8x16_set_ref_current(struct snd_soc_codec *codec, + enum wcd_curr_ref curr_ref) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + pr_err("%s: curr_ref: %d\n", __func__, curr_ref); + + if (get_codec_version(msm8x16_wcd) < CAJON) + pr_err("%s: Setting ref current not required\n", __func__); + + msm8x16_wcd->imped_i_ref = imped_i_ref[curr_ref]; + + switch (curr_ref) { + case I_h4_UA: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x01); + break; + case I_pt5_UA: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x04); + break; + case I_14_UA: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x03); + break; + case I_l4_UA: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x01); + break; + case I_1_UA: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x00); + break; + default: + pr_err("%s: No ref current set\n", __func__); + break; + } +} + +static bool msm8x16_adj_ref_current(struct snd_soc_codec *codec, + s16 *impedance_l, s16 *impedance_r) +{ + int i = 2; + s16 compare_imp = 0; + + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + if (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHR) + compare_imp = *impedance_r; + else + compare_imp = *impedance_l; + + if (get_codec_version(msm8x16_wcd) < CAJON) { + pr_err("%s: Reference current adjustment not required\n", + __func__); + return false; + } + + while (compare_imp < imped_i_ref[i].min_val) { + msm8x16_set_ref_current(codec, + imped_i_ref[++i].curr_ref); + wcd_mbhc_meas_imped(codec, + impedance_l, impedance_r); + compare_imp = (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHR) + ? *impedance_r : *impedance_l; + } + + return true; +} + +void msm8x16_wcd_spk_ext_pa_cb( + int (*codec_spk_ext_pa)(struct snd_soc_codec *codec, + int enable), struct snd_soc_codec *codec) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + pr_err("%s: Enter\n", __func__); + msm8x16_wcd->codec_spk_ext_pa_cb = codec_spk_ext_pa; +} + +void msm8x16_wcd_hph_comp_cb( + int (*codec_hph_comp_gpio)(bool enable), struct snd_soc_codec *codec) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + pr_err("%s: Enter\n", __func__); + msm8x16_wcd->codec_hph_comp_gpio = codec_hph_comp_gpio; +} + +static void msm8x16_wcd_compute_impedance(struct snd_soc_codec *codec, s16 l, + s16 r, uint32_t *zl, uint32_t *zr, bool high) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + uint32_t rl = 0, rr = 0; + struct wcd_imped_i_ref R = msm8x16_wcd->imped_i_ref; + int codec_ver = get_codec_version(msm8x16_wcd); + + switch (codec_ver) { + case TOMBAK_1_0: + case TOMBAK_2_0: + case CONGA: + if (high) { + pr_err("%s: This plug has high range impedance\n", + __func__); + rl = (uint32_t)(((100 * (l * 400 - 200))/96) - 230); + rr = (uint32_t)(((100 * (r * 400 - 200))/96) - 230); + } else { + pr_err("%s: This plug has low range impedance\n", + __func__); + rl = (uint32_t)(((1000 * (l * 2 - 1))/1165) - (13/10)); + rr = (uint32_t)(((1000 * (r * 2 - 1))/1165) - (13/10)); + } + break; + case CAJON: + case CAJON_2_0: + case DIANGU: + if (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHL) { + rr = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * r - 5)) - + (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN); + rl = (uint32_t)(((10000 * (R.multiplier * (10 * l - 5))) + - R.offset * R.gain_adj)/(R.gain_adj * 100)); + } else if (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHR) { + rr = (uint32_t)(((10000 * (R.multiplier * (10 * r - 5))) + - R.offset * R.gain_adj)/(R.gain_adj * 100)); + rl = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * l - 5))- + (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN); + } else if (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_NONE) { + rr = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * r - 5)) - + (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN); + rl = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * l - 5))- + (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN); + } else { + rr = (uint32_t)(((10000 * (R.multiplier * (10 * r - 5))) + - R.offset * R.gain_adj)/(R.gain_adj * 100)); + rl = (uint32_t)(((10000 * (R.multiplier * (10 * l - 5))) + - R.offset * R.gain_adj)/(R.gain_adj * 100)); + } + break; + default: + pr_err("%s: No codec mentioned\n", __func__); + break; + } + *zl = rl; + *zr = rr; +} + +static struct firmware_cal *msm8x16_wcd_get_hwdep_fw_cal( + struct wcd_mbhc *mbhc, + enum wcd_cal_type type) +{ + struct msm8x16_wcd_priv *msm8x16_wcd; + struct firmware_cal *hwdep_cal; + struct snd_soc_codec *codec = mbhc->codec; + + if (!codec) { + pr_err("%s: NULL codec pointer\n", __func__); + return NULL; + } + msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + hwdep_cal = wcdcal_get_fw_cal(msm8x16_wcd->fw_data, type); + if (!hwdep_cal) { + dev_err(codec->dev, "%s: cal not sent by %d\n", + __func__, type); + return NULL; + } + return hwdep_cal; +} + +static void wcd9xxx_spmi_irq_control(struct snd_soc_codec *codec, + int irq, bool enable) +{ + if (enable) + wcd9xxx_spmi_enable_irq(irq); + else + wcd9xxx_spmi_disable_irq(irq); +} + +static void msm8x16_mbhc_clk_setup(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x08, 0x08); + else + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x08, 0x00); +} + +static int msm8x16_mbhc_map_btn_code_to_num(struct snd_soc_codec *codec) +{ + int btn_code; + int btn; + + btn_code = snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT); + + switch (btn_code) { + case 0: + btn = 0; + break; + case 1: + btn = 1; + break; + case 3: + btn = 2; + break; + case 7: + btn = 3; + break; + case 15: + btn = 4; + break; + default: + btn = -EINVAL; + break; + }; + + return btn; +} + +static bool msm8x16_spmi_lock_sleep(struct wcd_mbhc *mbhc, bool lock) +{ + if (lock) + return wcd9xxx_spmi_lock_sleep(); + wcd9xxx_spmi_unlock_sleep(); + return 0; +} + +static bool msm8x16_wcd_micb_en_status(struct wcd_mbhc *mbhc, int micb_num) +{ + if (micb_num == MIC_BIAS_1) + return (snd_soc_read_wrapper(mbhc->codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN) & + 0x80); + if (micb_num == MIC_BIAS_2) + return (snd_soc_read_wrapper(mbhc->codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN) & + 0x80); + return false; +} + +static void msm8x16_wcd_enable_master_bias(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, + 0x30, 0x30); + else + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, + 0x30, 0x00); +} + +static void msm8x16_wcd_mbhc_common_micb_ctrl(struct snd_soc_codec *codec, + int event, bool enable) +{ + u16 reg; + u8 mask; + u8 val; + + switch (event) { + case MBHC_COMMON_MICB_PRECHARGE: + reg = MSM89XX_PMIC_ANALOG_MICB_1_CTL; + mask = 0x60; + val = (enable ? 0x60 : 0x00); + break; + case MBHC_COMMON_MICB_SET_VAL: + reg = MSM89XX_PMIC_ANALOG_MICB_1_VAL; + mask = 0xFF; + val = (enable ? 0xC0 : 0x00); + break; + case MBHC_COMMON_MICB_TAIL_CURR: + reg = MSM89XX_PMIC_ANALOG_MICB_1_EN; + mask = 0x04; + val = (enable ? 0x04 : 0x00); + break; + }; + snd_soc_update_bits_wrapper(codec, reg, mask, val); +} + +static void msm8x16_wcd_mbhc_internal_micbias_ctrl(struct snd_soc_codec *codec, + int micbias_num, bool enable) +{ + if (micbias_num == 1) { + if (enable) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, + 0x10, 0x10); + else + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, + 0x10, 0x00); + } +} + +static bool msm8x16_wcd_mbhc_hph_pa_on_status(struct snd_soc_codec *codec) +{ + return (snd_soc_read_wrapper(codec, MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN) & + 0x30) ? true : false; +} + +static void msm8x16_wcd_mbhc_program_btn_thr(struct snd_soc_codec *codec, + s16 *btn_low, s16 *btn_high, + int num_btn, bool is_micbias) +{ + int i; + u32 course, fine, reg_val; + u16 reg_addr = MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL; + s16 *btn_voltage; + + btn_voltage = ((is_micbias) ? btn_high : btn_low); + + for (i = 0; i < num_btn; i++) { + course = (btn_voltage[i] / MSM89XX_MBHC_BTN_COARSE_ADJ); + fine = ((btn_voltage[i] % MSM89XX_MBHC_BTN_COARSE_ADJ) / + MSM89XX_MBHC_BTN_FINE_ADJ); + + reg_val = (course << 5) | (fine << 2); + snd_soc_update_bits_wrapper(codec, reg_addr, 0xFC, reg_val); + pr_err("%s: course: %d fine: %d reg_addr: %x reg_val: %x\n", + __func__, course, fine, reg_addr, reg_val); + reg_addr++; + } +} + +static void msm8x16_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + s16 impedance_l, impedance_r; + s16 impedance_l_fixed; + s16 reg0, reg1, reg2, reg3, reg4; + bool high = false; + bool min_range_used = false; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + reg0 = snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER); + reg1 = snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL); + reg2 = snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2); + reg3 = snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN); + reg4 = snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL); + + msm8x16_wcd->imped_det_pin = WCD_MBHC_DET_BOTH; + mbhc->hph_type = WCD_MBHC_HPH_NONE; + + /* disable FSM and micbias and enable pullup*/ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x80, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0xA5, 0x25); + /* + * Enable legacy electrical detection current sources + * and disable fast ramp and enable manual switching + * of extra capacitance + */ + pr_err("%s: Setup for impedance det\n", __func__); + + msm8x16_set_ref_current(codec, I_h4_UA); + + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, + 0x06, 0x02); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, + 0x02, 0x02); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, + 0x02, 0x00); + + pr_err("%s: Start performing impedance detection\n", + __func__); + + wcd_mbhc_meas_imped(codec, &impedance_l, &impedance_r); + + if (impedance_l > 2 || impedance_r > 2) { + high = true; + if (!mbhc->mbhc_cfg->mono_stero_detection) { + /* Set ZDET_CHG to 0 to discharge ramp */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x00); + /* wait 40ms for the discharge ramp to complete */ + usleep_range(40000, 40100); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x03, 0x00); + msm8x16_wcd->imped_det_pin = (impedance_l > 2 && + impedance_r > 2) ? + WCD_MBHC_DET_NONE : + ((impedance_l > 2) ? + WCD_MBHC_DET_HPHR : + WCD_MBHC_DET_HPHL); + if (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_NONE) + goto exit; + } else { + if (get_codec_version(msm8x16_wcd) >= CAJON) { + if (impedance_l == 63 && impedance_r == 63) { + pr_err("%s: HPHL and HPHR are floating\n", + __func__); + msm8x16_wcd->imped_det_pin = + WCD_MBHC_DET_NONE; + mbhc->hph_type = WCD_MBHC_HPH_NONE; + } else if (impedance_l == 63 + && impedance_r < 63) { + pr_err("%s: Mono HS with HPHL floating\n", + __func__); + msm8x16_wcd->imped_det_pin = + WCD_MBHC_DET_HPHR; + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } else if (impedance_r == 63 && + impedance_l < 63) { + pr_err("%s: Mono HS with HPHR floating\n", + __func__); + msm8x16_wcd->imped_det_pin = + WCD_MBHC_DET_HPHL; + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } else if (impedance_l > 3 && impedance_r > 3 && + (impedance_l == impedance_r)) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, + 0x06, 0x06); + wcd_mbhc_meas_imped(codec, &impedance_l, + &impedance_r); + if (impedance_r == impedance_l) + pr_err("%s: Mono Headset\n", + __func__); + msm8x16_wcd->imped_det_pin = + WCD_MBHC_DET_NONE; + mbhc->hph_type = + WCD_MBHC_HPH_MONO; + } else { + pr_err("%s: STEREO headset is found\n", + __func__); + msm8x16_wcd->imped_det_pin = + WCD_MBHC_DET_BOTH; + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } + } + } + } + + msm8x16_set_ref_current(codec, I_pt5_UA); + msm8x16_set_ref_current(codec, I_14_UA); + + /* Enable RAMP_L, RAMP_R & ZDET_CHG*/ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x03, 0x03); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x02); + /* wait for 50msec for the HW to apply ramp on HPHL and HPHR */ + usleep_range(50000, 50100); + /* Enable ZDET_DISCHG_CAP_CTL to add extra capacitance */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x01, 0x01); + /* wait for 5msec for the voltage to get stable */ + usleep_range(5000, 5100); + + + wcd_mbhc_meas_imped(codec, &impedance_l, &impedance_r); + + min_range_used = msm8x16_adj_ref_current(codec, + &impedance_l, &impedance_r); + if (!mbhc->mbhc_cfg->mono_stero_detection) { + /* Set ZDET_CHG to 0 to discharge ramp */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x00); + /* wait for 40msec for the capacitor to discharge */ + usleep_range(40000, 40100); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x03, 0x00); + goto exit; + } + + /* we are setting ref current to the minimun range or the measured + * value larger than the minimum value, so min_range_used is true. + * If the headset is mono headset with either HPHL or HPHR floating + * then we have already done the mono stereo detection and do not + * need to continue further. + */ + + if (!min_range_used || + msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHL || + msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHR) + goto exit; + + + /* Disable Set ZDET_CONN_RAMP_L and enable ZDET_CONN_FIXED_L */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x02, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, + 0x02, 0x02); + /* Set ZDET_CHG to 0 */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x00); + /* wait for 40msec for the capacitor to discharge */ + usleep_range(40000, 40100); + + /* Set ZDET_CONN_RAMP_R to 0 */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x01, 0x00); + /* Enable ZDET_L_MEAS_EN */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x08, 0x08); + /* wait for 2msec for the HW to compute left inpedance value */ + usleep_range(2000, 2100); + /* Read Left impedance value from Result1 */ + impedance_l_fixed = snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT); + /* Disable ZDET_L_MEAS_EN */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x08, 0x00); + /* + * Assume impedance_l is L1, impedance_l_fixed is L2. + * If the following condition is met, we can take this + * headset as mono one with impedance of L2. + * Otherwise, take it as stereo with impedance of L1. + * Condition: + * abs[(L2-0.5L1)/(L2+0.5L1)] < abs [(L2-L1)/(L2+L1)] + */ + if ((abs(impedance_l_fixed - impedance_l/2) * + (impedance_l_fixed + impedance_l)) >= + (abs(impedance_l_fixed - impedance_l) * + (impedance_l_fixed + impedance_l/2))) { + pr_err("%s: STEREO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } else { + pr_err("%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + impedance_l = impedance_l_fixed; + } + /* Enable ZDET_CHG */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x02); + /* wait for 10msec for the capacitor to charge */ + usleep_range(10000, 10100); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x02, 0x02); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, + 0x02, 0x00); + /* Set ZDET_CHG to 0 to discharge HPHL */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x00); + /* wait for 40msec for the capacitor to discharge */ + usleep_range(40000, 40100); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x02, 0x00); + +exit: + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, reg4); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, reg3); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, reg1); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, reg0); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, reg2); + msm8x16_wcd_compute_impedance(codec, impedance_l, impedance_r, + zl, zr, high); + + pr_err("%s: RL %d ohm, RR %d ohm\n", __func__, *zl, *zr); + pr_err("%s: Impedance detection completed\n", __func__); +} + +static int msm8x16_register_notifier(struct wcd_mbhc *mbhc, + struct notifier_block *nblock, + bool enable) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct msm8x16_wcd_priv *msm8x16_wcd = + snd_soc_codec_get_drvdata(codec); + + if (enable) + return blocking_notifier_chain_register(&msm8x16_wcd->notifier, + nblock); + return blocking_notifier_chain_unregister( + &msm8x16_wcd->notifier, nblock); +} + +static int msm8x16_wcd_request_irq(struct snd_soc_codec *codec, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + return wcd9xxx_spmi_request_irq(irq, handler, name, data); +} + +static int msm8x16_wcd_free_irq(struct snd_soc_codec *codec, + int irq, void *data) +{ + return wcd9xxx_spmi_free_irq(irq, data); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .enable_mb_source = msm8x16_wcd_enable_ext_mb_source, + .trim_btn_reg = msm8x16_trim_btn_reg, + .compute_impedance = msm8x16_wcd_mbhc_calc_impedance, + .set_micbias_value = msm8x16_wcd_set_micb_v, + .set_auto_zeroing = msm8x16_wcd_set_auto_zeroing, + .get_hwdep_fw_cal = msm8x16_wcd_get_hwdep_fw_cal, + .set_cap_mode = msm8x16_wcd_configure_cap, + .register_notifier = msm8x16_register_notifier, + .request_irq = msm8x16_wcd_request_irq, + .irq_control = wcd9xxx_spmi_irq_control, + .free_irq = msm8x16_wcd_free_irq, + .clk_setup = msm8x16_mbhc_clk_setup, + .map_btn_code_to_num = msm8x16_mbhc_map_btn_code_to_num, + .lock_sleep = msm8x16_spmi_lock_sleep, + .micbias_enable_status = msm8x16_wcd_micb_en_status, + .mbhc_bias = msm8x16_wcd_enable_master_bias, + .mbhc_common_micb_ctrl = msm8x16_wcd_mbhc_common_micb_ctrl, + .micb_internal = msm8x16_wcd_mbhc_internal_micbias_ctrl, + .hph_pa_on_status = msm8x16_wcd_mbhc_hph_pa_on_status, + .set_btn_thr = msm8x16_wcd_mbhc_program_btn_thr, + .extn_use_mb = msm8x16_wcd_use_mb, +}; + +static const uint32_t wcd_imped_val[] = {4, 8, 12, 13, 16, + 20, 24, 28, 32, + 36, 40, 44, 48}; + +void msm8x16_notifier_call(struct snd_soc_codec *codec, + const enum wcd_notify_event event) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + pr_err("%s: notifier call event %d\n", __func__, event); + blocking_notifier_call_chain(&msm8x16_wcd->notifier, event, + &msm8x16_wcd->mbhc); +} + +static void msm8x16_wcd_boost_on(struct snd_soc_codec *codec) +{ + u8 dest = 0x00; + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + + if ((dest & MASK_MSB_BIT) == 0) { + pr_err("PMIC MBG not ON, enable codec hw_en MB bit again\n"); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30); + /* Allow 1ms for PMIC MBG state to be updated */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + } + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, + 0x0F, 0x0F); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, + 0x0F); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, + 0x30); + if (get_codec_version(msm8x16_wcd) < CAJON_2_0) { + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, + 0x82); + } else { + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, + 0xA2); + } + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0x69, 0x69); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, + 0x01, 0x01); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, + 0x88, 0x88); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 0x03, 0x03); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, + 0xE1, 0xE1); + if (get_codec_version(msm8x16_wcd) < CAJON_2_0) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x20); + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0xDF, 0xDF); + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + } else { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0x40, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x20); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0x80, 0x80); + usleep_range(500, 510); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0x40, 0x40); + usleep_range(500, 510); + } +} + +static void msm8x16_wcd_boost_off(struct snd_soc_codec *codec) +{ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0xDF, 0x5F); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x00); +} + +static void msm8x16_wcd_bypass_on(struct snd_soc_codec *codec) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(msm8x16_wcd) < CAJON_2_0) { + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, + 0x07); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x02, 0x02); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x01, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x40, 0x40); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x80, 0x80); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0xDF, 0xDF); + } else { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x20); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x20, 0x20); + } +} + +static void msm8x16_wcd_bypass_off(struct snd_soc_codec *codec) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(msm8x16_wcd) < CAJON_2_0) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0x80, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x80, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x02, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x40, 0x00); + } else { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x20, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x00); + } +} + +static void msm8x16_wcd_boost_mode_sequence(struct snd_soc_codec *codec, + int flag) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + if (flag == EAR_PMU) { + switch (msm8x16_wcd->boost_option) { + case BOOST_SWITCH: + if (msm8x16_wcd->ear_pa_boost_set) { + msm8x16_wcd_boost_off(codec); + msm8x16_wcd_bypass_on(codec); + } + break; + case BOOST_ALWAYS: + msm8x16_wcd_boost_on(codec); + break; + case BYPASS_ALWAYS: + msm8x16_wcd_bypass_on(codec); + break; + case BOOST_ON_FOREVER: + msm8x16_wcd_boost_on(codec); + break; + default: + pr_err("%s: invalid boost option: %d\n", __func__, + msm8x16_wcd->boost_option); + break; + } + } else if (flag == EAR_PMD) { + switch (msm8x16_wcd->boost_option) { + case BOOST_SWITCH: + if (msm8x16_wcd->ear_pa_boost_set) + msm8x16_wcd_bypass_off(codec); + break; + case BOOST_ALWAYS: + msm8x16_wcd_boost_off(codec); + /* 80ms for EAR boost to settle down */ + msleep(80); + break; + case BYPASS_ALWAYS: + /* nothing to do as bypass on always */ + break; + case BOOST_ON_FOREVER: + /* nothing to do as boost on forever */ + break; + default: + pr_err("%s: invalid boost option: %d\n", __func__, + msm8x16_wcd->boost_option); + break; + } + } else if (flag == SPK_PMU) { + switch (msm8x16_wcd->boost_option) { + case BOOST_SWITCH: + if (msm8x16_wcd->spk_boost_set) { + msm8x16_wcd_bypass_off(codec); + msm8x16_wcd_boost_on(codec); + } + break; + case BOOST_ALWAYS: + msm8x16_wcd_boost_on(codec); + break; + case BYPASS_ALWAYS: + msm8x16_wcd_bypass_on(codec); + break; + case BOOST_ON_FOREVER: + msm8x16_wcd_boost_on(codec); + break; + default: + pr_err("%s: invalid boost option: %d\n", __func__, + msm8x16_wcd->boost_option); + break; + } + } else if (flag == SPK_PMD) { + switch (msm8x16_wcd->boost_option) { + case BOOST_SWITCH: + if (msm8x16_wcd->spk_boost_set) { + msm8x16_wcd_boost_off(codec); + /* + * Add 40 ms sleep for the spk + * boost to settle down + */ + msleep(40); + } + break; + case BOOST_ALWAYS: + msm8x16_wcd_boost_off(codec); + /* + * Add 40 ms sleep for the spk + * boost to settle down + */ + msleep(40); + break; + case BYPASS_ALWAYS: + /* nothing to do as bypass on always */ + break; + case BOOST_ON_FOREVER: + /* nothing to do as boost on forever */ + break; + default: + pr_err("%s: invalid boost option: %d\n", __func__, + msm8x16_wcd->boost_option); + break; + } + } +} + +static int msm8x16_wcd_dt_parse_vreg_info(struct device *dev, + struct msm8x16_wcd_regulator *vreg, const char *vreg_name, + bool ondemand) +{ + int len, ret = 0; + const __be32 *prop; + char prop_name[CODEC_DT_MAX_PROP_SIZE]; + struct device_node *regnode = NULL; + u32 prop_val; + + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply", + vreg_name); + regnode = of_parse_phandle(dev->of_node, prop_name, 0); + + if (!regnode) { + dev_err(dev, "Looking up %s property in node %s failed\n", + prop_name, dev->of_node->full_name); + return -ENODEV; + } + + dev_err(dev, "Looking up %s property in node %s\n", + prop_name, dev->of_node->full_name); + + vreg->name = vreg_name; + vreg->ondemand = ondemand; + + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, + "qcom,%s-voltage", vreg_name); + prop = of_get_property(dev->of_node, prop_name, &len); + + if (!prop || (len != (2 * sizeof(__be32)))) { + dev_err(dev, "%s %s property\n", + prop ? "invalid format" : "no", prop_name); + return -EINVAL; + } + vreg->min_uv = be32_to_cpup(&prop[0]); + vreg->max_uv = be32_to_cpup(&prop[1]); + + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, + "qcom,%s-current", vreg_name); + + ret = of_property_read_u32(dev->of_node, prop_name, &prop_val); + if (ret) { + dev_err(dev, "Looking up %s property in node %s failed", + prop_name, dev->of_node->full_name); + return -EFAULT; + } + vreg->optimum_ua = prop_val; + + dev_err(dev, "%s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n\n", vreg->name, + vreg->min_uv, vreg->max_uv, vreg->optimum_ua, vreg->ondemand); + return 0; +} + +static void msm8x16_wcd_dt_parse_boost_info(struct snd_soc_codec *codec) +{ + struct msm8x16_wcd_priv *msm8x16_wcd_priv = + snd_soc_codec_get_drvdata(codec); + const char *prop_name = "qcom,cdc-boost-voltage"; + int boost_voltage, ret; + + ret = of_property_read_u32(codec->dev->of_node, prop_name, + &boost_voltage); + if (ret) { + dev_err(codec->dev, "Looking up %s property in node %s failed\n", + prop_name, codec->dev->of_node->full_name); + boost_voltage = DEFAULT_BOOST_VOLTAGE; + } + if (boost_voltage < MIN_BOOST_VOLTAGE || + boost_voltage > MAX_BOOST_VOLTAGE) { + dev_err(codec->dev, + "Incorrect boost voltage. Reverting to default\n"); + boost_voltage = DEFAULT_BOOST_VOLTAGE; + } + + msm8x16_wcd_priv->boost_voltage = + VOLTAGE_CONVERTER(boost_voltage, MIN_BOOST_VOLTAGE, + BOOST_VOLTAGE_STEP); + dev_err(codec->dev, "Boost voltage value is: %d\n", + boost_voltage); +} + +static void msm8x16_wcd_dt_parse_micbias_info(struct device *dev, + struct wcd9xxx_micbias_setting *micbias) +{ + const char *prop_name = "qcom,cdc-micbias-cfilt-mv"; + int ret; + + ret = of_property_read_u32(dev->of_node, prop_name, + &micbias->cfilt1_mv); + if (ret) { + dev_err(dev, "Looking up %s property in node %s failed", + prop_name, dev->of_node->full_name); + micbias->cfilt1_mv = MICBIAS_DEFAULT_VAL; + } +} + +static struct msm8x16_wcd_pdata *msm8x16_wcd_populate_dt_pdata( + struct device *dev) +{ + struct msm8x16_wcd_pdata *pdata; + int ret, static_cnt, ond_cnt, idx, i; + const char *name = NULL; + const char *static_prop_name = "qcom,cdc-static-supplies"; + const char *ond_prop_name = "qcom,cdc-on-demand-supplies"; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + static_cnt = of_property_count_strings(dev->of_node, static_prop_name); + if (IS_ERR_VALUE(static_cnt)) { + dev_err(dev, "%s: Failed to get static supplies %d\n", __func__, + static_cnt); + ret = -EINVAL; + goto err; + } + + /* On-demand supply list is an optional property */ + ond_cnt = of_property_count_strings(dev->of_node, ond_prop_name); + if (IS_ERR_VALUE(ond_cnt)) + ond_cnt = 0; + + WARN_ON(static_cnt <= 0 || ond_cnt < 0); + if ((static_cnt + ond_cnt) > ARRAY_SIZE(pdata->regulator)) { + dev_err(dev, "%s: Num of supplies %u > max supported %zd\n", + __func__, (static_cnt + ond_cnt), + ARRAY_SIZE(pdata->regulator)); + ret = -EINVAL; + goto err; + } + + for (idx = 0; idx < static_cnt; idx++) { + ret = of_property_read_string_index(dev->of_node, + static_prop_name, idx, + &name); + if (ret) { + dev_err(dev, "%s: of read string %s idx %d error %d\n", + __func__, static_prop_name, idx, ret); + goto err; + } + + dev_err(dev, "%s: Found static cdc supply %s\n", __func__, + name); + ret = msm8x16_wcd_dt_parse_vreg_info(dev, + &pdata->regulator[idx], + name, false); + if (ret) { + dev_err(dev, "%s:err parsing vreg for %s idx %d\n", + __func__, name, idx); + goto err; + } + } + + for (i = 0; i < ond_cnt; i++, idx++) { + ret = of_property_read_string_index(dev->of_node, ond_prop_name, + i, &name); + if (ret) { + dev_err(dev, "%s: err parsing on_demand for %s idx %d\n", + __func__, ond_prop_name, i); + goto err; + } + + dev_err(dev, "%s: Found on-demand cdc supply %s\n", __func__, + name); + ret = msm8x16_wcd_dt_parse_vreg_info(dev, + &pdata->regulator[idx], + name, true); + if (ret) { + dev_err(dev, "%s: err parsing vreg on_demand for %s idx %d\n", + __func__, name, idx); + goto err; + } + } + msm8x16_wcd_dt_parse_micbias_info(dev, &pdata->micbias); + return pdata; +err: + devm_kfree(dev, pdata); + dev_err(dev, "%s: Failed to populate DT data ret = %d\n", + __func__, ret); + return NULL; +} + +static int msm8x16_wcd_codec_enable_on_demand_supply( + struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + struct on_demand_supply *supply; + + if (w->shift >= ON_DEMAND_SUPPLIES_MAX) { + dev_err(codec->dev, "%s: error index > MAX Demand supplies", + __func__); + ret = -EINVAL; + goto out; + } + dev_err(codec->dev, "%s: supply: %s event: %d ref: %d\n", + __func__, on_demand_supply_name[w->shift], event, + atomic_read(&msm8x16_wcd->on_demand_list[w->shift].ref)); + + supply = &msm8x16_wcd->on_demand_list[w->shift]; + WARN_ONCE(!supply->supply, "%s isn't defined\n", + on_demand_supply_name[w->shift]); + if (!supply->supply) { + dev_err(codec->dev, "%s: err supply not present ond for %d", + __func__, w->shift); + goto out; + } + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (atomic_inc_return(&supply->ref) == 1) + ret = regulator_enable(supply->supply); + if (ret) + dev_err(codec->dev, "%s: Failed to enable %s\n", + __func__, + on_demand_supply_name[w->shift]); + break; + case SND_SOC_DAPM_POST_PMD: + if (atomic_read(&supply->ref) == 0) { + dev_err(codec->dev, "%s: %s supply has been disabled.\n", + __func__, on_demand_supply_name[w->shift]); + goto out; + } + if (atomic_dec_return(&supply->ref) == 0) + ret = regulator_disable(supply->supply); + if (ret) + dev_err(codec->dev, "%s: Failed to disable %s\n", + __func__, + on_demand_supply_name[w->shift]); + break; + default: + break; + } +out: + return ret; +} + +static int msm8x16_wcd_codec_enable_clock_block(struct snd_soc_codec *codec, + int enable) +{ + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + if (enable) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x01); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x03); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30, 0x30); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80, 0x80); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x0C); + if (pdata->mclk_freq == MCLK_RATE_12P288MHZ) + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x00); + else if (pdata->mclk_freq == MCLK_RATE_9P6MHZ) + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x01); + } else { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x00); + + } + return 0; +} + +static int msm8x16_wcd_codec_enable_charge_pump(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm8x16_wcd_codec_enable_clock_block(codec, 1); + if (!(strcmp(w->name, "EAR CP"))) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x80); + msm8x16_wcd_boost_mode_sequence(codec, EAR_PMU); + } else if (get_codec_version(msm8x16_wcd) == DIANGU) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x80); + } else { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0xC0, 0xC0); + } + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + break; + case SND_SOC_DAPM_POST_PMD: + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + if (!(strcmp(w->name, "EAR CP"))) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x00); + if (msm8x16_wcd->boost_option != BOOST_ALWAYS) { + dev_err(codec->dev, + "%s: boost_option:%d, tear down ear\n", + __func__, msm8x16_wcd->boost_option); + msm8x16_wcd_boost_mode_sequence(codec, EAR_PMD); + } + /* + * Reset pa select bit from ear to hph after ear pa + * is disabled and HPH DAC disable to reduce ear + * turn off pop and avoid HPH pop in concurrency + */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x80, 0x00); + } else { + if (get_codec_version(msm8x16_wcd) < DIANGU) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x40, 0x00); + if (msm8x16_wcd->rx_bias_count == 0) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x00); + dev_err(codec->dev, "%s: rx_bias_count = %d\n", + __func__, msm8x16_wcd->rx_bias_count); + } + break; + } + return 0; +} + +static int msm8x16_wcd_ear_pa_boost_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = + (msm8x16_wcd->ear_pa_boost_set ? 1 : 0); + dev_err(codec->dev, "%s: msm8x16_wcd->ear_pa_boost_set = %d\n", + __func__, msm8x16_wcd->ear_pa_boost_set); + return 0; +} + +static int msm8x16_wcd_ear_pa_boost_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm8x16_wcd_priv *msm8x16_wcd = + snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + msm8x16_wcd->ear_pa_boost_set = + (ucontrol->value.integer.value[0] ? true : false); + return 0; +} + +static int msm8x16_wcd_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ear_pa_gain = snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL); + + ear_pa_gain = (ear_pa_gain >> 5) & 0x1; + + if (ear_pa_gain == 0x00) { + ucontrol->value.integer.value[0] = 0; + } else if (ear_pa_gain == 0x01) { + ucontrol->value.integer.value[0] = 1; + } else { + dev_err(codec->dev, "%s: ERROR: Unsupported Ear Gain = 0x%x\n", + __func__, ear_pa_gain); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = ear_pa_gain; + dev_err(codec->dev, "%s: ear_pa_gain = 0x%x\n", + __func__, ear_pa_gain); + return 0; +} + +static int msm8x16_wcd_loopback_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return pdata->lb_mode; +} + +static int msm8x16_wcd_loopback_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + pdata->lb_mode = false; + break; + case 1: + pdata->lb_mode = true; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int msm8x16_wcd_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + ear_pa_gain = 0x00; + break; + case 1: + ear_pa_gain = 0x20; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits_wrapper(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, ear_pa_gain); + return 0; +} + +static int msm8x16_wcd_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + if (msm8x16_wcd->hph_mode == NORMAL_MODE) { + ucontrol->value.integer.value[0] = 0; + } else if (msm8x16_wcd->hph_mode == HD2_MODE) { + ucontrol->value.integer.value[0] = 1; + } else { + dev_err(codec->dev, "%s: ERROR: Default HPH Mode= %d\n", + __func__, msm8x16_wcd->hph_mode); + } + + dev_err(codec->dev, "%s: msm8x16_wcd->hph_mode = %d\n", __func__, + msm8x16_wcd->hph_mode); + return 0; +} + +static int msm8x16_wcd_hph_mode_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + msm8x16_wcd->hph_mode = NORMAL_MODE; + break; + case 1: + if (get_codec_version(msm8x16_wcd) >= DIANGU) + msm8x16_wcd->hph_mode = HD2_MODE; + break; + default: + msm8x16_wcd->hph_mode = NORMAL_MODE; + break; + } + dev_err(codec->dev, "%s: msm8x16_wcd->hph_mode_set = %d\n", + __func__, msm8x16_wcd->hph_mode); + return 0; +} + +static int msm8x16_wcd_boost_option_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + if (msm8x16_wcd->boost_option == BOOST_SWITCH) { + ucontrol->value.integer.value[0] = 0; + } else if (msm8x16_wcd->boost_option == BOOST_ALWAYS) { + ucontrol->value.integer.value[0] = 1; + } else if (msm8x16_wcd->boost_option == BYPASS_ALWAYS) { + ucontrol->value.integer.value[0] = 2; + } else if (msm8x16_wcd->boost_option == BOOST_ON_FOREVER) { + ucontrol->value.integer.value[0] = 3; + } else { + dev_err(codec->dev, "%s: ERROR: Unsupported Boost option= %d\n", + __func__, msm8x16_wcd->boost_option); + return -EINVAL; + } + + dev_err(codec->dev, "%s: msm8x16_wcd->boost_option = %d\n", __func__, + msm8x16_wcd->boost_option); + return 0; +} + +static int msm8x16_wcd_boost_option_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + msm8x16_wcd->boost_option = BOOST_SWITCH; + break; + case 1: + msm8x16_wcd->boost_option = BOOST_ALWAYS; + break; + case 2: + msm8x16_wcd->boost_option = BYPASS_ALWAYS; + msm8x16_wcd_bypass_on(codec); + break; + case 3: + msm8x16_wcd->boost_option = BOOST_ON_FOREVER; + msm8x16_wcd_boost_on(codec); + break; + default: + pr_err("%s: invalid boost option: %d\n", __func__, + msm8x16_wcd->boost_option); + return -EINVAL; + } + dev_err(codec->dev, "%s: msm8x16_wcd->boost_option_set = %d\n", + __func__, msm8x16_wcd->boost_option); + return 0; +} + +static int msm8x16_wcd_ext_spk_boost_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + if (msm8x16_wcd->ext_spk_boost_set == false) + ucontrol->value.integer.value[0] = 0; + else + ucontrol->value.integer.value[0] = 1; + + dev_err(codec->dev, "%s: msm8x16_wcd->ext_spk_boost_set = %d\n", + __func__, msm8x16_wcd->ext_spk_boost_set); + return 0; +} + +static int msm8x16_wcd_ext_spk_boost_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + msm8x16_wcd->ext_spk_boost_set = false; + break; + case 1: + msm8x16_wcd->ext_spk_boost_set = true; + break; + default: + return -EINVAL; + } + dev_err(codec->dev, "%s: msm8x16_wcd->spk_boost_set = %d\n", + __func__, msm8x16_wcd->spk_boost_set); + return 0; +} +static int msm8x16_wcd_get_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + (snd_soc_read_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx)) & + (1 << band_idx)) != 0; + + dev_err(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int msm8x16_wcd_put_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx), + (1 << band_idx), (value << band_idx)); + + dev_err(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + ((snd_soc_read_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx)) & + (1 << band_idx)) != 0)); + + return 0; +} +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)); + + snd_soc_write_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 8); + + snd_soc_write_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 16); + + snd_soc_write_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + + 64 * iir_idx)) & 0x3f) << 24); + + return value; + +} + +static int msm8x16_wcd_get_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + dev_err(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value & 0xFF)); + + snd_soc_write_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 24) & 0x3F); + +} + +static int msm8x16_wcd_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + /* Mask top bit it is reserved */ + /* Updates addr automatically for each B2 write */ + snd_soc_write_wrapper(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[0]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[1]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[2]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[3]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[4]); + + dev_err(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static int msm8x16_wcd_compander_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + int comp_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int rx_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + dev_err(codec->dev, "%s: msm8x16_wcd->comp[%d]_enabled[%d] = %d\n", + __func__, comp_idx, rx_idx, + msm8x16_wcd->comp_enabled[rx_idx]); + + ucontrol->value.integer.value[0] = msm8x16_wcd->comp_enabled[rx_idx]; + + dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm8x16_wcd_compander_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + int comp_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int rx_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (get_codec_version(msm8x16_wcd) >= DIANGU) { + if (!value) + msm8x16_wcd->comp_enabled[rx_idx] = 0; + else + msm8x16_wcd->comp_enabled[rx_idx] = comp_idx; + } + + dev_err(codec->dev, "%s: msm8x16_wcd->comp[%d]_enabled[%d] = %d\n", + __func__, comp_idx, rx_idx, + msm8x16_wcd->comp_enabled[rx_idx]); + + return 0; +} + +static const char * const msm8x16_wcd_loopback_mode_ctrl_text[] = { + "DISABLE", "ENABLE"}; +static const struct soc_enum msm8x16_wcd_loopback_mode_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_loopback_mode_ctrl_text), +}; + +static const char * const msm8x16_wcd_ear_pa_boost_ctrl_text[] = { + "DISABLE", "ENABLE"}; +static const struct soc_enum msm8x16_wcd_ear_pa_boost_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_ear_pa_boost_ctrl_text), +}; + +static const char * const msm8x16_wcd_ear_pa_gain_text[] = { + "POS_1P5_DB", "POS_6_DB"}; +static const struct soc_enum msm8x16_wcd_ear_pa_gain_enum[] = { + SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_ear_pa_gain_text), +}; + +static const char * const msm8x16_wcd_boost_option_ctrl_text[] = { + "BOOST_SWITCH", "BOOST_ALWAYS", "BYPASS_ALWAYS", + "BOOST_ON_FOREVER"}; +static const struct soc_enum msm8x16_wcd_boost_option_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(4, msm8x16_wcd_boost_option_ctrl_text), +}; +static const char * const msm8x16_wcd_spk_boost_ctrl_text[] = { + "DISABLE", "ENABLE"}; +static const struct soc_enum msm8x16_wcd_spk_boost_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_spk_boost_ctrl_text), +}; + +static const char * const msm8x16_wcd_ext_spk_boost_ctrl_text[] = { + "DISABLE", "ENABLE"}; +static const struct soc_enum msm8x16_wcd_ext_spk_boost_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_ext_spk_boost_ctrl_text), +}; + +static const char * const msm8x16_wcd_hph_mode_ctrl_text[] = { + "NORMAL", "HD2"}; +static const struct soc_enum msm8x16_wcd_hph_mode_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(msm8x16_wcd_hph_mode_ctrl_text), + msm8x16_wcd_hph_mode_ctrl_text), +}; + +/*cut of frequency for high pass filter*/ +static const char * const cf_text[] = { + "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz" +}; + +static const struct soc_enum cf_dec1_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX1_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec2_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX2_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_rxmix1_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX1_B4_CTL, 0, 3, cf_text); + +static const struct soc_enum cf_rxmix2_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX2_B4_CTL, 0, 3, cf_text); + +static const struct soc_enum cf_rxmix3_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX3_B4_CTL, 0, 3, cf_text); + +static const struct snd_kcontrol_new msm8x16_wcd_snd_controls[] = { + + SOC_ENUM_EXT("RX HPH Mode", msm8x16_wcd_hph_mode_ctl_enum[0], + msm8x16_wcd_hph_mode_get, msm8x16_wcd_hph_mode_set), + + SOC_ENUM_EXT("Boost Option", msm8x16_wcd_boost_option_ctl_enum[0], + msm8x16_wcd_boost_option_get, msm8x16_wcd_boost_option_set), + + SOC_ENUM_EXT("EAR PA Boost", msm8x16_wcd_ear_pa_boost_ctl_enum[0], + msm8x16_wcd_ear_pa_boost_get, msm8x16_wcd_ear_pa_boost_set), + + SOC_ENUM_EXT("EAR PA Gain", msm8x16_wcd_ear_pa_gain_enum[0], + msm8x16_wcd_pa_gain_get, msm8x16_wcd_pa_gain_put), + + SOC_ENUM_EXT("Ext Spk Boost", msm8x16_wcd_ext_spk_boost_ctl_enum[0], + msm8x16_wcd_ext_spk_boost_get, msm8x16_wcd_ext_spk_boost_set), + + SOC_ENUM_EXT("LOOPBACK Mode", msm8x16_wcd_loopback_mode_ctl_enum[0], + msm8x16_wcd_loopback_mode_get, msm8x16_wcd_loopback_mode_put), + + SOC_SINGLE_TLV("ADC1 Volume", MSM89XX_PMIC_ANALOG_TX_1_EN, 3, + 8, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", MSM89XX_PMIC_ANALOG_TX_2_EN, 3, + 8, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", MSM89XX_PMIC_ANALOG_TX_3_EN, 3, + 8, 0, analog_gain), + + SOC_SINGLE_SX_TLV("RX1 Digital Volume", + MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Digital Volume", + MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Digital Volume", + MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("DEC1 Volume", + MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC2 Volume", + MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP4 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR2 INP1 Volume", + MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL, + 0, -84, 40, digital_gain), + + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + + SOC_SINGLE("TX1 HPF Switch", + MSM89XX_CDC_CORE_TX1_MUX_CTL, 3, 1, 0), + SOC_SINGLE("TX2 HPF Switch", + MSM89XX_CDC_CORE_TX2_MUX_CTL, 3, 1, 0), + + SOC_SINGLE("RX1 HPF Switch", + MSM89XX_CDC_CORE_RX1_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX2 HPF Switch", + MSM89XX_CDC_CORE_RX2_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX3 HPF Switch", + MSM89XX_CDC_CORE_RX3_B5_CTL, 2, 1, 0), + + SOC_ENUM("RX1 HPF cut off", cf_rxmix1_enum), + SOC_ENUM("RX2 HPF cut off", cf_rxmix2_enum), + SOC_ENUM("RX3 HPF cut off", cf_rxmix3_enum), + + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + msm8x16_wcd_get_iir_enable_audio_mixer, + msm8x16_wcd_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + msm8x16_wcd_get_iir_enable_audio_mixer, + msm8x16_wcd_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + msm8x16_wcd_get_iir_enable_audio_mixer, + msm8x16_wcd_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + msm8x16_wcd_get_iir_enable_audio_mixer, + msm8x16_wcd_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + msm8x16_wcd_get_iir_enable_audio_mixer, + msm8x16_wcd_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band1", IIR2, BAND1, 1, 0, + msm8x16_wcd_get_iir_enable_audio_mixer, + msm8x16_wcd_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band2", IIR2, BAND2, 1, 0, + msm8x16_wcd_get_iir_enable_audio_mixer, + msm8x16_wcd_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band3", IIR2, BAND3, 1, 0, + msm8x16_wcd_get_iir_enable_audio_mixer, + msm8x16_wcd_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band4", IIR2, BAND4, 1, 0, + msm8x16_wcd_get_iir_enable_audio_mixer, + msm8x16_wcd_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band5", IIR2, BAND5, 1, 0, + msm8x16_wcd_get_iir_enable_audio_mixer, + msm8x16_wcd_put_iir_enable_audio_mixer), + + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + msm8x16_wcd_get_iir_band_audio_mixer, + msm8x16_wcd_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + msm8x16_wcd_get_iir_band_audio_mixer, + msm8x16_wcd_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + msm8x16_wcd_get_iir_band_audio_mixer, + msm8x16_wcd_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + msm8x16_wcd_get_iir_band_audio_mixer, + msm8x16_wcd_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + msm8x16_wcd_get_iir_band_audio_mixer, + msm8x16_wcd_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band1", IIR2, BAND1, 255, 0, 5, + msm8x16_wcd_get_iir_band_audio_mixer, + msm8x16_wcd_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band2", IIR2, BAND2, 255, 0, 5, + msm8x16_wcd_get_iir_band_audio_mixer, + msm8x16_wcd_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band3", IIR2, BAND3, 255, 0, 5, + msm8x16_wcd_get_iir_band_audio_mixer, + msm8x16_wcd_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band4", IIR2, BAND4, 255, 0, 5, + msm8x16_wcd_get_iir_band_audio_mixer, + msm8x16_wcd_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5, + msm8x16_wcd_get_iir_band_audio_mixer, + msm8x16_wcd_put_iir_band_audio_mixer), + + SOC_SINGLE_EXT("COMP0 RX1", COMPANDER_1, MSM89XX_RX1, 1, 0, + msm8x16_wcd_compander_get, msm8x16_wcd_compander_set), + + SOC_SINGLE_EXT("COMP0 RX2", COMPANDER_1, MSM89XX_RX2, 1, 0, + msm8x16_wcd_compander_get, msm8x16_wcd_compander_set), +}; + +static int tombak_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret; + uint32_t zl, zr; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm8x16_wcd_priv *priv = snd_soc_codec_get_drvdata(codec); + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + + hphr = mc->shift; + ret = wcd_mbhc_get_impedance(&priv->mbhc, &zl, &zr); + if (ret) + pr_err("%s: Failed to get mbhc imped", __func__); + pr_err("%s: zl %u, zr %u\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + tombak_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + tombak_hph_impedance_get, NULL), +}; + +static int tombak_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm8x16_wcd_priv *priv = snd_soc_codec_get_drvdata(codec); + struct wcd_mbhc *mbhc; + + if (!priv) { + pr_err("%s: msm8x16-wcd private data is NULL\n", + __func__); + return -EINVAL; + } + + mbhc = &priv->mbhc; + if (!mbhc) { + pr_err("%s: mbhc not initialized\n", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + pr_err("%s: hph_type = %u\n", __func__, mbhc->hph_type); + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + tombak_get_hph_type, NULL), +}; + +static const char * const rx_mix1_text[] = { + "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3" +}; + +static const char * const rx_mix2_text[] = { + "ZERO", "IIR1", "IIR2" +}; + +static const char * const dec_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2" +}; + +static const char * const dec3_mux_text[] = { + "ZERO", "DMIC3" +}; + +static const char * const dec4_mux_text[] = { + "ZERO", "DMIC4" +}; + +static const char * const adc2_mux_text[] = { + "ZERO", "INP2", "INP3" +}; + +static const char * const ext_spk_text[] = { + "Off", "On" +}; + +static const char * const wsa_spk_text[] = { + "ZERO", "WSA" +}; + +static const char * const rdac2_mux_text[] = { + "ZERO", "RX2", "RX1" +}; + +static const char * const iir_inp1_text[] = { + "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3" +}; + +static const struct soc_enum adc2_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + +static const struct soc_enum ext_spk_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(ext_spk_text), ext_spk_text); + +static const struct soc_enum wsa_spk_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(wsa_spk_text), wsa_spk_text); + +/* RX1 MIX1 */ +static const struct soc_enum rx_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, + 0, 6, rx_mix1_text); + +static const struct soc_enum rx_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, + 3, 6, rx_mix1_text); + +static const struct soc_enum rx_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B2_CTL, + 0, 6, rx_mix1_text); + +/* RX1 MIX2 */ +static const struct soc_enum rx_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B3_CTL, + 0, 3, rx_mix2_text); + +/* RX2 MIX1 */ +static const struct soc_enum rx2_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, + 0, 6, rx_mix1_text); + +static const struct soc_enum rx2_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, + 3, 6, rx_mix1_text); + +static const struct soc_enum rx2_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, + 0, 6, rx_mix1_text); + +/* RX2 MIX2 */ +static const struct soc_enum rx2_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B3_CTL, + 0, 3, rx_mix2_text); + +/* RX3 MIX1 */ +static const struct soc_enum rx3_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, + 0, 6, rx_mix1_text); + +static const struct soc_enum rx3_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, + 3, 6, rx_mix1_text); + +static const struct soc_enum rx3_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, + 0, 6, rx_mix1_text); + +/* DEC */ +static const struct soc_enum dec1_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B1_CTL, + 0, 6, dec_mux_text); + +static const struct soc_enum dec2_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B1_CTL, + 3, 6, dec_mux_text); + +static const struct soc_enum dec3_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX3_MUX_CTL, 0, + ARRAY_SIZE(dec3_mux_text), dec3_mux_text); + +static const struct soc_enum dec4_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX4_MUX_CTL, 0, + ARRAY_SIZE(dec4_mux_text), dec4_mux_text); + +static const struct soc_enum rdac2_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL, + 0, 3, rdac2_mux_text); + +static const struct soc_enum iir1_inp1_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL, + 0, 6, iir_inp1_text); + +static const struct soc_enum iir2_inp1_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL, + 0, 6, iir_inp1_text); + +static const struct snd_kcontrol_new ext_spk_mux = + SOC_DAPM_ENUM("Ext Spk Switch Mux", ext_spk_enum); + +static const struct snd_kcontrol_new rx_mix1_inp1_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_mix1_inp2_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_mix1_inp3_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP3 Mux", rx_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp1_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp2_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP2 Mux", rx2_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp3_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP3 Mux", rx2_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx3_mix1_inp1_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx3_mix1_inp2_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx3_mix1_inp3_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP3 Mux", rx3_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx1_mix2_inp1_mux = + SOC_DAPM_ENUM("RX1 MIX2 INP1 Mux", rx_mix2_inp1_chain_enum); + +static const struct snd_kcontrol_new rx2_mix2_inp1_mux = + SOC_DAPM_ENUM("RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum); + +static const struct snd_kcontrol_new tx_adc2_mux = + SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + +static int msm8x16_wcd_put_dec_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *w = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int dec_mux, decimator; + char *dec_name = NULL; + char *widget_name = NULL; + char *temp; + u16 tx_mux_ctl_reg; + u8 adc_dmic_sel = 0x0; + int ret = 0; + char *dec_num; + + if (ucontrol->value.enumerated.item[0] > e->items) { + dev_err(codec->dev, "%s: Invalid enum value: %d\n", + __func__, ucontrol->value.enumerated.item[0]); + return -EINVAL; + } + dec_mux = ucontrol->value.enumerated.item[0]; + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) { + dev_err(codec->dev, "%s: failed to copy string\n", + __func__); + return -ENOMEM; + } + temp = widget_name; + + dec_name = strsep(&widget_name, " "); + widget_name = temp; + if (!dec_name) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + + dec_num = strpbrk(dec_name, "12"); + if (dec_num == NULL) { + dev_err(codec->dev, "%s: Invalid DEC selected\n", __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec_num, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, dec_name); + ret = -EINVAL; + goto out; + } + + dev_err(w->dapm->dev, "%s(): widget = %s decimator = %u dec_mux = %u\n" + , __func__, w->name, decimator, dec_mux); + + switch (decimator) { + case 1: + case 2: + if ((dec_mux == 4) || (dec_mux == 5)) + adc_dmic_sel = 0x1; + else + adc_dmic_sel = 0x0; + break; + default: + dev_err(codec->dev, "%s: Invalid Decimator = %u\n", + __func__, decimator); + ret = -EINVAL; + goto out; + } + + tx_mux_ctl_reg = + MSM89XX_CDC_CORE_TX1_MUX_CTL + 32 * (decimator - 1); + + snd_soc_update_bits_wrapper(codec, tx_mux_ctl_reg, 0x1, adc_dmic_sel); + + ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + +out: + kfree(widget_name); + return ret; +} + +#define MSM89XX_DEC_ENUM(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_dapm_get_enum_double, \ + .put = msm8x16_wcd_put_dec_enum, \ + .private_value = (unsigned long)&xenum } + +static const struct snd_kcontrol_new dec1_mux = + MSM89XX_DEC_ENUM("DEC1 MUX Mux", dec1_mux_enum); + +static const struct snd_kcontrol_new dec2_mux = + MSM89XX_DEC_ENUM("DEC2 MUX Mux", dec2_mux_enum); + +static const struct snd_kcontrol_new dec3_mux = + SOC_DAPM_ENUM("DEC3 MUX Mux", dec3_mux_enum); + +static const struct snd_kcontrol_new dec4_mux = + SOC_DAPM_ENUM("DEC4 MUX Mux", dec4_mux_enum); + +static const struct snd_kcontrol_new rdac2_mux = + SOC_DAPM_ENUM("RDAC2 MUX Mux", rdac2_mux_enum); + +static const struct snd_kcontrol_new iir1_inp1_mux = + SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); + +static const char * const ear_text[] = { + "ZERO", "Switch", +}; + +static const struct soc_enum ear_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(ear_text), ear_text); + +static const struct snd_kcontrol_new ear_pa_mux[] = { + SOC_DAPM_ENUM("EAR_S", ear_enum) +}; + +static const struct snd_kcontrol_new wsa_spk_mux[] = { + SOC_DAPM_ENUM("WSA Spk Switch", wsa_spk_enum) +}; + +static const struct snd_kcontrol_new iir2_inp1_mux = + SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum); + +static const char * const hph_text[] = { + "ZERO", "Switch", +}; + +static const struct soc_enum hph_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(hph_text), hph_text); + +static const struct snd_kcontrol_new hphl_mux[] = { + SOC_DAPM_ENUM("HPHL", hph_enum) +}; + +static const struct snd_kcontrol_new hphr_mux[] = { + SOC_DAPM_ENUM("HPHR", hph_enum) +}; + +static const struct snd_kcontrol_new spkr_mux[] = { + SOC_DAPM_ENUM("SPK", hph_enum) +}; + +static const char * const lo_text[] = { + "ZERO", "Switch", +}; + +static const struct soc_enum lo_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(hph_text), hph_text); + +static const struct snd_kcontrol_new lo_mux[] = { + SOC_DAPM_ENUM("LINE_OUT", lo_enum) +}; + +static void msm8x16_wcd_codec_enable_adc_block(struct snd_soc_codec *codec, + int enable) +{ + struct msm8x16_wcd_priv *wcd8x16 = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s %d\n", __func__, enable); + + if (enable) { + wcd8x16->adc_count++; + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, + 0x20, 0x20); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x10, 0x10); + } else { + wcd8x16->adc_count--; + if (!wcd8x16->adc_count) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x10, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, + 0x20, 0x0); + } + } +} + +static int msm8x16_wcd_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 adc_reg; + u8 init_bit_shift; + + dev_err(codec->dev, "%s %d\n", __func__, event); + + adc_reg = MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2; + + if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN) + init_bit_shift = 5; + else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) || + (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN)) + init_bit_shift = 4; + else { + dev_err(codec->dev, "%s: Error, invalid adc register\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm8x16_wcd_codec_enable_adc_block(codec, 1); + if (w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x02, 0x02); + /* + * Add delay of 10 ms to give sufficient time for the voltage + * to shoot up and settle so that the txfe init does not + * happen when the input voltage is changing too much. + */ + usleep_range(10000, 10010); + snd_soc_update_bits_wrapper(codec, + adc_reg, 1 << init_bit_shift, + 1 << init_bit_shift); + if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, + 0x03, 0x00); + else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) || + (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN)) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, + 0x03, 0x00); + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * Add delay of 12 ms before deasserting the init + * to reduce the tx pop + */ + usleep_range(12000, 12010); + snd_soc_update_bits_wrapper(codec, + adc_reg, 1 << init_bit_shift, 0x00); + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + break; + case SND_SOC_DAPM_POST_PMD: + msm8x16_wcd_codec_enable_adc_block(codec, 0); + if (w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x02, 0x00); + if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, + 0x03, 0x02); + else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) || + (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN)) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, + 0x03, 0x02); + + break; + } + return 0; +} + +static int msm8x16_wcd_codec_enable_spk_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s %d %s\n", __func__, event, w->name); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x01, 0x01); + switch (msm8x16_wcd->boost_option) { + case BOOST_SWITCH: + if (!msm8x16_wcd->spk_boost_set) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 0x10, 0x10); + break; + case BOOST_ALWAYS: + case BOOST_ON_FOREVER: + break; + case BYPASS_ALWAYS: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 0x10, 0x10); + break; + default: + pr_err("%s: invalid boost option: %d\n", __func__, + msm8x16_wcd->boost_option); + break; + } + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0xE0, 0xE0); + if (get_codec_version(msm8x16_wcd) != TOMBAK_1_0) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x01, 0x01); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + switch (msm8x16_wcd->boost_option) { + case BOOST_SWITCH: + if (msm8x16_wcd->spk_boost_set) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xEF, 0xEF); + else + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 0x10, 0x00); + break; + case BOOST_ALWAYS: + case BOOST_ON_FOREVER: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xEF, 0xEF); + break; + case BYPASS_ALWAYS: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x00); + break; + default: + pr_err("%s: invalid boost option: %d\n", __func__, + msm8x16_wcd->boost_option); + break; + } + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00); + snd_soc_update_bits_wrapper(codec, w->reg, 0x80, 0x80); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x01); + msm8x16_wcd->mute_mask |= SPKR_PA_DISABLE; + /* + * Add 1 ms sleep for the mute to take effect + */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x10); + if (get_codec_version(msm8x16_wcd) < CAJON_2_0) + msm8x16_wcd_boost_mode_sequence(codec, SPK_PMD); + snd_soc_update_bits_wrapper(codec, w->reg, 0x80, 0x00); + switch (msm8x16_wcd->boost_option) { + case BOOST_SWITCH: + if (msm8x16_wcd->spk_boost_set) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xEF, 0x69); + break; + case BOOST_ALWAYS: + case BOOST_ON_FOREVER: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xEF, 0x69); + break; + case BYPASS_ALWAYS: + break; + default: + pr_err("%s: invalid boost option: %d\n", __func__, + msm8x16_wcd->boost_option); + break; + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0xE0, 0x00); + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x01, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x00); + if (get_codec_version(msm8x16_wcd) != TOMBAK_1_0) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x01, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x00); + if (get_codec_version(msm8x16_wcd) >= CAJON_2_0) + msm8x16_wcd_boost_mode_sequence(codec, SPK_PMD); + break; + } + return 0; +} + +static int msm8x16_wcd_codec_enable_dig_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + + dev_err(codec->dev, "%s event %d w->name %s\n", __func__, + event, w->name); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm8x16_wcd_codec_enable_clock_block(codec, 1); + snd_soc_update_bits_wrapper(codec, w->reg, 0x80, 0x80); + msm8x16_wcd_boost_mode_sequence(codec, SPK_PMU); + break; + case SND_SOC_DAPM_POST_PMD: + if (msm8x16_wcd->rx_bias_count == 0) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x00); + } + return 0; +} + +static int msm8x16_wcd_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + u8 dmic_clk_en; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + unsigned int dmic; + int ret; + char *dec_num = strpbrk(w->name, "12"); + + if (dec_num == NULL) { + dev_err(codec->dev, "%s: Invalid DMIC\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(dec_num, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, + "%s: Invalid DMIC line on the codec\n", __func__); + return -EINVAL; + } + + switch (dmic) { + case 1: + case 2: + dmic_clk_en = 0x01; + dmic_clk_cnt = &(msm8x16_wcd->dmic_1_2_clk_cnt); + dmic_clk_reg = MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL; + dev_err(codec->dev, + "%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits_wrapper(codec, dmic_clk_reg, + 0x0E, 0x02); + snd_soc_update_bits_wrapper(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + if (dmic == 1) + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_TX1_DMIC_CTL, 0x07, 0x01); + if (dmic == 2) + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_TX2_DMIC_CTL, 0x07, 0x01); + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) + snd_soc_update_bits_wrapper(codec, dmic_clk_reg, + dmic_clk_en, 0); + break; + } + return 0; +} + +static bool msm8x16_wcd_use_mb(struct snd_soc_codec *codec) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(msm8x16_wcd) < CAJON) + return true; + else + return false; +} + +static void msm8x16_wcd_set_auto_zeroing(struct snd_soc_codec *codec, + bool enable) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(msm8x16_wcd) < CONGA) { + if (enable) + /* + * Set autozeroing for special headset detection and + * buttons to work. + */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x18, 0x10); + else + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x18, 0x00); + + } else { + pr_err("%s: Auto Zeroing is not required from CONGA\n", + __func__); + } +} + +static void msm8x16_trim_btn_reg(struct snd_soc_codec *codec) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(msm8x16_wcd) == TOMBAK_1_0) { + pr_err("%s: This device needs to be trimmed\n", __func__); + /* + * Calculate the trim value for each device used + * till is comes in production by hardware team + */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5, 0xA5); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_TRIM_CTRL2, + 0xFF, 0x30); + } else { + pr_err("%s: This device is trimmed at ATE\n", __func__); + } +} +static int msm8x16_wcd_enable_ext_mb_source(struct wcd_mbhc *mbhc, + bool turn_on) +{ + int ret = 0; + static int count; + struct snd_soc_codec *codec = mbhc->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + dev_err(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on, + count); + if (turn_on) { + if (!count) { + ret = snd_soc_dapm_force_enable_pin(dapm, + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(dapm); + } + count++; + } else { + if (count > 0) + count--; + if (!count) { + ret = snd_soc_dapm_disable_pin(dapm, + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(dapm); + } + } + + if (ret) + dev_err(codec->dev, "%s: Failed to %s external micbias source\n", + __func__, turn_on ? "enable" : "disabled"); + else + dev_err(codec->dev, "%s: %s external micbias source\n", + __func__, turn_on ? "Enabled" : "Disabled"); + + return ret; +} + +static int msm8x16_wcd_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = + snd_soc_codec_get_drvdata(codec); + u16 micb_int_reg; + char *internal1_text = "Internal1"; + char *internal2_text = "Internal2"; + char *internal3_text = "Internal3"; + char *external2_text = "External2"; + char *external_text = "External"; + bool micbias2; + + dev_err(codec->dev, "%s %d\n", __func__, event); + switch (w->reg) { + case MSM89XX_PMIC_ANALOG_MICB_1_EN: + case MSM89XX_PMIC_ANALOG_MICB_2_EN: + micb_int_reg = MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS; + break; + default: + dev_err(codec->dev, + "%s: Error, invalid micbias register 0x%x\n", + __func__, w->reg); + return -EINVAL; + } + + micbias2 = (snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN) & 0x80); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (strnstr(w->name, internal1_text, strlen(w->name))) { + if (get_codec_version(msm8x16_wcd) >= CAJON) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, + 0x02, 0x02); + snd_soc_update_bits_wrapper(codec, + micb_int_reg, 0x80, 0x80); + } else if (strnstr(w->name, internal2_text, strlen(w->name))) { + snd_soc_update_bits_wrapper(codec, + micb_int_reg, 0x10, 0x10); + snd_soc_update_bits_wrapper(codec, + w->reg, 0x60, 0x00); + } else if (strnstr(w->name, internal3_text, strlen(w->name))) { + snd_soc_update_bits_wrapper(codec, + micb_int_reg, 0x2, 0x2); + /* + * update MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2 + * for external bias only, not for external2. + */ + } else if (!strnstr(w->name, external2_text, strlen(w->name)) && + strnstr(w->name, external_text, + strlen(w->name))) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, + 0x02, 0x02); + } + if (!strnstr(w->name, external_text, strlen(w->name))) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN, 0x05, 0x04); + if (w->reg == MSM89XX_PMIC_ANALOG_MICB_1_EN) + msm8x16_wcd_configure_cap(codec, true, micbias2); + + break; + case SND_SOC_DAPM_POST_PMU: + if (get_codec_version(msm8x16_wcd) <= TOMBAK_2_0) + usleep_range(20000, 20100); + if (strnstr(w->name, internal1_text, strlen(w->name))) { + snd_soc_update_bits_wrapper(codec, + micb_int_reg, 0x40, 0x40); + } else if (strnstr(w->name, internal2_text, strlen(w->name))) { + snd_soc_update_bits_wrapper(codec, + micb_int_reg, 0x08, 0x08); + msm8x16_notifier_call(codec, + WCD_EVENT_POST_MICBIAS_2_ON); + } else if (strnstr(w->name, internal3_text, 30)) { + snd_soc_update_bits_wrapper(codec, + micb_int_reg, 0x01, 0x01); + } else if (strnstr(w->name, external2_text, strlen(w->name))) { + msm8x16_notifier_call(codec, + WCD_EVENT_POST_MICBIAS_2_ON); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (strnstr(w->name, internal1_text, strlen(w->name))) { + snd_soc_update_bits_wrapper(codec, + micb_int_reg, 0xC0, 0x40); + } else if (strnstr(w->name, internal2_text, strlen(w->name))) { + msm8x16_notifier_call(codec, + WCD_EVENT_POST_MICBIAS_2_OFF); + } else if (strnstr(w->name, internal3_text, 30)) { + snd_soc_update_bits_wrapper(codec, + micb_int_reg, 0x2, 0x0); + } else if (strnstr(w->name, external2_text, strlen(w->name))) { + /* + * send micbias turn off event to mbhc driver and then + * break, as no need to set MICB_1_EN register. + */ + msm8x16_notifier_call(codec, + WCD_EVENT_POST_MICBIAS_2_OFF); + break; + } + if (w->reg == MSM89XX_PMIC_ANALOG_MICB_1_EN) + msm8x16_wcd_configure_cap(codec, false, micbias2); + break; + } + return 0; +} + +static void tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct msm8x16_wcd_priv *msm8x16_wcd; + struct snd_soc_codec *codec; + u16 tx_mux_ctl_reg; + u8 hpf_cut_of_freq; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + msm8x16_wcd = hpf_work->msm8x16_wcd; + codec = hpf_work->msm8x16_wcd->codec; + hpf_cut_of_freq = hpf_work->tx_hpf_cut_of_freq; + + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL + + (hpf_work->decimator - 1) * 32; + + dev_err(codec->dev, "%s(): decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, (unsigned int)hpf_cut_of_freq); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV, 0xFF, 0x51); + + snd_soc_update_bits_wrapper(codec, + tx_mux_ctl_reg, 0x30, hpf_cut_of_freq << 4); +} + + +#define TX_MUX_CTL_CUT_OFF_FREQ_MASK 0x30 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +static int msm8x16_wcd_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int value = 0, reg; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (w->shift == 0) + reg = MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL; + else if (w->shift == 1) + reg = MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL; + value = snd_soc_read_wrapper(codec, reg); + snd_soc_write_wrapper(codec, reg, value); + break; + default: + pr_err("%s: event = %d not expected\n", __func__, event); + } + return 0; +} + +static int msm8x16_wcd_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_asoc_mach_data *pdata = NULL; + unsigned int decimator; + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + char *dec_name = NULL; + char *widget_name = NULL; + char *temp; + int ret = 0, i; + u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg; + u8 dec_hpf_cut_of_freq; + int offset; + char *dec_num; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + dev_err(codec->dev, "%s %d\n", __func__, event); + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + temp = widget_name; + + dec_name = strsep(&widget_name, " "); + widget_name = temp; + if (!dec_name) { + dev_err(codec->dev, + "%s: Invalid decimator = %s\n", __func__, w->name); + ret = -EINVAL; + goto out; + } + + dec_num = strpbrk(dec_name, "1234"); + if (dec_num == NULL) { + dev_err(codec->dev, "%s: Invalid Decimator\n", __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec_num, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, + "%s: Invalid decimator = %s\n", __func__, dec_name); + ret = -EINVAL; + goto out; + } + + dev_err(codec->dev, + "%s(): widget = %s dec_name = %s decimator = %u\n", __func__, + w->name, dec_name, decimator); + + if (w->reg == MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL) { + dec_reset_reg = MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL; + offset = 0; + } else { + dev_err(codec->dev, "%s: Error, incorrect dec\n", __func__); + ret = -EINVAL; + goto out; + } + + tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG + + 32 * (decimator - 1); + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL + + 32 * (decimator - 1); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (decimator == 3 || decimator == 4) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_CLK_WSA_VI_B1_CTL, + 0xFF, 0x5); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_TX1_DMIC_CTL + + (decimator - 1) * 0x20, 0x7, 0x2); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_TX1_DMIC_CTL + + (decimator - 1) * 0x20, 0x7, 0x2); + } + /* Enableable TX digital mute */ + snd_soc_update_bits_wrapper(codec, tx_vol_ctl_reg, 0x01, 0x01); + for (i = 0; i < NUM_DECIMATORS; i++) { + if (decimator == i + 1) + msm8x16_wcd->dec_active[i] = true; + } + + dec_hpf_cut_of_freq = + snd_soc_read_wrapper(codec, tx_mux_ctl_reg); + + dec_hpf_cut_of_freq = (dec_hpf_cut_of_freq & 0x30) >> 4; + + tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq = + dec_hpf_cut_of_freq; + + if (dec_hpf_cut_of_freq != CF_MIN_3DB_150HZ) { + + /* set cut of freq to CF_MIN_3DB_150HZ (0x1); */ + snd_soc_update_bits_wrapper(codec, tx_mux_ctl_reg, 0x30, + CF_MIN_3DB_150HZ << 4); + } + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV, + 0xFF, 0x42); + + break; + case SND_SOC_DAPM_POST_PMU: + /* enable HPF */ + snd_soc_update_bits_wrapper(codec, tx_mux_ctl_reg, 0x08, 0x00); + + if (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq != + CF_MIN_3DB_150HZ) { + + schedule_delayed_work(&tx_hpf_work[decimator - 1].dwork, + msecs_to_jiffies(300)); + } + /* apply the digital gain after the decimator is enabled*/ + if ((w->shift) < ARRAY_SIZE(tx_digital_gain_reg)) + snd_soc_write_wrapper(codec, + tx_digital_gain_reg[w->shift + offset], + snd_soc_read_wrapper(codec, + tx_digital_gain_reg[w->shift + offset]) + ); + if (pdata->lb_mode) { + pr_err("%s: loopback mode unmute the DEC\n", + __func__); + snd_soc_update_bits_wrapper(codec, + tx_vol_ctl_reg, 0x01, 0x00); + } + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits_wrapper(codec, tx_vol_ctl_reg, 0x01, 0x01); + msleep(20); + snd_soc_update_bits_wrapper(codec, tx_mux_ctl_reg, 0x08, 0x08); + cancel_delayed_work_sync(&tx_hpf_work[decimator - 1].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits_wrapper(codec, + dec_reset_reg, 1 << w->shift, 1 << w->shift); + snd_soc_update_bits_wrapper(codec, + dec_reset_reg, 1 << w->shift, 0x0); + snd_soc_update_bits_wrapper(codec, + tx_mux_ctl_reg, 0x08, 0x08); + snd_soc_update_bits_wrapper(codec, + tx_mux_ctl_reg, 0x30, + (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq) << 4); + snd_soc_update_bits_wrapper(codec, + tx_vol_ctl_reg, 0x01, 0x00); + for (i = 0; i < NUM_DECIMATORS; i++) { + if (decimator == i + 1) + msm8x16_wcd->dec_active[i] = false; + } + if (decimator == 3 || decimator == 4) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_CLK_WSA_VI_B1_CTL, + 0xFF, 0x0); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_TX1_DMIC_CTL + + (decimator - 1) * 0x20, 0x7, 0x0); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_TX1_DMIC_CTL + + (decimator - 1) * 0x20, 0x7, 0x0); + } + break; + } +out: + kfree(widget_name); + return ret; +} + +static int msm89xx_wcd_codec_enable_vdd_spkr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (!msm8x16_wcd->ext_spk_boost_set) { + dev_err(codec->dev, "%s: ext_boost not supported/disabled\n", + __func__); + return 0; + } + dev_err(codec->dev, "%s: %s %d\n", __func__, w->name, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (msm8x16_wcd->spkdrv_reg) { + ret = regulator_enable(msm8x16_wcd->spkdrv_reg); + if (ret) + dev_err(codec->dev, + "%s Failed to enable spkdrv reg %s\n", + __func__, MSM89XX_VDD_SPKDRV_NAME); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (msm8x16_wcd->spkdrv_reg) { + ret = regulator_disable(msm8x16_wcd->spkdrv_reg); + if (ret) + dev_err(codec->dev, + "%s: Failed to disable spkdrv_reg %s\n", + __func__, MSM89XX_VDD_SPKDRV_NAME); + } + break; + } + return 0; +} + +static int msm8x16_wcd_codec_config_compander(struct snd_soc_codec *codec, + int interp_n, int event) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s: event %d shift %d, enabled %d\n", + __func__, event, interp_n, + msm8x16_wcd->comp_enabled[interp_n]); + + /* compander is not enabled */ + if (!msm8x16_wcd->comp_enabled[interp_n]) + return 0; + + switch (msm8x16_wcd->comp_enabled[interp_n]) { + case COMPANDER_1: + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x09); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x01); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL, + 1 << interp_n, 1 << interp_n); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x01); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0x50); + /* add sleep for compander to settle */ + usleep_range(1000, 1100); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x28); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0xB0); + + /* Enable Compander GPIO */ + if (msm8x16_wcd->codec_hph_comp_gpio) + msm8x16_wcd->codec_hph_comp_gpio(1); + } else if (SND_SOC_DAPM_EVENT_OFF(event)) { + /* Disable Compander GPIO */ + if (msm8x16_wcd->codec_hph_comp_gpio) + msm8x16_wcd->codec_hph_comp_gpio(0); + + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x05); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL, + 1 << interp_n, 0); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x00); + } + break; + default: + dev_err(codec->dev, "%s: Invalid compander %d\n", __func__, + msm8x16_wcd->comp_enabled[interp_n]); + break; + }; + + return 0; +} + +static int msm8x16_wcd_codec_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s %d %s\n", __func__, event, w->name); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msm8x16_wcd_codec_config_compander(codec, w->shift, event); + /* apply the digital gain after the interpolator is enabled*/ + if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg)) + snd_soc_write_wrapper(codec, + rx_digital_gain_reg[w->shift], + snd_soc_read_wrapper(codec, + rx_digital_gain_reg[w->shift]) + ); + break; + case SND_SOC_DAPM_POST_PMD: + msm8x16_wcd_codec_config_compander(codec, w->shift, event); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, + 1 << w->shift, 1 << w->shift); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, + 1 << w->shift, 0x0); + /* + * disable the mute enabled during the PMD of this device + */ + if ((w->shift == 0) && + (msm8x16_wcd->mute_mask & HPHL_PA_DISABLE)) { + pr_err("disabling HPHL mute\n"); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00); + if (get_codec_version(msm8x16_wcd) >= CAJON) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP, + 0xF0, 0x20); + msm8x16_wcd->mute_mask &= ~(HPHL_PA_DISABLE); + } else if ((w->shift == 1) && + (msm8x16_wcd->mute_mask & HPHR_PA_DISABLE)) { + pr_err("disabling HPHR mute\n"); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x00); + if (get_codec_version(msm8x16_wcd) >= CAJON) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP, + 0xF0, 0x20); + msm8x16_wcd->mute_mask &= ~(HPHR_PA_DISABLE); + } else if ((w->shift == 2) && + (msm8x16_wcd->mute_mask & SPKR_PA_DISABLE)) { + pr_err("disabling SPKR mute\n"); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00); + msm8x16_wcd->mute_mask &= ~(SPKR_PA_DISABLE); + } else if ((w->shift == 0) && + (msm8x16_wcd->mute_mask & EAR_PA_DISABLE)) { + pr_err("disabling EAR mute\n"); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00); + msm8x16_wcd->mute_mask &= ~(EAR_PA_DISABLE); + } + } + return 0; +} + + +/* The register address is the same as other codec so it can use resmgr */ +static int msm8x16_wcd_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm8x16_wcd->rx_bias_count++; + if (msm8x16_wcd->rx_bias_count == 1) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x80, 0x80); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x01, 0x01); + } + break; + case SND_SOC_DAPM_POST_PMD: + msm8x16_wcd->rx_bias_count--; + if (msm8x16_wcd->rx_bias_count == 0) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x01, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x80, 0x00); + } + break; + } + dev_err(codec->dev, "%s rx_bias_count = %d\n", + __func__, msm8x16_wcd->rx_bias_count); + return 0; +} + +static uint32_t wcd_get_impedance_value(uint32_t imped) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wcd_imped_val) - 1; i++) { + if (imped >= wcd_imped_val[i] && + imped < wcd_imped_val[i + 1]) + break; + } + + pr_err("%s: selected impedance value = %d\n", + __func__, wcd_imped_val[i]); + return wcd_imped_val[i]; +} + +void wcd_imped_config(struct snd_soc_codec *codec, + uint32_t imped, bool set_gain) +{ + uint32_t value; + int codec_version; + struct msm8x16_wcd_priv *msm8x16_wcd = + snd_soc_codec_get_drvdata(codec); + + value = wcd_get_impedance_value(imped); + + if (value < wcd_imped_val[0]) { + pr_err("%s, detected impedance is less than 4 Ohm\n", + __func__); + return; + } + if (value >= wcd_imped_val[ARRAY_SIZE(wcd_imped_val) - 1]) { + pr_err("%s, invalid imped, greater than 48 Ohm\n = %d\n", + __func__, value); + return; + } + + codec_version = get_codec_version(msm8x16_wcd); + + if (set_gain) { + switch (codec_version) { + case TOMBAK_1_0: + case TOMBAK_2_0: + case CONGA: + /* + * For 32Ohm load and higher loads, Set 0x19E + * bit 5 to 1 (POS_6_DB_DI). For loads lower + * than 32Ohm (such as 16Ohm load), Set 0x19E + * bit 5 to 0 (POS_1P5_DB_DI) + */ + if (value >= 32) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x20); + else + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x00); + break; + case CAJON: + case CAJON_2_0: + case DIANGU: + if (value >= 13) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x20); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_NCP_VCTRL, + 0x07, 0x07); + } else { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_NCP_VCTRL, + 0x07, 0x04); + } + break; + } + } else { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_NCP_VCTRL, + 0x07, 0x04); + } + + pr_err("%s: Exit\n", __func__); +} + +static int msm8x16_wcd_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + uint32_t impedl, impedr; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + int ret; + + dev_err(codec->dev, "%s %s %d\n", __func__, w->name, event); + ret = wcd_mbhc_get_impedance(&msm8x16_wcd->mbhc, + &impedl, &impedr); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (get_codec_version(msm8x16_wcd) > CAJON) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 0x08, 0x08); + if (get_codec_version(msm8x16_wcd) == CAJON || + get_codec_version(msm8x16_wcd) == CAJON_2_0) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, + 0x80, 0x80); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, + 0x80, 0x80); + } + if (get_codec_version(msm8x16_wcd) > CAJON) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 0x08, 0x00); + if (msm8x16_wcd->hph_mode == HD2_MODE) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x1C, 0x14); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0x10); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x80, 0x80); + } + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x02); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x01); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x02); + if (!ret) + wcd_imped_config(codec, impedl, true); + else + dev_err(codec->dev, "Failed to get mbhc impedance %d\n", + ret); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_imped_config(codec, impedl, false); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x00); + if (msm8x16_wcd->hph_mode == HD2_MODE) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x1C, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0xFF); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x80, 0x00); + } + break; + } + return 0; +} + +static int msm8x16_wcd_lo_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_err(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x20, 0x20); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x80, 0x80); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x08); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x40, 0x40); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x80, 0x80); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x40, 0x40); + break; + case SND_SOC_DAPM_POST_PMD: + usleep_range(20000, 20100); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x80, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x40, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x80, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x40, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x20, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x00); + break; + } + return 0; +} + +static int msm8x16_wcd_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (msm8x16_wcd->hph_mode == HD2_MODE) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x1C, 0x14); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0x10); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x80, 0x80); + } + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x02, 0x02); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x02, 0x02); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x01); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x02, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x02, 0x00); + if (msm8x16_wcd->hph_mode == HD2_MODE) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x1C, 0x00); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0xFF); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x80, 0x00); + } + break; + } + return 0; +} + +static int msm8x16_wcd_hph_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s: %s event = %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (w->shift == 5) + msm8x16_notifier_call(codec, + WCD_EVENT_PRE_HPHL_PA_ON); + else if (w->shift == 4) + msm8x16_notifier_call(codec, + WCD_EVENT_PRE_HPHR_PA_ON); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x20, 0x20); + break; + + case SND_SOC_DAPM_POST_PMU: + usleep_range(7000, 7100); + if (w->shift == 5) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x04); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00); + } else if (w->shift == 4) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x04); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x00); + } + break; + + case SND_SOC_DAPM_PRE_PMD: + if (w->shift == 5) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x01); + msleep(20); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x00); + msm8x16_wcd->mute_mask |= HPHL_PA_DISABLE; + msm8x16_notifier_call(codec, + WCD_EVENT_PRE_HPHL_PA_OFF); + } else if (w->shift == 4) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x01); + msleep(20); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x00); + msm8x16_wcd->mute_mask |= HPHR_PA_DISABLE; + msm8x16_notifier_call(codec, + WCD_EVENT_PRE_HPHR_PA_OFF); + } + if (get_codec_version(msm8x16_wcd) >= CAJON) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP, + 0xF0, 0x30); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (w->shift == 5) { + clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK, + &msm8x16_wcd->mbhc.hph_pa_dac_state); + msm8x16_notifier_call(codec, + WCD_EVENT_POST_HPHL_PA_OFF); + } else if (w->shift == 4) { + clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK, + &msm8x16_wcd->mbhc.hph_pa_dac_state); + msm8x16_notifier_call(codec, + WCD_EVENT_POST_HPHR_PA_OFF); + } + usleep_range(4000, 4100); + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + + dev_err(codec->dev, + "%s: sleep 10 ms after %s PA disable.\n", __func__, + w->name); + usleep_range(10000, 10100); + break; + } + return 0; +} + +static const struct snd_soc_dapm_route audio_map[] = { + {"RX_I2S_CLK", NULL, "CDC_CONN"}, + {"I2S RX1", NULL, "RX_I2S_CLK"}, + {"I2S RX2", NULL, "RX_I2S_CLK"}, + {"I2S RX3", NULL, "RX_I2S_CLK"}, + + {"I2S TX1", NULL, "TX_I2S_CLK"}, + {"I2S TX2", NULL, "TX_I2S_CLK"}, + {"AIF2 VI", NULL, "TX_I2S_CLK"}, + + {"I2S TX1", NULL, "DEC1 MUX"}, + {"I2S TX2", NULL, "DEC2 MUX"}, + {"AIF2 VI", NULL, "DEC3 MUX"}, + {"AIF2 VI", NULL, "DEC4 MUX"}, + + /* RDAC Connections */ + {"HPHR DAC", NULL, "RDAC2 MUX"}, + {"RDAC2 MUX", "RX1", "RX1 CHAIN"}, + {"RDAC2 MUX", "RX2", "RX2 CHAIN"}, + + /* WSA */ + {"WSA_SPK OUT", NULL, "WSA Spk Switch"}, + {"WSA Spk Switch", "WSA", "EAR PA"}, + + /* Earpiece (RX MIX1) */ + {"EAR", NULL, "EAR_S"}, + {"EAR_S", "Switch", "EAR PA"}, + {"EAR PA", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "HPHL DAC"}, + {"EAR PA", NULL, "HPHR DAC"}, + {"EAR PA", NULL, "EAR CP"}, + + /* Headset (RX MIX1 and RX MIX2) */ + {"HEADPHONE", NULL, "HPHL PA"}, + {"HEADPHONE", NULL, "HPHR PA"}, + + {"Ext Spk", NULL, "Ext Spk Switch"}, + {"Ext Spk Switch", "On", "HPHL PA"}, + {"Ext Spk Switch", "On", "HPHR PA"}, + + {"HPHL PA", NULL, "HPHL"}, + {"HPHR PA", NULL, "HPHR"}, + {"HPHL", "Switch", "HPHL DAC"}, + {"HPHR", "Switch", "HPHR DAC"}, + {"HPHL PA", NULL, "CP"}, + {"HPHL PA", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "CP"}, + {"HPHR PA", NULL, "RX_BIAS"}, + {"HPHL DAC", NULL, "RX1 CHAIN"}, + + {"SPK_OUT", NULL, "SPK PA"}, + {"SPK PA", NULL, "SPK_RX_BIAS"}, + {"SPK PA", NULL, "SPK"}, + {"SPK", "Switch", "SPK DAC"}, + {"SPK DAC", NULL, "RX3 CHAIN"}, + {"SPK DAC", NULL, "VDD_SPKDRV"}, + + /* lineout */ + {"LINEOUT", NULL, "LINEOUT PA"}, + {"LINEOUT PA", NULL, "SPK_RX_BIAS"}, + {"LINEOUT PA", NULL, "LINE_OUT"}, + {"LINE_OUT", "Switch", "LINEOUT DAC"}, + {"LINEOUT DAC", NULL, "RX3 CHAIN"}, + + /* lineout to WSA */ + {"WSA_SPK OUT", NULL, "LINEOUT PA"}, + + {"RX1 CHAIN", NULL, "RX1 CLK"}, + {"RX2 CHAIN", NULL, "RX2 CLK"}, + {"RX3 CHAIN", NULL, "RX3 CLK"}, + {"RX1 CHAIN", NULL, "RX1 MIX2"}, + {"RX2 CHAIN", NULL, "RX2 MIX2"}, + {"RX3 CHAIN", NULL, "RX3 MIX1"}, + + {"RX1 MIX1", NULL, "RX1 MIX1 INP1"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP2"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP3"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP1"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP2"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP1"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP2"}, + {"RX1 MIX2", NULL, "RX1 MIX1"}, + {"RX1 MIX2", NULL, "RX1 MIX2 INP1"}, + {"RX2 MIX2", NULL, "RX2 MIX1"}, + {"RX2 MIX2", NULL, "RX2 MIX2 INP1"}, + + {"RX1 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX1 MIX1 INP1", "IIR2", "IIR2"}, + {"RX1 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX1 MIX1 INP2", "IIR2", "IIR2"}, + {"RX1 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP3", "RX3", "I2S RX3"}, + + {"RX2 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP1", "IIR1", "IIR1"}, + {"RX2 MIX1 INP1", "IIR2", "IIR2"}, + {"RX2 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP2", "IIR1", "IIR1"}, + {"RX2 MIX1 INP2", "IIR2", "IIR2"}, + + {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP1", "IIR1", "IIR1"}, + {"RX3 MIX1 INP1", "IIR2", "IIR2"}, + {"RX3 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP2", "IIR1", "IIR1"}, + {"RX3 MIX1 INP2", "IIR2", "IIR2"}, + + {"RX1 MIX2 INP1", "IIR1", "IIR1"}, + {"RX2 MIX2 INP1", "IIR1", "IIR1"}, + {"RX1 MIX2 INP1", "IIR2", "IIR2"}, + {"RX2 MIX2 INP1", "IIR2", "IIR2"}, + + /* Decimator Inputs */ + {"DEC1 MUX", "DMIC1", "DMIC1"}, + {"DEC1 MUX", "DMIC2", "DMIC2"}, + {"DEC1 MUX", "ADC1", "ADC1"}, + {"DEC1 MUX", "ADC2", "ADC2"}, + {"DEC1 MUX", "ADC3", "ADC3"}, + {"DEC1 MUX", NULL, "CDC_CONN"}, + + {"DEC2 MUX", "DMIC1", "DMIC1"}, + {"DEC2 MUX", "DMIC2", "DMIC2"}, + {"DEC2 MUX", "ADC1", "ADC1"}, + {"DEC2 MUX", "ADC2", "ADC2"}, + {"DEC2 MUX", "ADC3", "ADC3"}, + {"DEC2 MUX", NULL, "CDC_CONN"}, + + {"DEC3 MUX", "DMIC3", "DMIC3"}, + {"DEC4 MUX", "DMIC4", "DMIC4"}, + {"DEC3 MUX", NULL, "CDC_CONN"}, + {"DEC4 MUX", NULL, "CDC_CONN"}, + /* ADC Connections */ + {"ADC2", NULL, "ADC2 MUX"}, + {"ADC3", NULL, "ADC2 MUX"}, + {"ADC2 MUX", "INP2", "ADC2_INP2"}, + {"ADC2 MUX", "INP3", "ADC2_INP3"}, + + {"ADC1", NULL, "AMIC1"}, + {"ADC2_INP2", NULL, "AMIC2"}, + {"ADC2_INP3", NULL, "AMIC3"}, + + /* TODO: Fix this */ + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"}, + {"IIR2", NULL, "IIR2 INP1 MUX"}, + {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"}, + {"MIC BIAS Internal1", NULL, "INT_LDO_H"}, + {"MIC BIAS Internal2", NULL, "INT_LDO_H"}, + {"MIC BIAS External", NULL, "INT_LDO_H"}, + {"MIC BIAS External2", NULL, "INT_LDO_H"}, + {"MIC BIAS Internal1", NULL, "MICBIAS_REGULATOR"}, + {"MIC BIAS Internal2", NULL, "MICBIAS_REGULATOR"}, + {"MIC BIAS External", NULL, "MICBIAS_REGULATOR"}, + {"MIC BIAS External2", NULL, "MICBIAS_REGULATOR"}, +}; + +static int msm8x16_wcd_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = + snd_soc_codec_get_drvdata(dai->codec); + + dev_err(dai->codec->dev, "%s(): substream = %s stream = %d\n", + __func__, + substream->name, substream->stream); + /* + * If status_mask is BU_DOWN it means SSR is not complete. + * So retun error. + */ + if (test_bit(BUS_DOWN, &msm8x16_wcd->status_mask)) { + dev_err(dai->codec->dev, "Error, Device is not up post SSR\n"); + return -EINVAL; + } + return 0; +} + +static void msm8x16_wcd_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + dev_err(dai->codec->dev, + "%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); +} + +int msm8x16_wcd_mclk_enable(struct snd_soc_codec *codec, + int mclk_enable, bool dapm) +{ + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s: mclk_enable = %u, dapm = %d\n", + __func__, mclk_enable, dapm); + if (mclk_enable) { + msm8x16_wcd->int_mclk0_enabled = true; + msm8x16_wcd_codec_enable_clock_block(codec, 1); + } else { + if (!msm8x16_wcd->int_mclk0_enabled) { + dev_err(codec->dev, "Error, MCLK already diabled\n"); + return -EINVAL; + } + msm8x16_wcd->int_mclk0_enabled = false; + msm8x16_wcd_codec_enable_clock_block(codec, 0); + } + return 0; +} + +static int msm8x16_wcd_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + dev_err(dai->codec->dev, "%s\n", __func__); + return 0; +} + +static int msm8x16_wcd_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + dev_err(dai->codec->dev, "%s\n", __func__); + return 0; +} + +static int msm8x16_wcd_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) + +{ + dev_err(dai->codec->dev, "%s\n", __func__); + return 0; +} + +static int msm8x16_wcd_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) + +{ + dev_err(dai->codec->dev, "%s\n", __func__); + return 0; +} + +static int msm8x16_wcd_set_interpolator_rate(struct snd_soc_dai *dai, + u8 rx_fs_rate_reg_val, u32 sample_rate) +{ + snd_soc_update_bits_wrapper(dai->codec, + MSM89XX_CDC_CORE_RX1_B5_CTL, 0xF0, rx_fs_rate_reg_val); + snd_soc_update_bits_wrapper(dai->codec, + MSM89XX_CDC_CORE_RX2_B5_CTL, 0xF0, rx_fs_rate_reg_val); + return 0; +} + +static int msm8x16_wcd_set_decimator_rate(struct snd_soc_dai *dai, + u8 tx_fs_rate_reg_val, u32 sample_rate) +{ + return 0; +} + +static int msm8x16_wcd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u8 tx_fs_rate, rx_fs_rate, rx_clk_fs_rate; + int ret; + + dev_err(dai->codec->dev, + "%s: dai_name = %s DAI-ID %x rate %d num_ch %d format %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params), params_format(params)); + + switch (params_rate(params)) { + case 8000: + tx_fs_rate = 0x00; + rx_fs_rate = 0x00; + rx_clk_fs_rate = 0x00; + break; + case 16000: + tx_fs_rate = 0x20; + rx_fs_rate = 0x20; + rx_clk_fs_rate = 0x01; + break; + case 32000: + tx_fs_rate = 0x40; + rx_fs_rate = 0x40; + rx_clk_fs_rate = 0x02; + break; + case 48000: + tx_fs_rate = 0x60; + rx_fs_rate = 0x60; + rx_clk_fs_rate = 0x03; + break; + case 96000: + tx_fs_rate = 0x80; + rx_fs_rate = 0x80; + rx_clk_fs_rate = 0x04; + break; + case 192000: + tx_fs_rate = 0xA0; + rx_fs_rate = 0xA0; + rx_clk_fs_rate = 0x05; + break; + default: + dev_err(dai->codec->dev, + "%s: Invalid sampling rate %d\n", __func__, + params_rate(params)); + return -EINVAL; + } + + snd_soc_update_bits_wrapper(dai->codec, + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x0F, rx_clk_fs_rate); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + ret = msm8x16_wcd_set_decimator_rate(dai, tx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(dai->codec->dev, + "%s: set decimator rate failed %d\n", __func__, + ret); + return ret; + } + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = msm8x16_wcd_set_interpolator_rate(dai, rx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(dai->codec->dev, + "%s: set decimator rate failed %d\n", __func__, + ret); + return ret; + } + break; + default: + dev_err(dai->codec->dev, + "%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + } + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits_wrapper(dai->codec, + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x20, 0x20); + break; + case SNDRV_PCM_FORMAT_S24_LE: + snd_soc_update_bits_wrapper(dai->codec, + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x20, 0x00); + break; + default: + dev_err(dai->codec->dev, "%s: wrong format selected\n", + __func__); + return -EINVAL; + } + + return 0; +} + +int msm8x16_wcd_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = NULL; + u16 tx_vol_ctl_reg = 0; + u8 decimator = 0, i; + struct msm8x16_wcd_priv *msm8x16_wcd; + + pr_err("%s: Digital Mute val = %d\n", __func__, mute); + + if (!dai || !dai->codec) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + codec = dai->codec; + msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + if ((dai->id != AIF1_CAP) && (dai->id != AIF2_VIFEED)) { + dev_err(codec->dev, "%s: Not capture use case skip\n", + __func__); + return 0; + } + + mute = (mute) ? 1 : 0; + if (!mute) { + /* + * 15 ms is an emperical value for the mute time + * that was arrived by checking the pop level + * to be inaudible + */ + usleep_range(15000, 15010); + } + + for (i = 0; i < NUM_DECIMATORS; i++) { + if (msm8x16_wcd->dec_active[i]) + decimator = i + 1; + if (decimator && decimator <= NUM_DECIMATORS) { + pr_err("%s: Mute = %d Decimator = %d", __func__, + mute, decimator); + tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG + + 32 * (decimator - 1); + snd_soc_update_bits_wrapper(codec, + tx_vol_ctl_reg, 0x01, mute); + } + decimator = 0; + } + return 0; +} + +static struct snd_soc_dai_ops msm8x16_wcd_dai_ops = { + .startup = msm8x16_wcd_startup, + .shutdown = msm8x16_wcd_shutdown, + .hw_params = msm8x16_wcd_hw_params, + .set_sysclk = msm8x16_wcd_set_dai_sysclk, + .set_fmt = msm8x16_wcd_set_dai_fmt, + .set_channel_map = msm8x16_wcd_set_channel_map, + .get_channel_map = msm8x16_wcd_get_channel_map, + .digital_mute = msm8x16_wcd_digital_mute, +}; + +static struct snd_soc_dai_driver msm8x16_wcd_i2s_dai[] = { + { + .name = "msm8x16_wcd_i2s_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = MSM89XX_RATES, + .formats = MSM89XX_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 3, + }, + .ops = &msm8x16_wcd_dai_ops, + }, + { + .name = "msm8x16_wcd_i2s_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = MSM89XX_RATES, + .formats = MSM89XX_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &msm8x16_wcd_dai_ops, + }, + { + .name = "cajon_vifeedback", + .id = AIF2_VIFEED, + .capture = { + .stream_name = "VIfeed", + .rates = MSM89XX_RATES, + .formats = MSM89XX_FORMATS, + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &msm8x16_wcd_dai_ops, + }, +}; + +static int msm8x16_wcd_codec_enable_rx_chain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dev_err(codec->dev, + "%s: PMU:Sleeping 20ms after disabling mute\n", + __func__); + break; + case SND_SOC_DAPM_POST_PMD: + dev_err(codec->dev, + "%s: PMD:Sleeping 20ms after disabling mute\n", + __func__); + snd_soc_update_bits_wrapper(codec, w->reg, + 1 << w->shift, 0x00); + msleep(20); + break; + } + return 0; +} + +static int msm8x16_wcd_codec_enable_lo_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_err(codec->dev, "%s: %d %s\n", __func__, event, w->name); + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00); + break; + } + + return 0; +} + +static int msm8x16_wcd_codec_enable_spk_ext_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + dev_err(codec->dev, "%s: %s event = %d\n", __func__, w->name, event); + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dev_err(codec->dev, + "%s: enable external speaker PA\n", __func__); + if (msm8x16_wcd->codec_spk_ext_pa_cb) + msm8x16_wcd->codec_spk_ext_pa_cb(codec, 1); + break; + case SND_SOC_DAPM_PRE_PMD: + dev_err(codec->dev, + "%s: enable external speaker PA\n", __func__); + if (msm8x16_wcd->codec_spk_ext_pa_cb) + msm8x16_wcd->codec_spk_ext_pa_cb(codec, 0); + break; + } + return 0; +} + +static int msm8x16_wcd_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_err(codec->dev, + "%s: Sleeping 20ms after select EAR PA\n", + __func__); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x80, 0x80); + if (get_codec_version(msm8x16_wcd) < CONGA) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFF, 0x2A); + break; + case SND_SOC_DAPM_POST_PMU: + dev_err(codec->dev, + "%s: Sleeping 20ms after enabling EAR PA\n", + __func__); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x40, 0x40); + usleep_range(7000, 7100); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x01); + msleep(20); + msm8x16_wcd->mute_mask |= EAR_PA_DISABLE; + if (msm8x16_wcd->boost_option == BOOST_ALWAYS) { + dev_err(codec->dev, + "%s: boost_option:%d, tear down ear\n", + __func__, msm8x16_wcd->boost_option); + msm8x16_wcd_boost_mode_sequence(codec, EAR_PMD); + } + break; + case SND_SOC_DAPM_POST_PMD: + dev_err(codec->dev, + "%s: Sleeping 7ms after disabling EAR PA\n", + __func__); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x40, 0x00); + usleep_range(7000, 7100); + if (get_codec_version(msm8x16_wcd) < CONGA) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFF, 0x16); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget msm8x16_wcd_dapm_widgets[] = { + /*RX stuff */ + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("WSA_SPK OUT"), + + SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM, + 0, 0, NULL, 0, msm8x16_wcd_codec_enable_ear_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, + ear_pa_mux), + + SND_SOC_DAPM_MUX("WSA Spk Switch", SND_SOC_NOPM, 0, 0, + wsa_spk_mux), + + SND_SOC_DAPM_AIF_IN("I2S RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("I2S RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("I2S RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0), + + SND_SOC_DAPM_SPK("Ext Spk", msm8x16_wcd_codec_enable_spk_ext_pa), + + SND_SOC_DAPM_OUTPUT("HEADPHONE"), + SND_SOC_DAPM_PGA_E("HPHL PA", MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 5, 0, NULL, 0, + msm8x16_wcd_hph_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, + hphl_mux), + + SND_SOC_DAPM_MIXER_E("HPHL DAC", + MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, + 0, msm8x16_wcd_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("HPHR PA", MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 4, 0, NULL, 0, + msm8x16_wcd_hph_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, + hphr_mux), + + SND_SOC_DAPM_MIXER_E("HPHR DAC", + MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, + 0, msm8x16_wcd_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("SPK", SND_SOC_NOPM, 0, 0, + spkr_mux), + + SND_SOC_DAPM_DAC("SPK DAC", NULL, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 7, 0), + + SND_SOC_DAPM_MUX("LINE_OUT", + SND_SOC_NOPM, 0, 0, lo_mux), + + SND_SOC_DAPM_DAC_E("LINEOUT DAC", NULL, + SND_SOC_NOPM, 0, 0, msm8x16_wcd_lo_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* Speaker */ + SND_SOC_DAPM_OUTPUT("SPK_OUT"), + + /* Lineout */ + SND_SOC_DAPM_OUTPUT("LINEOUT"), + + SND_SOC_DAPM_PGA_E("SPK PA", MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 6, 0, NULL, 0, msm8x16_wcd_codec_enable_spk_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("LINEOUT PA", MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, + 5, 0, NULL, 0, msm8x16_wcd_codec_enable_lo_pa, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("VDD_SPKDRV", SND_SOC_NOPM, 0, 0, + msm89xx_wcd_codec_enable_vdd_spkr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("Ext Spk Switch", SND_SOC_NOPM, 0, 0, + &ext_spk_mux), + + SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("RX1 MIX2", + MSM89XX_CDC_CORE_CLK_RX_B1_CTL, MSM89XX_RX1, 0, NULL, + 0, msm8x16_wcd_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX2 MIX2", + MSM89XX_CDC_CORE_CLK_RX_B1_CTL, MSM89XX_RX2, 0, NULL, + 0, msm8x16_wcd_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX3 MIX1", + MSM89XX_CDC_CORE_CLK_RX_B1_CTL, MSM89XX_RX3, 0, NULL, + 0, msm8x16_wcd_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("RX1 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RX2 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RX3 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 2, 0, msm8x16_wcd_codec_enable_dig_clk, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX1 CHAIN", MSM89XX_CDC_CORE_RX1_B6_CTL, 0, 0, + NULL, 0, + msm8x16_wcd_codec_enable_rx_chain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX2 CHAIN", MSM89XX_CDC_CORE_RX2_B6_CTL, 0, 0, + NULL, 0, + msm8x16_wcd_codec_enable_rx_chain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX3 CHAIN", MSM89XX_CDC_CORE_RX3_B6_CTL, 0, 0, + NULL, 0, + msm8x16_wcd_codec_enable_rx_chain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp3_mux), + + SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp3_mux), + + SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp3_mux), + + SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx1_mix2_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix2_inp1_mux), + + SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM, + ON_DEMAND_MICBIAS, 0, + msm8x16_wcd_codec_enable_on_demand_supply, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("CP", MSM89XX_PMIC_ANALOG_NCP_EN, 0, 0, + msm8x16_wcd_codec_enable_charge_pump, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("EAR CP", MSM89XX_PMIC_ANALOG_NCP_EN, 4, 0, + msm8x16_wcd_codec_enable_charge_pump, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("RX_BIAS", 1, SND_SOC_NOPM, + 0, 0, msm8x16_wcd_codec_enable_rx_bias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("SPK_RX_BIAS", 1, SND_SOC_NOPM, 0, 0, + msm8x16_wcd_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* TX */ + + SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, MSM89XX_CDC_CORE_CLK_OTHR_CTL, + 2, 0, NULL, 0), + + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal1", + MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0, + msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal2", + MSM89XX_PMIC_ANALOG_MICB_2_EN, 7, 0, + msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal3", + MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0, + msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC1", NULL, MSM89XX_PMIC_ANALOG_TX_1_EN, 7, 0, + msm8x16_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP2", + NULL, MSM89XX_PMIC_ANALOG_TX_2_EN, 7, 0, + msm8x16_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP3", + NULL, MSM89XX_PMIC_ANALOG_TX_3_EN, 7, 0, + msm8x16_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, + &tx_adc2_mux), + + SND_SOC_DAPM_MICBIAS_E("MIC BIAS External", + MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0, + msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS_E("MIC BIAS External2", + MSM89XX_PMIC_ANALOG_MICB_2_EN, 7, 0, + msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + + SND_SOC_DAPM_INPUT("AMIC3"), + + SND_SOC_DAPM_MUX_E("DEC1 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 0, 0, + &dec1_mux, msm8x16_wcd_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("DEC2 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 1, 0, + &dec2_mux, msm8x16_wcd_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("DEC3 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 2, 0, + &dec3_mux, msm8x16_wcd_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("DEC4 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 3, 0, + &dec4_mux, msm8x16_wcd_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("RDAC2 MUX", SND_SOC_NOPM, 0, 0, &rdac2_mux), + + SND_SOC_DAPM_INPUT("AMIC2"), + + SND_SOC_DAPM_AIF_OUT("I2S TX1", "AIF1 Capture", 0, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX2", "AIF1 Capture", 0, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX3", "AIF1 Capture", 0, SND_SOC_NOPM, + 0, 0), + + SND_SOC_DAPM_AIF_OUT("AIF2 VI", "VIfeed", 0, SND_SOC_NOPM, + 0, 0), + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + msm8x16_wcd_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, + msm8x16_wcd_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("DMIC3"), + + SND_SOC_DAPM_INPUT("DMIC4"), + + /* Sidetone */ + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), + SND_SOC_DAPM_PGA_E("IIR1", MSM89XX_CDC_CORE_CLK_SD_CTL, 0, 0, NULL, 0, + msm8x16_wcd_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux), + SND_SOC_DAPM_PGA_E("IIR2", MSM89XX_CDC_CORE_CLK_SD_CTL, 1, 0, NULL, 0, + msm8x16_wcd_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", + MSM89XX_CDC_CORE_CLK_TX_I2S_CTL, 4, 0, + NULL, 0), +}; + +static const struct msm8x16_wcd_reg_mask_val msm8x16_wcd_reg_defaults[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), +}; + +static const struct msm8x16_wcd_reg_mask_val msm8x16_wcd_reg_defaults_2_0[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x28), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, 0x5F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, 0x88), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80), +}; + +static const struct msm8x16_wcd_reg_mask_val msm8909_wcd_reg_defaults[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x28), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE, 0x0A), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80), +}; + +static const struct msm8x16_wcd_reg_mask_val cajon_wcd_reg_defaults[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0xA8), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0xA4), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x41), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0xFA), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80), +}; + +static const struct msm8x16_wcd_reg_mask_val cajon2p0_wcd_reg_defaults[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0xA2), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0xA8), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0xA4), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x41), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_EAR_STATUS, 0x10), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_BYPASS_MODE, 0x18), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0xFA), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80), +}; + +static void msm8x16_wcd_update_reg_defaults(struct snd_soc_codec *codec) +{ + u32 i, version; + struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + + version = get_codec_version(msm8x16_wcd); + if (version == TOMBAK_1_0) { + for (i = 0; i < ARRAY_SIZE(msm8x16_wcd_reg_defaults); i++) + snd_soc_write_wrapper(codec, + msm8x16_wcd_reg_defaults[i].reg, + msm8x16_wcd_reg_defaults[i].val); + } else if (version == TOMBAK_2_0) { + for (i = 0; i < ARRAY_SIZE(msm8x16_wcd_reg_defaults_2_0); i++) + snd_soc_write_wrapper(codec, + msm8x16_wcd_reg_defaults_2_0[i].reg, + msm8x16_wcd_reg_defaults_2_0[i].val); + } else if (version == CONGA) { + for (i = 0; i < ARRAY_SIZE(msm8909_wcd_reg_defaults); i++) + snd_soc_write_wrapper(codec, + msm8909_wcd_reg_defaults[i].reg, + msm8909_wcd_reg_defaults[i].val); + } else if (version == CAJON) { + for (i = 0; i < ARRAY_SIZE(cajon_wcd_reg_defaults); i++) + snd_soc_write_wrapper(codec, + cajon_wcd_reg_defaults[i].reg, + cajon_wcd_reg_defaults[i].val); + } else if (version == CAJON_2_0 || version == DIANGU) { + for (i = 0; i < ARRAY_SIZE(cajon2p0_wcd_reg_defaults); i++) + snd_soc_write_wrapper(codec, + cajon2p0_wcd_reg_defaults[i].reg, + cajon2p0_wcd_reg_defaults[i].val); + } +} + +static const struct msm8x16_wcd_reg_mask_val + msm8x16_wcd_codec_reg_init_val[] = { + + /* Initialize current threshold to 350MA + * number of wait and run cycles to 4096 + */ + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0xFF, 0x12}, + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0xFF, 0xFF}, +}; + +static void msm8x16_wcd_codec_init_reg(struct snd_soc_codec *codec) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(msm8x16_wcd_codec_reg_init_val); i++) + snd_soc_update_bits_wrapper(codec, + msm8x16_wcd_codec_reg_init_val[i].reg, + msm8x16_wcd_codec_reg_init_val[i].mask, + msm8x16_wcd_codec_reg_init_val[i].val); +} + +static int msm8x16_wcd_bringup(struct snd_soc_codec *codec) +{ + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_DIGITAL_SEC_ACCESS, + 0xA5); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x01); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x01); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_DIGITAL_SEC_ACCESS, + 0xA5); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x00); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00); + return 0; +} + +static struct regulator *wcd8x16_wcd_codec_find_regulator( + const struct msm8x16_wcd *msm8x16, + const char *name) +{ + int i; + + for (i = 0; i < msm8x16->num_of_supplies; i++) { + if (msm8x16->supplies[i].supply && + !strcmp(msm8x16->supplies[i].supply, name)) + return msm8x16->supplies[i].consumer; + } + + dev_err(msm8x16->dev, "Error: regulator not found:%s\n" + , name); + return NULL; +} + +static int msm8x16_wcd_device_down(struct snd_soc_codec *codec) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct msm8x16_wcd_priv *msm8x16_wcd_priv = + snd_soc_codec_get_drvdata(codec); + int i; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + dev_err(codec->dev, "%s: device down!\n", __func__); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_TX_1_EN, 0x3); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_TX_2_EN, 0x3); + if (msm8x16_wcd_priv->boost_option == BOOST_ON_FOREVER) { + if ((snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL) + & 0x80) == 0) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x01); + snd_soc_update_bits_wrapper(codec, + MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x03); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80, 0x80); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, + 0x0C, 0x0C); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x84, 0x84); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, + 0x10, 0x10); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, + 0x1F, 0x1F); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x90, 0x90); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0xFF, 0xFF); + usleep_range(20, 21); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, + 0xFF, 0xFF); + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xE9, 0xE9); + } + } + msm8x16_wcd_boost_off(codec); + msm8x16_wcd_priv->hph_mode = NORMAL_MODE; + for (i = 0; i < MSM89XX_RX_MAX; i++) + msm8x16_wcd_priv->comp_enabled[i] = COMPANDER_NONE; + + /* 40ms to allow boost to discharge */ + msleep(40); + /* Disable PA to avoid pop during codec bring up */ + snd_soc_update_bits_wrapper(codec, MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 0x30, 0x00); + snd_soc_update_bits_wrapper(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0x80, 0x00); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x20); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x20); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x12); + snd_soc_write_wrapper(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x93); + + msm8x16_wcd_bringup(codec); + atomic_set(&pdata->int_mclk0_enabled, false); + set_bit(BUS_DOWN, &msm8x16_wcd_priv->status_mask); + snd_soc_card_change_online_state(codec->component.card, 0); + return 0; +} + +static int msm8x16_wcd_device_up(struct snd_soc_codec *codec) +{ + struct msm8x16_wcd_priv *msm8x16_wcd_priv = + snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_err(codec->dev, "%s: device up!\n", __func__); + + clear_bit(BUS_DOWN, &msm8x16_wcd_priv->status_mask); + + snd_soc_card_change_online_state(codec->component.card, 1); + /* delay is required to make sure sound card state updated */ + usleep_range(5000, 5100); + + msm8x16_wcd_codec_init_reg(codec); + msm8x16_wcd_update_reg_defaults(codec); + + snd_soc_write_wrapper(codec, MSM89XX_PMIC_DIGITAL_INT_EN_SET, + MSM89XX_PMIC_DIGITAL_INT_EN_SET__POR); + snd_soc_write_wrapper(codec, MSM89XX_PMIC_DIGITAL_INT_EN_CLR, + MSM89XX_PMIC_DIGITAL_INT_EN_CLR__POR); + + msm8x16_wcd_set_boost_v(codec); + + msm8x16_wcd_set_micb_v(codec); + if (msm8x16_wcd_priv->boost_option == BOOST_ON_FOREVER) + msm8x16_wcd_boost_on(codec); + else if (msm8x16_wcd_priv->boost_option == BYPASS_ALWAYS) + msm8x16_wcd_bypass_on(codec); + + msm8x16_wcd_configure_cap(codec, false, false); + wcd_mbhc_stop(&msm8x16_wcd_priv->mbhc); + wcd_mbhc_deinit(&msm8x16_wcd_priv->mbhc); + ret = wcd_mbhc_init(&msm8x16_wcd_priv->mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, true); + if (ret) + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + else + wcd_mbhc_start(&msm8x16_wcd_priv->mbhc, + msm8x16_wcd_priv->mbhc.mbhc_cfg); + + + return 0; +} + +static int adsp_state_callback(struct notifier_block *nb, unsigned long value, + void *priv) +{ + bool timedout; + unsigned long timeout; + + if (value == SUBSYS_BEFORE_SHUTDOWN) + msm8x16_wcd_device_down(registered_codec); + else if (value == SUBSYS_AFTER_POWERUP) { + + dev_err(registered_codec->dev, + "ADSP is about to power up. bring up codec\n"); + + if (!q6core_is_adsp_ready()) { + dev_err(registered_codec->dev, + "ADSP isn't ready\n"); + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + while (!(timedout = time_after(jiffies, timeout))) { + if (!q6core_is_adsp_ready()) { + dev_err(registered_codec->dev, + "ADSP isn't ready\n"); + } else { + dev_err(registered_codec->dev, + "ADSP is ready\n"); + break; + } + } + } else { + dev_err(registered_codec->dev, + "%s: DSP is ready\n", __func__); + } + + msm8x16_wcd_device_up(registered_codec); + } + return NOTIFY_OK; +} + +static struct notifier_block adsp_state_notifier_block = { + .notifier_call = adsp_state_callback, + .priority = -INT_MAX, +}; + +int msm8x16_wcd_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + struct msm8x16_wcd_priv *msm8x16_wcd_priv = + snd_soc_codec_get_drvdata(codec); + + return wcd_mbhc_start(&msm8x16_wcd_priv->mbhc, mbhc_cfg); +} +EXPORT_SYMBOL(msm8x16_wcd_hs_detect); + +void msm8x16_wcd_hs_detect_exit(struct snd_soc_codec *codec) +{ + struct msm8x16_wcd_priv *msm8x16_wcd_priv = + snd_soc_codec_get_drvdata(codec); + + wcd_mbhc_stop(&msm8x16_wcd_priv->mbhc); +} +EXPORT_SYMBOL(msm8x16_wcd_hs_detect_exit); + +void msm8x16_update_int_spk_boost(bool enable) +{ + pr_err("%s: enable = %d\n", __func__, enable); + spkr_boost_en = enable; +} +EXPORT_SYMBOL(msm8x16_update_int_spk_boost); + +static void msm8x16_wcd_set_micb_v(struct snd_soc_codec *codec) +{ + + struct msm8x16_wcd *msm8x16 = codec->control_data; + struct msm8x16_wcd_pdata *pdata = msm8x16->dev->platform_data; + u8 reg_val; + + reg_val = VOLTAGE_CONVERTER(pdata->micbias.cfilt1_mv, MICBIAS_MIN_VAL, + MICBIAS_STEP_SIZE); + dev_err(codec->dev, "cfilt1_mv %d reg_val %x\n", + (u32)pdata->micbias.cfilt1_mv, reg_val); + snd_soc_update_bits_wrapper(codec, MSM89XX_PMIC_ANALOG_MICB_1_VAL, + 0xF8, (reg_val << 3)); +} + +static void msm8x16_wcd_set_boost_v(struct snd_soc_codec *codec) +{ + struct msm8x16_wcd_priv *msm8x16_wcd_priv = + snd_soc_codec_get_drvdata(codec); + + snd_soc_update_bits_wrapper(codec, MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE, + 0x1F, msm8x16_wcd_priv->boost_voltage); +} + +static void msm8x16_wcd_configure_cap(struct snd_soc_codec *codec, + bool micbias1, bool micbias2) +{ + + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + + pr_err("\n %s: micbias1 %x micbias2 = %d\n", __func__, micbias1, + micbias2); + if (micbias1 && micbias2) { + if ((pdata->micbias1_cap_mode + == MICBIAS_EXT_BYP_CAP) || + (pdata->micbias2_cap_mode + == MICBIAS_EXT_BYP_CAP)) + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, (MICBIAS_EXT_BYP_CAP << 6)); + else + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, (MICBIAS_NO_EXT_BYP_CAP << 6)); + } else if (micbias2) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, (pdata->micbias2_cap_mode << 6)); + } else if (micbias1) { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, (pdata->micbias1_cap_mode << 6)); + } else { + snd_soc_update_bits_wrapper(codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, 0x00); + } +} + +static int msm89xx_digcodec_probe(struct snd_soc_codec *codec) +{ + registered_digcodec = codec; + + return 0; +} + + +static int msm89xx_digcodec_remove(struct snd_soc_codec *codec) +{ + return 0; +} + +static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec) +{ + struct msm8x16_wcd_priv *msm8x16_wcd_priv; + int i, ret; + + dev_err(codec->dev, "%s()\n", __func__); + + msm8x16_wcd_priv = kzalloc(sizeof(struct msm8x16_wcd_priv), GFP_KERNEL); + if (!msm8x16_wcd_priv) + return -ENOMEM; + + for (i = 0; i < NUM_DECIMATORS; i++) { + tx_hpf_work[i].msm8x16_wcd = msm8x16_wcd_priv; + tx_hpf_work[i].decimator = i + 1; + INIT_DELAYED_WORK(&tx_hpf_work[i].dwork, + tx_hpf_corner_freq_callback); + } + + codec->control_data = dev_get_drvdata(codec->dev); + snd_soc_codec_set_drvdata(codec, msm8x16_wcd_priv); + msm8x16_wcd_priv->codec = codec; + + msm8x16_wcd_priv->spkdrv_reg = + wcd8x16_wcd_codec_find_regulator(codec->control_data, + MSM89XX_VDD_SPKDRV_NAME); + msm8x16_wcd_priv->pmic_rev = snd_soc_read_wrapper(codec, + MSM89XX_PMIC_DIGITAL_REVISION1); + msm8x16_wcd_priv->codec_version = snd_soc_read_wrapper(codec, + MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE); + if (msm8x16_wcd_priv->codec_version == CONGA) { + dev_err(codec->dev, "%s :Conga REV: %d\n", __func__, + msm8x16_wcd_priv->codec_version); + msm8x16_wcd_priv->ext_spk_boost_set = true; + } else { + dev_err(codec->dev, "%s :PMIC REV: %d\n", __func__, + msm8x16_wcd_priv->pmic_rev); + if (msm8x16_wcd_priv->pmic_rev == TOMBAK_1_0 && + msm8x16_wcd_priv->codec_version == CAJON_2_0) { + msm8x16_wcd_priv->codec_version = DIANGU; + dev_err(codec->dev, "%s : Diangu detected\n", + __func__); + } else if (msm8x16_wcd_priv->pmic_rev == TOMBAK_1_0 && + (snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_NCP_FBCTRL) + & 0x80)) { + msm8x16_wcd_priv->codec_version = CAJON; + dev_err(codec->dev, "%s : Cajon detected\n", __func__); + } else if (msm8x16_wcd_priv->pmic_rev == TOMBAK_2_0 && + (snd_soc_read_wrapper(codec, + MSM89XX_PMIC_ANALOG_NCP_FBCTRL) + & 0x80)) { + msm8x16_wcd_priv->codec_version = CAJON_2_0; + dev_err(codec->dev, "%s : Cajon 2.0 detected\n", + __func__); + } + } + /* + * set to default boost option BOOST_SWITCH, user mixer path can change + * it to BOOST_ALWAYS or BOOST_BYPASS based on solution chosen. + */ + msm8x16_wcd_priv->boost_option = BOOST_SWITCH; + msm8x16_wcd_priv->hph_mode = NORMAL_MODE; + + for (i = 0; i < MSM89XX_RX_MAX; i++) + msm8x16_wcd_priv->comp_enabled[i] = COMPANDER_NONE; + + msm8x16_wcd_dt_parse_boost_info(codec); + msm8x16_wcd_set_boost_v(codec); + + snd_soc_add_codec_controls(codec, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_codec_controls(codec, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + msm8x16_wcd_bringup(codec); + msm8x16_wcd_codec_init_reg(codec); + msm8x16_wcd_update_reg_defaults(codec); + + wcd9xxx_spmi_set_codec(codec); + + msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].supply = + wcd8x16_wcd_codec_find_regulator( + codec->control_data, + on_demand_supply_name[ON_DEMAND_MICBIAS]); + atomic_set(&msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, 0); + + BLOCKING_INIT_NOTIFIER_HEAD(&msm8x16_wcd_priv->notifier); + + msm8x16_wcd_priv->fw_data = kzalloc(sizeof(*(msm8x16_wcd_priv->fw_data)) + , GFP_KERNEL); + if (!msm8x16_wcd_priv->fw_data) { + kfree(msm8x16_wcd_priv); + return -ENOMEM; + } + + set_bit(WCD9XXX_MBHC_CAL, msm8x16_wcd_priv->fw_data->cal_bit); + ret = wcd_cal_create_hwdep(msm8x16_wcd_priv->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + kfree(msm8x16_wcd_priv->fw_data); + kfree(msm8x16_wcd_priv); + return ret; + } + + wcd_mbhc_init(&msm8x16_wcd_priv->mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, true); + + msm8x16_wcd_priv->int_mclk0_enabled = false; + msm8x16_wcd_priv->clock_active = false; + msm8x16_wcd_priv->config_mode_active = false; + + /*Update speaker boost configuration*/ + msm8x16_wcd_priv->spk_boost_set = spkr_boost_en; + pr_err("%s: speaker boost configured = %d\n", + __func__, msm8x16_wcd_priv->spk_boost_set); + + /* Set initial MICBIAS voltage level */ + msm8x16_wcd_set_micb_v(codec); + + /* Set initial cap mode */ + msm8x16_wcd_configure_cap(codec, false, false); + registered_codec = codec; + adsp_state_notifier = + subsys_notif_register_notifier("adsp", + &adsp_state_notifier_block); + if (!adsp_state_notifier) { + dev_err(codec->dev, "Failed to register adsp state notifier\n"); + kfree(msm8x16_wcd_priv->fw_data); + kfree(msm8x16_wcd_priv); + registered_codec = NULL; + return -ENOMEM; + } + return 0; +} + +static int msm8x16_wcd_codec_remove(struct snd_soc_codec *codec) +{ + struct msm8x16_wcd_priv *msm8x16_wcd_priv = + snd_soc_codec_get_drvdata(codec); + struct msm8x16_wcd *msm8x16_wcd; + + msm8x16_wcd = codec->control_data; + msm8x16_wcd_priv->spkdrv_reg = NULL; + msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].supply = NULL; + atomic_set(&msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, 0); + kfree(msm8x16_wcd_priv->fw_data); + kfree(msm8x16_wcd_priv); + + return 0; +} + +static int msm8x16_wcd_enable_static_supplies_to_optimum( + struct msm8x16_wcd *msm8x16, + struct msm8x16_wcd_pdata *pdata) +{ + int i; + int ret = 0; + + for (i = 0; i < msm8x16->num_of_supplies; i++) { + if (pdata->regulator[i].ondemand) + continue; + if (regulator_count_voltages(msm8x16->supplies[i].consumer) <= + 0) + continue; + + ret = regulator_set_voltage(msm8x16->supplies[i].consumer, + pdata->regulator[i].min_uv, + pdata->regulator[i].max_uv); + if (ret) { + dev_err(msm8x16->dev, + "Setting volt failed for regulator %s err %d\n", + msm8x16->supplies[i].supply, ret); + } + + ret = regulator_set_load(msm8x16->supplies[i].consumer, + pdata->regulator[i].optimum_ua); + dev_err(msm8x16->dev, "Regulator %s set optimum mode\n", + msm8x16->supplies[i].supply); + } + + return ret; +} + +static int msm8x16_wcd_disable_static_supplies_to_optimum( + struct msm8x16_wcd *msm8x16, + struct msm8x16_wcd_pdata *pdata) +{ + int i; + int ret = 0; + + for (i = 0; i < msm8x16->num_of_supplies; i++) { + if (pdata->regulator[i].ondemand) + continue; + if (regulator_count_voltages(msm8x16->supplies[i].consumer) <= + 0) + continue; + regulator_set_voltage(msm8x16->supplies[i].consumer, 0, + pdata->regulator[i].max_uv); + regulator_set_load(msm8x16->supplies[i].consumer, 0); + dev_err(msm8x16->dev, "Regulator %s set optimum mode\n", + msm8x16->supplies[i].supply); + } + + return ret; +} + +int msm8x16_wcd_suspend(struct snd_soc_codec *codec) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct msm8x16_wcd *msm8x16 = codec->control_data; + struct msm8x16_wcd_pdata *msm8x16_pdata = msm8x16->dev->platform_data; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + pr_err("%s: mclk cnt = %d, mclk_enabled = %d\n", + __func__, atomic_read(&pdata->int_mclk0_rsc_ref), + atomic_read(&pdata->int_mclk0_enabled)); + if (atomic_read(&pdata->int_mclk0_enabled) == true) { + cancel_delayed_work_sync( + &pdata->disable_int_mclk0_work); + mutex_lock(&pdata->cdc_int_mclk0_mutex); + pdata->digital_cdc_core_clk.enable = 0; + afe_set_lpass_clock_v2(AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + atomic_set(&pdata->int_mclk0_enabled, false); + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + } + msm8x16_wcd_disable_static_supplies_to_optimum(msm8x16, msm8x16_pdata); + return 0; +} + +int msm8x16_wcd_resume(struct snd_soc_codec *codec) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct msm8x16_wcd *msm8x16 = codec->control_data; + struct msm8x16_wcd_pdata *msm8x16_pdata = msm8x16->dev->platform_data; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + msm8x16_wcd_enable_static_supplies_to_optimum(msm8x16, msm8x16_pdata); + return 0; +} + +static struct regmap *msm89xx_pmic_cdc_regmap; +static struct regmap *msm89xx_pmic_cdc_get_regmap(struct device *dev) +{ + return msm89xx_pmic_cdc_regmap; +} + +static const struct snd_soc_codec_driver soc_codec_dev_msm8x16_wcd = { + .probe = msm8x16_wcd_codec_probe, + .remove = msm8x16_wcd_codec_remove, + + .suspend = msm8x16_wcd_suspend, + .resume = msm8x16_wcd_resume, + + .controls = msm8x16_wcd_snd_controls, + .num_controls = ARRAY_SIZE(msm8x16_wcd_snd_controls), + .dapm_widgets = msm8x16_wcd_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm8x16_wcd_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + .get_regmap = msm89xx_pmic_cdc_get_regmap, +}; + +static int msm8x16_wcd_init_supplies(struct msm8x16_wcd *msm8x16, + struct msm8x16_wcd_pdata *pdata) +{ + int ret; + int i; + + msm8x16->supplies = kzalloc(sizeof(struct regulator_bulk_data) * + ARRAY_SIZE(pdata->regulator), + GFP_KERNEL); + if (!msm8x16->supplies) { + ret = -ENOMEM; + goto err; + } + + msm8x16->num_of_supplies = 0; + + if (ARRAY_SIZE(pdata->regulator) > MAX_REGULATOR) { + dev_err(msm8x16->dev, "%s: Array Size out of bound\n", + __func__); + ret = -EINVAL; + goto err; + } + + for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) { + if (pdata->regulator[i].name) { + msm8x16->supplies[i].supply = pdata->regulator[i].name; + msm8x16->num_of_supplies++; + } + } + + ret = regulator_bulk_get(msm8x16->dev, msm8x16->num_of_supplies, + msm8x16->supplies); + if (ret != 0) { + dev_err(msm8x16->dev, "Failed to get supplies: err = %d\n", + ret); + goto err_supplies; + } + + for (i = 0; i < msm8x16->num_of_supplies; i++) { + if (regulator_count_voltages(msm8x16->supplies[i].consumer) <= + 0) + continue; + + ret = regulator_set_voltage(msm8x16->supplies[i].consumer, + pdata->regulator[i].min_uv, + pdata->regulator[i].max_uv); + if (ret) { + dev_err(msm8x16->dev, "Setting regulator voltage failed for regulator %s err = %d\n", + msm8x16->supplies[i].supply, ret); + goto err_get; + } + + ret = regulator_set_load(msm8x16->supplies[i].consumer, + pdata->regulator[i].optimum_ua); + if (ret < 0) { + dev_err(msm8x16->dev, "Setting regulator optimum mode failed for regulator %s err = %d\n", + msm8x16->supplies[i].supply, ret); + goto err_get; + } else { + ret = 0; + } + } + + return ret; + +err_get: + regulator_bulk_free(msm8x16->num_of_supplies, msm8x16->supplies); +err_supplies: + kfree(msm8x16->supplies); +err: + return ret; +} + +static int msm8x16_wcd_enable_static_supplies(struct msm8x16_wcd *msm8x16, + struct msm8x16_wcd_pdata *pdata) +{ + int i; + int ret = 0; + + for (i = 0; i < msm8x16->num_of_supplies; i++) { + if (pdata->regulator[i].ondemand) + continue; + ret = regulator_enable(msm8x16->supplies[i].consumer); + if (ret) { + dev_err(msm8x16->dev, "Failed to enable %s\n", + msm8x16->supplies[i].supply); + break; + } + dev_err(msm8x16->dev, "Enabled regulator %s\n", + msm8x16->supplies[i].supply); + } + + while (ret && --i) + if (!pdata->regulator[i].ondemand) + regulator_disable(msm8x16->supplies[i].consumer); + + return ret; +} + + + +static void msm8x16_wcd_disable_supplies(struct msm8x16_wcd *msm8x16, + struct msm8x16_wcd_pdata *pdata) +{ + int i; + + regulator_bulk_disable(msm8x16->num_of_supplies, + msm8x16->supplies); + for (i = 0; i < msm8x16->num_of_supplies; i++) { + if (regulator_count_voltages(msm8x16->supplies[i].consumer) <= + 0) + continue; + regulator_set_voltage(msm8x16->supplies[i].consumer, 0, + pdata->regulator[i].max_uv); + regulator_set_load(msm8x16->supplies[i].consumer, 0); + } + regulator_bulk_free(msm8x16->num_of_supplies, msm8x16->supplies); + kfree(msm8x16->supplies); +} + +static struct snd_soc_dai_driver msm_codec_dais[] = { + { + .name = "msm-codec-rx", + .playback = { /* Support maximum range */ + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, + { + .name = "msm-codec-tx", + .capture = { /* Support maximum range */ + .stream_name = "Record", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, +}; + +static struct regmap *msm89xx_codec_regmap; +static struct regmap *msm89xx_codec_get_regmap(struct device *dev) +{ + return msm89xx_codec_regmap; +} + +static struct snd_soc_codec_driver soc_msm89xx_codec = { + .probe = msm89xx_digcodec_probe, + .remove = msm89xx_digcodec_remove, + .get_regmap = msm89xx_codec_get_regmap, +}; + +static const struct of_device_id msm89xx_codec_of_match[] = { + { .compatible = "qcom,msm-codec-core", + .data = "msm_codec"}, + { .compatible = "qcom,pmic-codec-digital", + .data = "pmic-digital-codec"}, + { .compatible = "qcom,pmic-codec-analog", + .data = "pmic-analog-codec"}, + {}, +}; +MODULE_DEVICE_TABLE(of, msm89xx_codec_of_match); + +static struct msm8x16_wcd *temp_89xx; +static int msm89xx_codec_probe(struct platform_device *pdev) +{ + int ret = 0; + struct msm8x16_wcd *msm8x16 = NULL; + struct msm8x16_wcd_pdata *pdata; + int adsp_state; + static int dev_registered_cnt; + const struct of_device_id *match; + const char *addr_prop_name = "qcom,dig-cdc-base-addr"; + u32 dig_cdc_addr; + char __iomem *dig_base; + + adsp_state = apr_get_subsys_state(); + if (adsp_state != APR_SUBSYS_LOADED) { + dev_err(&pdev->dev, "Adsp is not loaded yet %d\n", + adsp_state); + return -EPROBE_DEFER; + } + + match = of_match_node(msm89xx_codec_of_match, + pdev->dev.of_node); + + dev_dbg(&pdev->dev, "%s(%d):%s\n", + __func__, __LINE__, (char *)match->data); + + if (!strcmp(match->data, "pmic-digital-codec")) { + device_init_wakeup(&pdev->dev, true); + + if (pdev->dev.of_node) { + dev_err(&pdev->dev, "%s:Platform data from device tree\n", + __func__); + pdata = msm8x16_wcd_populate_dt_pdata(&pdev->dev); + pdev->dev.platform_data = pdata; + } else { + dev_err(&pdev->dev, "%s:Platform data from board file\n", + __func__); + pdata = pdev->dev.platform_data; + } + if (pdata == NULL) { + dev_err(&pdev->dev, "%s:Platform data failed to populate\n", + __func__); + goto rtn; + } + msm8x16 = kzalloc(sizeof(struct msm8x16_wcd), GFP_KERNEL); + if (msm8x16 == NULL) { + ret = -ENOMEM; + goto rtn; + } + + msm8x16->dev = &pdev->dev; + ret = msm8x16_wcd_init_supplies(msm8x16, pdata); + if (ret) { + dev_err(&pdev->dev, "%s: Fail to enable Codec supplies\n", + __func__); + goto err_codec; + } + + ret = msm8x16_wcd_enable_static_supplies(msm8x16, pdata); + if (ret) { + dev_err(&pdev->dev, + "%s: Fail to enable Codec pre-reset supplies\n", + __func__); + goto err_codec; + } + usleep_range(5, 6); + + mutex_init(&msm8x16->io_lock); + dev_set_drvdata(&pdev->dev, msm8x16); + temp_89xx = msm8x16; + dev_registered_cnt++; + } else if (!strcmp(match->data, "pmic-analog-codec")) { + if (wcd9xxx_spmi_irq_init()) { + dev_err(&pdev->dev, + "%s: irq initialization failed\n", __func__); + } else { + dev_err(&pdev->dev, + "%s: irq initialization passed\n", __func__); + } + dev_registered_cnt++; + } else if (!strcmp(match->data, "msm-codec")) { + ret = of_property_read_u32(pdev->dev.of_node, addr_prop_name, + &dig_cdc_addr); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, addr_prop_name); + dig_cdc_addr = MSM89XX_DIGITAL_CODEC_BASE_ADDR; + } + dig_base = ioremap(dig_cdc_addr, + MSM89XX_DIGITAL_CODEC_REG_SIZE); + if (dig_base == NULL) { + dev_err(&pdev->dev, "%s ioremap failed\n", __func__); + return -ENOMEM; + } + msm89xx_codec_regmap = + devm_regmap_init_mmio_clk(&pdev->dev, NULL, + dig_base, &msm89xx_cdc_core_regmap_config); + snd_soc_register_codec(&pdev->dev, &soc_msm89xx_codec, + msm_codec_dais, ARRAY_SIZE(msm_codec_dais)); + dev_registered_cnt++; + } + + if ((dev_registered_cnt == MAX_MSM89XX_DEVICE) && (!ret)) { + msm89xx_pmic_cdc_regmap = + devm_regmap_init_spmi_ext( + (struct spmi_device *) &pdev->dev.parent, + &msm89xx_pmic_cdc_regmap_config); + ret = snd_soc_register_codec(temp_89xx->dev, + &soc_codec_dev_msm8x16_wcd, + msm8x16_wcd_i2s_dai, + ARRAY_SIZE(msm8x16_wcd_i2s_dai)); + if (ret) { + dev_err(&pdev->dev, + "%s:snd_soc_register_codec failed with error %d\n", + __func__, ret); + goto err_supplies; + } + } + return ret; +err_supplies: + msm8x16_wcd_disable_supplies(msm8x16, pdata); +err_codec: + kfree(msm8x16); +rtn: + return ret; +} + +static int msm89xx_codec_remove(struct platform_device *pdev) +{ + struct msm8x16_wcd *msm8x16 = dev_get_drvdata(&pdev->dev); + + mutex_destroy(&msm8x16->io_lock); + kfree(msm8x16); + + return 0; +} + +static struct platform_driver msm_codec_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .of_match_table = of_match_ptr(msm89xx_codec_of_match) + }, + .probe = msm89xx_codec_probe, + .remove = msm89xx_codec_remove, +}; +module_platform_driver(msm_codec_driver); + +MODULE_DESCRIPTION("MSM89xx Audio codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/msm8x16/msm8x16-wcd.h b/sound/soc/codecs/msm8x16/msm8x16-wcd.h new file mode 100644 index 0000000000000000000000000000000000000000..45ebab2030315b70f7d53a02517762bac4efe54d --- /dev/null +++ b/sound/soc/codecs/msm8x16/msm8x16-wcd.h @@ -0,0 +1,315 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef MSM8X16_WCD_H +#define MSM8X16_WCD_H + +#include +#include +#include +#include "../wcd-mbhc-v2.h" +#include "../wcdcal-hwdep.h" +#include "msm8x16_wcd_registers.h" + +#define MICBIAS_EXT_BYP_CAP 0x00 +#define MICBIAS_NO_EXT_BYP_CAP 0x01 + +#define MSM89XX_NUM_IRQ_REGS 2 +#define MAX_REGULATOR 7 +#define MSM89XX_REG_VAL(reg, val) {reg, 0, val} +#define MSM8X16_TOMBAK_LPASS_AUDIO_CORE_DIG_CODEC_CLK_SEL 0xFE03B004 +#define MSM8X16_TOMBAK_LPASS_DIGCODEC_CMD_RCGR 0x0181C09C +#define MSM8X16_TOMBAK_LPASS_DIGCODEC_CFG_RCGR 0x0181C0A0 +#define MSM8X16_TOMBAK_LPASS_DIGCODEC_M 0x0181C0A4 +#define MSM8X16_TOMBAK_LPASS_DIGCODEC_N 0x0181C0A8 +#define MSM8X16_TOMBAK_LPASS_DIGCODEC_D 0x0181C0AC +#define MSM8X16_TOMBAK_LPASS_DIGCODEC_CBCR 0x0181C0B0 +#define MSM8X16_TOMBAK_LPASS_DIGCODEC_AHB_CBCR 0x0181C0B4 + +#define MSM8X16_CODEC_NAME "msm8x16_wcd_codec" + +#define MSM89XX_IS_CDC_CORE_REG(reg) \ + (((reg >= 0x00) && (reg <= 0x3FF)) ? 1 : 0) +#define MSM89XX_IS_PMIC_CDC_REG(reg) \ + (((reg >= 0xF000) && (reg <= 0xF1FF)) ? 1 : 0) +/* + * MCLK activity indicators during suspend and resume call + */ +#define MCLK_SUS_DIS 1 +#define MCLK_SUS_RSC 2 +#define MCLK_SUS_NO_ACT 3 + +#define NUM_DECIMATORS 4 +#define MSM89XX_VDD_SPKDRV_NAME "cdc-vdd-spkdrv" + +#define DEFAULT_MULTIPLIER 800 +#define DEFAULT_GAIN 9 +#define DEFAULT_OFFSET 100 + +extern const u8 msm89xx_pmic_cdc_reg_readable[MSM89XX_PMIC_CDC_CACHE_SIZE]; +extern const u8 msm89xx_cdc_core_reg_readable[MSM89XX_CDC_CORE_CACHE_SIZE]; +extern struct regmap_config msm89xx_cdc_core_regmap_config; +extern struct regmap_config msm89xx_pmic_cdc_regmap_config; + +enum codec_versions { + TOMBAK_1_0, + TOMBAK_2_0, + CONGA, + CAJON, + CAJON_2_0, + DIANGU, + UNSUPPORTED, +}; + +/* Support different hph modes */ +enum { + NORMAL_MODE = 0, + HD2_MODE, +}; + +/* Codec supports 1 compander */ +enum { + COMPANDER_NONE = 0, + COMPANDER_1, /* HPHL/R */ + COMPANDER_MAX, +}; + +enum wcd_curr_ref { + I_h4_UA = 0, + I_pt5_UA, + I_14_UA, + I_l4_UA, + I_1_UA, +}; + +enum wcd_mbhc_imp_det_pin { + WCD_MBHC_DET_NONE = 0, + WCD_MBHC_DET_HPHL, + WCD_MBHC_DET_HPHR, + WCD_MBHC_DET_BOTH, +}; + + +/* Each micbias can be assigned to one of three cfilters + * Vbatt_min >= .15V + ldoh_v + * ldoh_v >= .15v + cfiltx_mv + * If ldoh_v = 1.95 160 mv < cfiltx_mv < 1800 mv + * If ldoh_v = 2.35 200 mv < cfiltx_mv < 2200 mv + * If ldoh_v = 2.75 240 mv < cfiltx_mv < 2600 mv + * If ldoh_v = 2.85 250 mv < cfiltx_mv < 2700 mv + */ + +struct wcd9xxx_micbias_setting { + u8 ldoh_v; + u32 cfilt1_mv; /* in mv */ + u32 cfilt2_mv; /* in mv */ + u32 cfilt3_mv; /* in mv */ + /* Different WCD9xxx series codecs may not + * have 4 mic biases. If a codec has fewer + * mic biases, some of these properties will + * not be used. + */ + u8 bias1_cfilt_sel; + u8 bias2_cfilt_sel; + u8 bias3_cfilt_sel; + u8 bias4_cfilt_sel; + u8 bias1_cap_mode; + u8 bias2_cap_mode; + u8 bias3_cap_mode; + u8 bias4_cap_mode; + bool bias2_is_headset_only; +}; + +enum msm8x16_wcd_pid_current { + MSM89XX_PID_MIC_2P5_UA, + MSM89XX_PID_MIC_5_UA, + MSM89XX_PID_MIC_10_UA, + MSM89XX_PID_MIC_20_UA, +}; + +struct msm8x16_wcd_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +enum msm8x16_wcd_mbhc_analog_pwr_cfg { + MSM89XX_ANALOG_PWR_COLLAPSED = 0, + MSM89XX_ANALOG_PWR_ON, + MSM89XX_NUM_ANALOG_PWR_CONFIGS, +}; + +/* Number of input and output I2S port */ +enum { + MSM89XX_RX1 = 0, + MSM89XX_RX2, + MSM89XX_RX3, + MSM89XX_RX_MAX, +}; + +enum { + MSM89XX_TX1 = 0, + MSM89XX_TX2, + MSM89XX_TX3, + MSM89XX_TX4, + MSM89XX_TX_MAX, +}; + +enum { + /* INTR_REG 0 - Digital Periph */ + MSM89XX_IRQ_SPKR_CNP = 0, + MSM89XX_IRQ_SPKR_CLIP, + MSM89XX_IRQ_SPKR_OCP, + MSM89XX_IRQ_MBHC_INSREM_DET1, + MSM89XX_IRQ_MBHC_RELEASE, + MSM89XX_IRQ_MBHC_PRESS, + MSM89XX_IRQ_MBHC_INSREM_DET, + MSM89XX_IRQ_MBHC_HS_DET, + /* INTR_REG 1 - Analog Periph */ + MSM89XX_IRQ_EAR_OCP, + MSM89XX_IRQ_HPHR_OCP, + MSM89XX_IRQ_HPHL_OCP, + MSM89XX_IRQ_EAR_CNP, + MSM89XX_IRQ_HPHR_CNP, + MSM89XX_IRQ_HPHL_CNP, + MSM89XX_NUM_IRQS, +}; + +enum { + ON_DEMAND_MICBIAS = 0, + ON_DEMAND_SPKDRV, + ON_DEMAND_SUPPLIES_MAX, +}; + +/* + * The delay list is per codec HW specification. + * Please add delay in the list in the future instead + * of magic number + */ +enum { + CODEC_DELAY_1_MS = 1000, + CODEC_DELAY_1_1_MS = 1100, +}; + +struct msm8x16_wcd_regulator { + const char *name; + int min_uv; + int max_uv; + int optimum_ua; + bool ondemand; + struct regulator *regulator; +}; + +struct on_demand_supply { + struct regulator *supply; + atomic_t ref; +}; + +struct wcd_imped_i_ref { + enum wcd_curr_ref curr_ref; + int min_val; + int multiplier; + int gain_adj; + int offset; +}; + +struct msm8x16_wcd_pdata { + int irq; + int irq_base; + int num_irqs; + int reset_gpio; + void *msm8x16_wcd_ahb_base_vaddr; + struct wcd9xxx_micbias_setting micbias; + struct msm8x16_wcd_regulator regulator[MAX_REGULATOR]; + u32 mclk_rate; + u32 is_lpass; +}; + +enum msm8x16_wcd_micbias_num { + MSM89XX_MICBIAS1 = 0, +}; + +struct msm8x16_wcd { + struct device *dev; + struct mutex io_lock; + u8 version; + + int reset_gpio; + int (*read_dev)(struct snd_soc_codec *codec, + unsigned short reg); + int (*write_dev)(struct snd_soc_codec *codec, + unsigned short reg, u8 val); + + u32 num_of_supplies; + struct regulator_bulk_data *supplies; + + u8 idbyte[4]; + + int num_irqs; + u32 mclk_rate; +}; + +struct msm8x16_wcd_priv { + struct snd_soc_codec *codec; + u16 pmic_rev; + u16 codec_version; + u32 boost_voltage; + u32 adc_count; + u32 rx_bias_count; + s32 dmic_1_2_clk_cnt; + u32 mute_mask; + bool int_mclk0_enabled; + bool clock_active; + bool config_mode_active; + u16 boost_option; + /* mode to select hd2 */ + u32 hph_mode; + /* compander used for each rx chain */ + u32 comp_enabled[MSM89XX_RX_MAX]; + bool spk_boost_set; + bool ear_pa_boost_set; + bool ext_spk_boost_set; + bool dec_active[NUM_DECIMATORS]; + struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX]; + struct regulator *spkdrv_reg; + /* mbhc module */ + struct wcd_mbhc mbhc; + /* cal info for codec */ + struct fw_info *fw_data; + struct blocking_notifier_head notifier; + int (*codec_spk_ext_pa_cb)(struct snd_soc_codec *codec, int enable); + int (*codec_hph_comp_gpio)(bool enable); + unsigned long status_mask; + struct wcd_imped_i_ref imped_i_ref; + enum wcd_mbhc_imp_det_pin imped_det_pin; +}; + +extern int msm8x16_wcd_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, + bool dapm); + +extern int msm8x16_wcd_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg); + +extern void msm8x16_wcd_hs_detect_exit(struct snd_soc_codec *codec); + +extern void msm8x16_update_int_spk_boost(bool enable); + +extern void msm8x16_wcd_spk_ext_pa_cb( + int (*codec_spk_ext_pa)(struct snd_soc_codec *codec, + int enable), struct snd_soc_codec *codec); + +extern void msm8x16_wcd_hph_comp_cb( + int (*codec_hph_comp_gpio)(bool enable), + struct snd_soc_codec *codec); +void enable_digital_callback(void *flag); +void disable_digital_callback(void *flag); + +#endif diff --git a/sound/soc/codecs/msm8x16/msm8x16_wcd_registers.h b/sound/soc/codecs/msm8x16/msm8x16_wcd_registers.h new file mode 100644 index 0000000000000000000000000000000000000000..ec26ef39c88186df49b640450e812e31e7d574e6 --- /dev/null +++ b/sound/soc/codecs/msm8x16/msm8x16_wcd_registers.h @@ -0,0 +1,585 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef MSM8X16_WCD_REGISTERS_H +#define MSM8X16_WCD_REGISTERS_H + +#define CDC_DIG_BASE 0xF000 +#define CDC_ANA_BASE 0xF100 + +#define MSM89XX_PMIC_DIGITAL_REVISION1 (CDC_DIG_BASE+0x000) +#define MSM89XX_PMIC_DIGITAL_REVISION1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_REVISION2 (CDC_DIG_BASE+0x001) +#define MSM89XX_PMIC_DIGITAL_REVISION2__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PERPH_TYPE (CDC_DIG_BASE+0x004) +#define MSM89XX_PMIC_DIGITAL_PERPH_TYPE__POR (0x23) +#define MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE (CDC_DIG_BASE+0x005) +#define MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE__POR (0x01) +#define MSM89XX_PMIC_DIGITAL_INT_RT_STS (CDC_DIG_BASE+0x010) +#define MSM89XX_PMIC_DIGITAL_INT_RT_STS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_SET_TYPE (CDC_DIG_BASE+0x011) +#define MSM89XX_PMIC_DIGITAL_INT_SET_TYPE__POR (0xFF) +#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH (CDC_DIG_BASE+0x012) +#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH__POR (0xFF) +#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW (CDC_DIG_BASE+0x013) +#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR (CDC_DIG_BASE+0x014) +#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_EN_SET (CDC_DIG_BASE+0x015) +#define MSM89XX_PMIC_DIGITAL_INT_EN_SET__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_EN_CLR (CDC_DIG_BASE+0x016) +#define MSM89XX_PMIC_DIGITAL_INT_EN_CLR__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS (CDC_DIG_BASE+0x018) +#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_PENDING_STS (CDC_DIG_BASE+0x019) +#define MSM89XX_PMIC_DIGITAL_INT_PENDING_STS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_MID_SEL (CDC_DIG_BASE+0x01A) +#define MSM89XX_PMIC_DIGITAL_INT_MID_SEL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_PRIORITY (CDC_DIG_BASE+0x01B) +#define MSM89XX_PMIC_DIGITAL_INT_PRIORITY__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_GPIO_MODE (CDC_DIG_BASE+0x040) +#define MSM89XX_PMIC_DIGITAL_GPIO_MODE__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PIN_CTL_OE (CDC_DIG_BASE+0x041) +#define MSM89XX_PMIC_DIGITAL_PIN_CTL_OE__POR (0x01) +#define MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA (CDC_DIG_BASE+0x042) +#define MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PIN_STATUS (CDC_DIG_BASE+0x043) +#define MSM89XX_PMIC_DIGITAL_PIN_STATUS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_HDRIVE_CTL (CDC_DIG_BASE+0x044) +#define MSM89XX_PMIC_DIGITAL_HDRIVE_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_RST_CTL (CDC_DIG_BASE+0x046) +#define MSM89XX_PMIC_DIGITAL_CDC_RST_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL (CDC_DIG_BASE+0x048) +#define MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL (CDC_DIG_BASE+0x049) +#define MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL (CDC_DIG_BASE+0x04A) +#define MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL (CDC_DIG_BASE+0x050) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL__POR (0x02) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL (CDC_DIG_BASE+0x051) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL__POR (0x02) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL (CDC_DIG_BASE+0x052) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL (CDC_DIG_BASE+0x053) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL (CDC_DIG_BASE+0x054) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL (CDC_DIG_BASE+0x055) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL (CDC_DIG_BASE+0x056) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1 (CDC_DIG_BASE+0x058) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1__POR (0x7C) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2 (CDC_DIG_BASE+0x059) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2__POR (0x7C) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3 (CDC_DIG_BASE+0x05A) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3__POR (0x7C) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0 (CDC_DIG_BASE+0x05B) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1 (CDC_DIG_BASE+0x05C) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2 (CDC_DIG_BASE+0x05D) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3 (CDC_DIG_BASE+0x05E) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL (CDC_DIG_BASE+0x068) +#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN (CDC_DIG_BASE+0x069) +#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_SPARE_0 (CDC_DIG_BASE+0x070) +#define MSM89XX_PMIC_DIGITAL_SPARE_0__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_SPARE_1 (CDC_DIG_BASE+0x071) +#define MSM89XX_PMIC_DIGITAL_SPARE_1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_SPARE_2 (CDC_DIG_BASE+0x072) +#define MSM89XX_PMIC_DIGITAL_SPARE_2__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_SEC_ACCESS (CDC_DIG_BASE+0x0D0) +#define MSM89XX_PMIC_DIGITAL_SEC_ACCESS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1 (CDC_DIG_BASE+0x0D8) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2 (CDC_DIG_BASE+0x0D9) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2__POR (0x01) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3 (CDC_DIG_BASE+0x0DA) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3__POR (0x05) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4 (CDC_DIG_BASE+0x0DB) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_TEST1 (CDC_DIG_BASE+0x0E0) +#define MSM89XX_PMIC_DIGITAL_INT_TEST1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_TEST_VAL (CDC_DIG_BASE+0x0E1) +#define MSM89XX_PMIC_DIGITAL_INT_TEST_VAL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_TRIM_NUM (CDC_DIG_BASE+0x0F0) +#define MSM89XX_PMIC_DIGITAL_TRIM_NUM__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_TRIM_CTRL (CDC_DIG_BASE+0x0F1) +#define MSM89XX_PMIC_DIGITAL_TRIM_CTRL__POR (0x00) + +#define MSM89XX_PMIC_ANALOG_REVISION1 (CDC_ANA_BASE+0x00) +#define MSM89XX_PMIC_ANALOG_REVISION1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_REVISION2 (CDC_ANA_BASE+0x01) +#define MSM89XX_PMIC_ANALOG_REVISION2__POR (0x00) +#define MSM89XX_PMIC_ANALOG_REVISION3 (CDC_ANA_BASE+0x02) +#define MSM89XX_PMIC_ANALOG_REVISION3__POR (0x00) +#define MSM89XX_PMIC_ANALOG_REVISION4 (CDC_ANA_BASE+0x03) +#define MSM89XX_PMIC_ANALOG_REVISION4__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PERPH_TYPE (CDC_ANA_BASE+0x04) +#define MSM89XX_PMIC_ANALOG_PERPH_TYPE__POR (0x23) +#define MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE (CDC_ANA_BASE+0x05) +#define MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE__POR (0x09) +#define MSM89XX_PMIC_ANALOG_INT_RT_STS (CDC_ANA_BASE+0x10) +#define MSM89XX_PMIC_ANALOG_INT_RT_STS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_SET_TYPE (CDC_ANA_BASE+0x11) +#define MSM89XX_PMIC_ANALOG_INT_SET_TYPE__POR (0x3F) +#define MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH (CDC_ANA_BASE+0x12) +#define MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH__POR (0x3F) +#define MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW (CDC_ANA_BASE+0x13) +#define MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR (CDC_ANA_BASE+0x14) +#define MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_EN_SET (CDC_ANA_BASE+0x15) +#define MSM89XX_PMIC_ANALOG_INT_EN_SET__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_EN_CLR (CDC_ANA_BASE+0x16) +#define MSM89XX_PMIC_ANALOG_INT_EN_CLR__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_LATCHED_STS (CDC_ANA_BASE+0x18) +#define MSM89XX_PMIC_ANALOG_INT_LATCHED_STS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_PENDING_STS (CDC_ANA_BASE+0x19) +#define MSM89XX_PMIC_ANALOG_INT_PENDING_STS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_MID_SEL (CDC_ANA_BASE+0x1A) +#define MSM89XX_PMIC_ANALOG_INT_MID_SEL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_PRIORITY (CDC_ANA_BASE+0x1B) +#define MSM89XX_PMIC_ANALOG_INT_PRIORITY__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MICB_1_EN (CDC_ANA_BASE+0x40) +#define MSM89XX_PMIC_ANALOG_MICB_1_EN__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MICB_1_VAL (CDC_ANA_BASE+0x41) +#define MSM89XX_PMIC_ANALOG_MICB_1_VAL__POR (0x20) +#define MSM89XX_PMIC_ANALOG_MICB_1_CTL (CDC_ANA_BASE+0x42) +#define MSM89XX_PMIC_ANALOG_MICB_1_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS (CDC_ANA_BASE+0x43) +#define MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS__POR (0x49) +#define MSM89XX_PMIC_ANALOG_MICB_2_EN (CDC_ANA_BASE+0x44) +#define MSM89XX_PMIC_ANALOG_MICB_2_EN__POR (0x20) +#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2 (CDC_ANA_BASE+0x45) +#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL (CDC_ANA_BASE+0x46) +#define MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1 (CDC_ANA_BASE+0x47) +#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1__POR (0x35) +#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2 (CDC_ANA_BASE+0x50) +#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2__POR (0x08) +#define MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL (CDC_ANA_BASE+0x51) +#define MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER (CDC_ANA_BASE+0x52) +#define MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER__POR (0x98) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL (CDC_ANA_BASE+0x53) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL (CDC_ANA_BASE+0x54) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL__POR (0x20) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL (CDC_ANA_BASE+0x55) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL__POR (0x40) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL (CDC_ANA_BASE+0x56) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL__POR (0x61) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL (CDC_ANA_BASE+0x57) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL__POR (0x80) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT (CDC_ANA_BASE+0x58) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT (CDC_ANA_BASE+0x59) +#define MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TX_1_EN (CDC_ANA_BASE+0x60) +#define MSM89XX_PMIC_ANALOG_TX_1_EN__POR (0x03) +#define MSM89XX_PMIC_ANALOG_TX_2_EN (CDC_ANA_BASE+0x61) +#define MSM89XX_PMIC_ANALOG_TX_2_EN__POR (0x03) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1 (CDC_ANA_BASE+0x62) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1__POR (0xBF) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2 (CDC_ANA_BASE+0x63) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2__POR (0x8C) +#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL (CDC_ANA_BASE+0x64) +#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS (CDC_ANA_BASE+0x65) +#define MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS__POR (0x6B) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV (CDC_ANA_BASE+0x66) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV__POR (0x51) +#define MSM89XX_PMIC_ANALOG_TX_3_EN (CDC_ANA_BASE+0x67) +#define MSM89XX_PMIC_ANALOG_TX_3_EN__POR (0x02) +#define MSM89XX_PMIC_ANALOG_NCP_EN (CDC_ANA_BASE+0x80) +#define MSM89XX_PMIC_ANALOG_NCP_EN__POR (0x26) +#define MSM89XX_PMIC_ANALOG_NCP_CLK (CDC_ANA_BASE+0x81) +#define MSM89XX_PMIC_ANALOG_NCP_CLK__POR (0x23) +#define MSM89XX_PMIC_ANALOG_NCP_DEGLITCH (CDC_ANA_BASE+0x82) +#define MSM89XX_PMIC_ANALOG_NCP_DEGLITCH__POR (0x5B) +#define MSM89XX_PMIC_ANALOG_NCP_FBCTRL (CDC_ANA_BASE+0x83) +#define MSM89XX_PMIC_ANALOG_NCP_FBCTRL__POR (0x08) +#define MSM89XX_PMIC_ANALOG_NCP_BIAS (CDC_ANA_BASE+0x84) +#define MSM89XX_PMIC_ANALOG_NCP_BIAS__POR (0x29) +#define MSM89XX_PMIC_ANALOG_NCP_VCTRL (CDC_ANA_BASE+0x85) +#define MSM89XX_PMIC_ANALOG_NCP_VCTRL__POR (0x24) +#define MSM89XX_PMIC_ANALOG_NCP_TEST (CDC_ANA_BASE+0x86) +#define MSM89XX_PMIC_ANALOG_NCP_TEST__POR (0x00) +#define MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR (CDC_ANA_BASE+0x87) +#define MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR__POR (0xD5) +#define MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER (CDC_ANA_BASE+0x90) +#define MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER__POR (0xE8) +#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL (CDC_ANA_BASE+0x91) +#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL__POR (0xCF) +#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT (CDC_ANA_BASE+0x92) +#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT__POR (0x6E) +#define MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC (CDC_ANA_BASE+0x93) +#define MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC__POR (0x18) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA (CDC_ANA_BASE+0x94) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA__POR (0x5A) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP (CDC_ANA_BASE+0x95) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP__POR (0x69) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP (CDC_ANA_BASE+0x96) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP__POR (0x29) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN (CDC_ANA_BASE+0x97) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN__POR (0x80) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL (CDC_ANA_BASE+0x98) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL__POR (0xDA) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME (CDC_ANA_BASE+0x99) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME__POR (0x16) +#define MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST (CDC_ANA_BASE+0x9A) +#define MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL (CDC_ANA_BASE+0x9B) +#define MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL__POR (0x20) +#define MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST (CDC_ANA_BASE+0x9C) +#define MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL (CDC_ANA_BASE+0x9D) +#define MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL__POR (0x20) +#define MSM89XX_PMIC_ANALOG_RX_EAR_CTL (CDC_ANA_BASE+0x9E) +#define MSM89XX_PMIC_ANALOG_RX_EAR_CTL___POR (0x12) +#define MSM89XX_PMIC_ANALOG_RX_ATEST (CDC_ANA_BASE+0x9F) +#define MSM89XX_PMIC_ANALOG_RX_ATEST__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_HPH_STATUS (CDC_ANA_BASE+0xA0) +#define MSM89XX_PMIC_ANALOG_RX_HPH_STATUS__POR (0x0C) +#define MSM89XX_PMIC_ANALOG_RX_EAR_STATUS (CDC_ANA_BASE+0xA1) +#define MSM89XX_PMIC_ANALOG_RX_EAR_STATUS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL (CDC_ANA_BASE+0xAC) +#define MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL (CDC_ANA_BASE+0xAD) +#define MSM89XX_PMIC_ANALOG_RX_RX_LO_EN_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL (CDC_ANA_BASE+0xB0) +#define MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL__POR (0x83) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET (CDC_ANA_BASE+0xB1) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET__POR (0x91) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL (CDC_ANA_BASE+0xB2) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL__POR (0x29) +#define MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET (CDC_ANA_BASE+0xB3) +#define MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET__POR (0x4D) +#define MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL (CDC_ANA_BASE+0xB4) +#define MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL__POR (0xE1) +#define MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL (CDC_ANA_BASE+0xB5) +#define MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL__POR (0x1E) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC (CDC_ANA_BASE+0xB6) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC__POR (0xCB) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG (CDC_ANA_BASE+0xB7) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG__POR (0x00) +#define MSM89XX_PMIC_ANALOG_CURRENT_LIMIT (CDC_ANA_BASE+0xC0) +#define MSM89XX_PMIC_ANALOG_CURRENT_LIMIT__POR (0x02) +#define MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE (CDC_ANA_BASE+0xC1) +#define MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE__POR (0x14) +#define MSM89XX_PMIC_ANALOG_BYPASS_MODE (CDC_ANA_BASE+0xC2) +#define MSM89XX_PMIC_ANALOG_BYPASS_MODE__POR (0x00) +#define MSM89XX_PMIC_ANALOG_BOOST_EN_CTL (CDC_ANA_BASE+0xC3) +#define MSM89XX_PMIC_ANALOG_BOOST_EN_CTL__POR (0x1F) +#define MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO (CDC_ANA_BASE+0xC4) +#define MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO__POR (0x8C) +#define MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE (CDC_ANA_BASE+0xC5) +#define MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE__POR (0xC0) +#define MSM89XX_PMIC_ANALOG_BOOST_TEST1_1 (CDC_ANA_BASE+0xC6) +#define MSM89XX_PMIC_ANALOG_BOOST_TEST1_1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_BOOST_TEST_2 (CDC_ANA_BASE+0xC7) +#define MSM89XX_PMIC_ANALOG_BOOST_TEST_2__POR (0x00) +#define MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS (CDC_ANA_BASE+0xC8) +#define MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS (CDC_ANA_BASE+0xC9) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR (CDC_ANA_BASE+0xCE) +#define MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL (CDC_ANA_BASE+0xCF) +#define MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_SEC_ACCESS (CDC_ANA_BASE+0xD0) +#define MSM89XX_PMIC_ANALOG_SEC_ACCESS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1 (CDC_ANA_BASE+0xD8) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2 (CDC_ANA_BASE+0xD9) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2__POR (0x01) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3 (CDC_ANA_BASE+0xDA) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3__POR (0x05) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4 (CDC_ANA_BASE+0xDB) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_TEST1 (CDC_ANA_BASE+0xE0) +#define MSM89XX_PMIC_ANALOG_INT_TEST1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_TEST_VAL (CDC_ANA_BASE+0xE1) +#define MSM89XX_PMIC_ANALOG_INT_TEST_VAL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TRIM_NUM (CDC_ANA_BASE+0xF0) +#define MSM89XX_PMIC_ANALOG_TRIM_NUM__POR (0x04) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL1 (CDC_ANA_BASE+0xF1) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL2 (CDC_ANA_BASE+0xF2) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL2__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL3 (CDC_ANA_BASE+0xF3) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL3__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL4 (CDC_ANA_BASE+0xF4) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL4__POR (0x00) + +#define MSM89XX_PMIC_CDC_NUM_REGISTERS \ + (MSM89XX_PMIC_ANALOG_TRIM_CTRL4+1) +#define MSM89XX_PMIC_CDC_MAX_REGISTER \ + (MSM89XX_PMIC_CDC_NUM_REGISTERS-1) +#define MSM89XX_PMIC_CDC_CACHE_SIZE \ + MSM89XX_PMIC_CDC_NUM_REGISTERS + + +#define MSM89XX_CDC_CORE_CLK_RX_RESET_CTL (0x00) +#define MSM89XX_CDC_CORE_CLK_RX_RESET_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL (0x04) +#define MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL (0x08) +#define MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_RX_I2S_CTL (0x0C) +#define MSM89XX_CDC_CORE_CLK_RX_I2S_CTL__POR (0x13) +#define MSM89XX_CDC_CORE_CLK_TX_I2S_CTL (0x10) +#define MSM89XX_CDC_CORE_CLK_TX_I2S_CTL__POR (0x13) +#define MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL (0x14) +#define MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL (0x18) +#define MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_OTHR_CTL (0x1C) +#define MSM89XX_CDC_CORE_CLK_OTHR_CTL__POR (0x04) +#define MSM89XX_CDC_CORE_CLK_RX_B1_CTL (0x20) +#define MSM89XX_CDC_CORE_CLK_RX_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_MCLK_CTL (0x24) +#define MSM89XX_CDC_CORE_CLK_MCLK_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_PDM_CTL (0x28) +#define MSM89XX_CDC_CORE_CLK_PDM_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_SD_CTL (0x2C) +#define MSM89XX_CDC_CORE_CLK_SD_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_WSA_VI_B1_CTL (0x30) +#define MSM89XX_CDC_CORE_CLK_WSA_VI_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_RX_B2_CTL (0x34) +#define MSM89XX_CDC_CORE_CLK_RX_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B1_CTL (0x40) +#define MSM89XX_CDC_CORE_RX1_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B1_CTL (0x60) +#define MSM89XX_CDC_CORE_RX2_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B1_CTL (0x80) +#define MSM89XX_CDC_CORE_RX3_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B2_CTL (0x44) +#define MSM89XX_CDC_CORE_RX1_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B2_CTL (0x64) +#define MSM89XX_CDC_CORE_RX2_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B2_CTL (0x84) +#define MSM89XX_CDC_CORE_RX3_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B3_CTL (0x48) +#define MSM89XX_CDC_CORE_RX1_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B3_CTL (0x68) +#define MSM89XX_CDC_CORE_RX2_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B3_CTL (0x88) +#define MSM89XX_CDC_CORE_RX3_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B4_CTL (0x4C) +#define MSM89XX_CDC_CORE_RX1_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B4_CTL (0x6C) +#define MSM89XX_CDC_CORE_RX2_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B4_CTL (0x8C) +#define MSM89XX_CDC_CORE_RX3_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B5_CTL (0x50) +#define MSM89XX_CDC_CORE_RX1_B5_CTL__POR (0x68) +#define MSM89XX_CDC_CORE_RX2_B5_CTL (0x70) +#define MSM89XX_CDC_CORE_RX2_B5_CTL__POR (0x68) +#define MSM89XX_CDC_CORE_RX3_B5_CTL (0x90) +#define MSM89XX_CDC_CORE_RX3_B5_CTL__POR (0x68) +#define MSM89XX_CDC_CORE_RX1_B6_CTL (0x54) +#define MSM89XX_CDC_CORE_RX1_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B6_CTL (0x74) +#define MSM89XX_CDC_CORE_RX2_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B6_CTL (0x94) +#define MSM89XX_CDC_CORE_RX3_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL (0x58) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL (0x78) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL (0x98) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL (0x5C) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL (0x7C) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL (0x9C) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TOP_GAIN_UPDATE (0xA0) +#define MSM89XX_CDC_CORE_TOP_GAIN_UPDATE__POR (0x00) +#define MSM89XX_CDC_CORE_TOP_CTL (0xA4) +#define MSM89XX_CDC_CORE_TOP_CTL__POR (0x01) +#define MSM89XX_CDC_CORE_COMP0_B1_CTL (0xB0) +#define MSM89XX_CDC_CORE_COMP0_B1_CTL__POR (0x30) +#define MSM89XX_CDC_CORE_COMP0_B2_CTL (0xB4) +#define MSM89XX_CDC_CORE_COMP0_B2_CTL__POR (0xB5) +#define MSM89XX_CDC_CORE_COMP0_B3_CTL (0xB8) +#define MSM89XX_CDC_CORE_COMP0_B3_CTL__POR (0x28) +#define MSM89XX_CDC_CORE_COMP0_B4_CTL (0xBC) +#define MSM89XX_CDC_CORE_COMP0_B4_CTL__POR (0x37) +#define MSM89XX_CDC_CORE_COMP0_B5_CTL (0xC0) +#define MSM89XX_CDC_CORE_COMP0_B5_CTL__POR (0x7F) +#define MSM89XX_CDC_CORE_COMP0_B6_CTL (0xC4) +#define MSM89XX_CDC_CORE_COMP0_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS (0xC8) +#define MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS__POR (0x03) +#define MSM89XX_CDC_CORE_COMP0_FS_CFG (0xCC) +#define MSM89XX_CDC_CORE_COMP0_FS_CFG__POR (0x03) +#define MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL (0xD0) +#define MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL__POR (0x02) +#define MSM89XX_CDC_CORE_DEBUG_DESER1_CTL (0xE0) +#define MSM89XX_CDC_CORE_DEBUG_DESER1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_DEBUG_DESER2_CTL (0xE4) +#define MSM89XX_CDC_CORE_DEBUG_DESER2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG (0xE8) +#define MSM89XX_CDC_CORE_DEBUG_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG (0xEC) +#define MSM89XX_CDC_CORE_DEBUG_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG (0xF0) +#define MSM89XX_CDC_CORE_DEBUG_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL (0x100) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL (0x140) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL (0x104) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL (0x144) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL (0x108) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL (0x148) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL (0x10C) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL (0x14C) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL (0x110) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL (0x150) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL (0x114) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL (0x154) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL (0x118) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL (0x158) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL (0x11C) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL (0x15C) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_CTL (0x120) +#define MSM89XX_CDC_CORE_IIR1_CTL__POR (0x40) +#define MSM89XX_CDC_CORE_IIR2_CTL (0x160) +#define MSM89XX_CDC_CORE_IIR2_CTL__POR (0x40) +#define MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL (0x124) +#define MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL (0x164) +#define MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL (0x128) +#define MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL (0x168) +#define MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL (0x12C) +#define MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL (0x16C) +#define MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX1_B1_CTL (0x180) +#define MSM89XX_CDC_CORE_CONN_RX1_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX1_B2_CTL (0x184) +#define MSM89XX_CDC_CORE_CONN_RX1_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX1_B3_CTL (0x188) +#define MSM89XX_CDC_CORE_CONN_RX1_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX2_B1_CTL (0x18C) +#define MSM89XX_CDC_CORE_CONN_RX2_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX2_B2_CTL (0x190) +#define MSM89XX_CDC_CORE_CONN_RX2_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX2_B3_CTL (0x194) +#define MSM89XX_CDC_CORE_CONN_RX2_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX3_B1_CTL (0x198) +#define MSM89XX_CDC_CORE_CONN_RX3_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX3_B2_CTL (0x19C) +#define MSM89XX_CDC_CORE_CONN_RX3_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_TX_B1_CTL (0x1A0) +#define MSM89XX_CDC_CORE_CONN_TX_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL (0x1A8) +#define MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL (0x1AC) +#define MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL (0x1B0) +#define MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL (0x1B4) +#define MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL (0x1B8) +#define MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL (0x1BC) +#define MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL (0x1C0) +#define MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL (0x1C4) +#define MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL (0x1C8) +#define MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER (0x280) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER (0x2A0) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER (0x2C0) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER (0x2E0) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN (0x284) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN (0x2A4) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN (0x2C4) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN (0x2E4) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG (0x288) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG (0x2A8) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG (0x2C8) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG (0x2E8) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_MUX_CTL (0x28C) +#define MSM89XX_CDC_CORE_TX1_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_MUX_CTL (0x2AC) +#define MSM89XX_CDC_CORE_TX2_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_MUX_CTL (0x2CC) +#define MSM89XX_CDC_CORE_TX3_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_MUX_CTL (0x2EC) +#define MSM89XX_CDC_CORE_TX4_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_CLK_FS_CTL (0x290) +#define MSM89XX_CDC_CORE_TX1_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX2_CLK_FS_CTL (0x2B0) +#define MSM89XX_CDC_CORE_TX2_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX3_CLK_FS_CTL (0x2D0) +#define MSM89XX_CDC_CORE_TX3_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX4_CLK_FS_CTL (0x2F0) +#define MSM89XX_CDC_CORE_TX4_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX1_DMIC_CTL (0x294) +#define MSM89XX_CDC_CORE_TX1_DMIC_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_DMIC_CTL (0x2B4) +#define MSM89XX_CDC_CORE_TX2_DMIC_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_DMIC_CTL (0x2D4) +#define MSM89XX_CDC_CORE_TX3_DMIC_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_DMIC_CTL (0x2F4) +#define MSM89XX_CDC_CORE_TX4_DMIC_CTL__POR (0x00) + +#define MSM89XX_CDC_CORE_NUM_REGISTERS \ + (MSM89XX_CDC_CORE_TX4_DMIC_CTL+1) +#define MSM89XX_CDC_CORE_MAX_REGISTER \ + (MSM89XX_CDC_CORE_NUM_REGISTERS-1) +#define MSM89XX_CDC_CORE_CACHE_SIZE \ + MSM89XX_CDC_CORE_NUM_REGISTERS +#endif diff --git a/sound/soc/codecs/msm_hdmi_codec_rx.c b/sound/soc/codecs/msm_hdmi_codec_rx.c new file mode 100644 index 0000000000000000000000000000000000000000..fca4168c83df0421d35e815f623dbcf6d7aa8b67 --- /dev/null +++ b/sound/soc/codecs/msm_hdmi_codec_rx.c @@ -0,0 +1,494 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSM_EXT_DISP_PCM_RATES SNDRV_PCM_RATE_48000 + +static const char *const ext_disp_audio_type_text[] = {"None", "HDMI", "DP"}; + +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_type, ext_disp_audio_type_text); + +struct msm_ext_disp_audio_codec_rx_data { + struct platform_device *ext_disp_core_pdev; + struct msm_ext_disp_audio_codec_ops ext_disp_ops; + int cable_status; +}; + +static int msm_ext_disp_edid_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_ext_disp_audio_codec_rx_data *codec_data; + struct msm_ext_disp_audio_edid_blk edid_blk; + int rc; + + codec_data = snd_soc_codec_get_drvdata(codec); + + if (!codec_data) { + dev_err(codec->dev, "%s: codec_data is NULL\n", __func__); + return -EINVAL; + } + + if (!codec_data->ext_disp_ops.get_audio_edid_blk) { + dev_dbg(codec->dev, "%s: get_audio_edid_blk() is NULL\n", + __func__); + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 0; + return 0; + } + + rc = codec_data->ext_disp_ops.get_audio_edid_blk( + codec_data->ext_disp_core_pdev, &edid_blk); + + if (!IS_ERR_VALUE(rc)) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = edid_blk.audio_data_blk_size + + edid_blk.spk_alloc_data_blk_size; + } + + dev_dbg(codec->dev, "%s: count: %d\n", __func__, uinfo->count); + + return rc; +} + +static int msm_ext_disp_edid_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_ext_disp_audio_codec_rx_data *codec_data; + struct msm_ext_disp_audio_edid_blk edid_blk; + int rc; + + codec_data = snd_soc_codec_get_drvdata(codec); + if (!codec_data || !codec_data->ext_disp_ops.get_audio_edid_blk) { + dev_err(codec->dev, "%s: codec_data or get_audio_edid_blk() is NULL\n", + __func__); + return -EINVAL; + } + + rc = codec_data->ext_disp_ops.get_audio_edid_blk( + codec_data->ext_disp_core_pdev, &edid_blk); + if (!IS_ERR_VALUE(rc)) { + memcpy(ucontrol->value.bytes.data, + edid_blk.audio_data_blk, + edid_blk.audio_data_blk_size); + memcpy((ucontrol->value.bytes.data + + edid_blk.audio_data_blk_size), + edid_blk.spk_alloc_data_blk, + edid_blk.spk_alloc_data_blk_size); + + dev_dbg(codec->dev, "%s: data_blk_size:%d, spk_alloc_data_blk_size:%d\n", + __func__, edid_blk.audio_data_blk_size, + edid_blk.spk_alloc_data_blk_size); + } + + return rc; +} + +static int msm_ext_disp_audio_type_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_ext_disp_audio_codec_rx_data *codec_data; + enum msm_ext_disp_cable_state cable_state; + enum msm_ext_disp_type disp_type; + int rc; + + codec_data = snd_soc_codec_get_drvdata(codec); + if (!codec_data || + !codec_data->ext_disp_ops.get_audio_edid_blk || + !codec_data->ext_disp_ops.get_intf_id) { + dev_err(codec->dev, "%s: codec_data, get_audio_edid_blk() or get_intf_id is NULL\n", + __func__); + return -EINVAL; + } + + cable_state = codec_data->ext_disp_ops.cable_status( + codec_data->ext_disp_core_pdev, 1); + if (IS_ERR_VALUE(cable_state)) { + dev_err(codec->dev, "%s: Error retrieving cable state from ext_disp, err:%d\n", + __func__, cable_state); + rc = cable_state; + goto done; + } + + codec_data->cable_status = cable_state; + if (cable_state == EXT_DISPLAY_CABLE_DISCONNECT) { + dev_err(codec->dev, "%s: Display cable disconnected\n", + __func__); + ucontrol->value.integer.value[0] = 0; + rc = 0; + goto done; + } + + disp_type = codec_data->ext_disp_ops.get_intf_id( + codec_data->ext_disp_core_pdev); + if (!IS_ERR_VALUE(disp_type)) { + switch (disp_type) { + case EXT_DISPLAY_TYPE_DP: + ucontrol->value.integer.value[0] = 2; + rc = 0; + break; + case EXT_DISPLAY_TYPE_HDMI: + ucontrol->value.integer.value[0] = 1; + rc = 0; + break; + default: + rc = -EINVAL; + dev_err(codec->dev, "%s: Invalid disp_type:%d\n", + __func__, disp_type); + goto done; + } + dev_dbg(codec->dev, "%s: Display type: %d\n", + __func__, disp_type); + } else { + dev_err(codec->dev, "%s: Error retrieving disp_type from ext_disp, err:%d\n", + __func__, disp_type); + rc = disp_type; + } + +done: + return rc; +} + +static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "HDMI EDID", + .info = msm_ext_disp_edid_ctl_info, + .get = msm_ext_disp_edid_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Display Port EDID", + .info = msm_ext_disp_edid_ctl_info, + .get = msm_ext_disp_edid_get, + }, + SOC_ENUM_EXT("External Display Type", ext_disp_audio_type, + msm_ext_disp_audio_type_get, NULL), +}; + +static int msm_ext_disp_audio_codec_rx_dai_startup( + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct msm_ext_disp_audio_codec_rx_data *codec_data = + dev_get_drvdata(dai->codec->dev); + + if (!codec_data || !codec_data->ext_disp_ops.cable_status) { + dev_err(dai->dev, "%s() codec_data or cable_status is null\n", + __func__); + return -EINVAL; + } + + codec_data->cable_status = + codec_data->ext_disp_ops.cable_status( + codec_data->ext_disp_core_pdev, 1); + if (IS_ERR_VALUE(codec_data->cable_status)) { + dev_err(dai->dev, + "%s() ext disp core is not ready (ret val = %d)\n", + __func__, codec_data->cable_status); + ret = codec_data->cable_status; + } else if (!codec_data->cable_status) { + dev_err(dai->dev, + "%s() ext disp cable is not connected (ret val = %d)\n", + __func__, codec_data->cable_status); + ret = -ENODEV; + } + + return ret; +} + +static int msm_ext_disp_audio_codec_rx_dai_hw_params( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u32 channel_allocation = 0; + u32 level_shift = 0; /* 0dB */ + bool down_mix = 0; + u32 num_channels = params_channels(params); + int rc = 0; + struct msm_ext_disp_audio_setup_params audio_setup_params = {0}; + + struct msm_ext_disp_audio_codec_rx_data *codec_data = + dev_get_drvdata(dai->codec->dev); + + if (!codec_data || !codec_data->ext_disp_ops.audio_info_setup) { + dev_err(dai->dev, "%s: codec_data or audio_info_setup is null\n", + __func__); + return -EINVAL; + } + + if (IS_ERR_VALUE(codec_data->cable_status)) { + dev_err_ratelimited(dai->dev, + "%s() ext disp core is not ready (ret val = %d)\n", + __func__, codec_data->cable_status); + return codec_data->cable_status; + } else if (!codec_data->cable_status) { + dev_err_ratelimited(dai->dev, + "%s() ext disp cable is not connected (ret val = %d)\n", + __func__, codec_data->cable_status); + return -ENODEV; + } + + /*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/ + switch (num_channels) { + case 2: + channel_allocation = 0; + break; + case 3: + channel_allocation = 0x02;/*default to FL/FR/FC*/ + audio_setup_params.sample_present = 0x3; + break; + case 4: + channel_allocation = 0x06;/*default to FL/FR/FC/RC*/ + audio_setup_params.sample_present = 0x7; + break; + case 5: + channel_allocation = 0x0A;/*default to FL/FR/FC/RR/RL*/ + audio_setup_params.sample_present = 0x7; + break; + case 6: + channel_allocation = 0x0B; + audio_setup_params.sample_present = 0x7; + break; + case 7: + channel_allocation = 0x12;/*default to FL/FR/FC/RL/RR/RRC/RLC*/ + audio_setup_params.sample_present = 0xf; + break; + case 8: + channel_allocation = 0x13; + audio_setup_params.sample_present = 0xf; + break; + default: + dev_err(dai->dev, "invalid Channels = %u\n", num_channels); + return -EINVAL; + } + + dev_dbg(dai->dev, + "%s() num_ch %u samplerate %u channel_allocation = %u\n", + __func__, num_channels, params_rate(params), + channel_allocation); + + audio_setup_params.sample_rate_hz = params_rate(params); + audio_setup_params.num_of_channels = num_channels; + audio_setup_params.channel_allocation = channel_allocation; + audio_setup_params.level_shift = level_shift; + audio_setup_params.down_mix = down_mix; + + rc = codec_data->ext_disp_ops.audio_info_setup( + codec_data->ext_disp_core_pdev, &audio_setup_params); + if (IS_ERR_VALUE(rc)) { + dev_err_ratelimited(dai->dev, + "%s() ext disp core is not ready, rc: %d\n", + __func__, rc); + } + + return rc; +} + +static void msm_ext_disp_audio_codec_rx_dai_shutdown( + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int rc; + + struct msm_ext_disp_audio_codec_rx_data *codec_data = + dev_get_drvdata(dai->codec->dev); + + if (!codec_data || !codec_data->ext_disp_ops.teardown_done || + !codec_data->ext_disp_ops.cable_status) { + dev_err(dai->dev, "%s: codec data or teardown_done or cable_status is null\n", + __func__); + return; + } + + rc = codec_data->ext_disp_ops.cable_status( + codec_data->ext_disp_core_pdev, 0); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, + "%s: ext disp core had problems releasing audio flag\n", + __func__); + } + + codec_data->ext_disp_ops.teardown_done( + codec_data->ext_disp_core_pdev); +} + +static int msm_ext_disp_audio_codec_rx_probe(struct snd_soc_codec *codec) +{ + struct msm_ext_disp_audio_codec_rx_data *codec_data; + struct device_node *of_node_parent = NULL; + + codec_data = kzalloc(sizeof(struct msm_ext_disp_audio_codec_rx_data), + GFP_KERNEL); + + if (!codec_data) { + dev_err(codec->dev, "%s(): fail to allocate dai data\n", + __func__); + return -ENOMEM; + } + + of_node_parent = of_get_parent(codec->dev->of_node); + if (!of_node_parent) { + dev_err(codec->dev, "%s(): Parent device tree node not found\n", + __func__); + kfree(codec_data); + return -ENODEV; + } + + codec_data->ext_disp_core_pdev = of_find_device_by_node(of_node_parent); + if (!codec_data->ext_disp_core_pdev) { + dev_err(codec->dev, "%s(): can't get parent pdev\n", __func__); + kfree(codec_data); + return -ENODEV; + } + + if (msm_ext_disp_register_audio_codec(codec_data->ext_disp_core_pdev, + &codec_data->ext_disp_ops)) { + dev_err(codec->dev, "%s(): can't register with ext disp core", + __func__); + kfree(codec_data); + return -ENODEV; + } + + dev_set_drvdata(codec->dev, codec_data); + + dev_dbg(codec->dev, "%s(): registered %s with ext disp core\n", + __func__, codec->component.name); + + return 0; +} + +static int msm_ext_disp_audio_codec_rx_remove(struct snd_soc_codec *codec) +{ + struct msm_ext_disp_audio_codec_rx_data *codec_data; + + codec_data = dev_get_drvdata(codec->dev); + kfree(codec_data); + + return 0; +} + +static struct snd_soc_dai_ops msm_ext_disp_audio_codec_rx_dai_ops = { + .startup = msm_ext_disp_audio_codec_rx_dai_startup, + .hw_params = msm_ext_disp_audio_codec_rx_dai_hw_params, + .shutdown = msm_ext_disp_audio_codec_rx_dai_shutdown +}; + +static struct snd_soc_dai_driver msm_ext_disp_audio_codec_rx_dais[] = { + { + .name = "msm_hdmi_audio_codec_rx_dai", + .playback = { + .stream_name = "HDMI Playback", + .channels_min = 1, + .channels_max = 8, + .rate_min = 48000, + .rate_max = 48000, + .rates = MSM_EXT_DISP_PCM_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &msm_ext_disp_audio_codec_rx_dai_ops, + }, + { + .name = "msm_dp_audio_codec_rx_dai", + .playback = { + .stream_name = "Display Port Playback", + .channels_min = 1, + .channels_max = 8, + .rate_min = 48000, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &msm_ext_disp_audio_codec_rx_dai_ops, + }, +}; + +static struct snd_soc_codec_driver msm_ext_disp_audio_codec_rx_soc_driver = { + .probe = msm_ext_disp_audio_codec_rx_probe, + .remove = msm_ext_disp_audio_codec_rx_remove, + .controls = msm_ext_disp_codec_rx_controls, + .num_controls = ARRAY_SIZE(msm_ext_disp_codec_rx_controls), +}; + +static int msm_ext_disp_audio_codec_rx_plat_probe( + struct platform_device *pdev) +{ + dev_dbg(&pdev->dev, "%s(): dev name %s\n", __func__, + dev_name(&pdev->dev)); + + return snd_soc_register_codec(&pdev->dev, + &msm_ext_disp_audio_codec_rx_soc_driver, + msm_ext_disp_audio_codec_rx_dais, + ARRAY_SIZE(msm_ext_disp_audio_codec_rx_dais)); +} + +static int msm_ext_disp_audio_codec_rx_plat_remove( + struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} +static const struct of_device_id msm_ext_disp_audio_codec_rx_dt_match[] = { + { .compatible = "qcom,msm-ext-disp-audio-codec-rx", }, + {} +}; +MODULE_DEVICE_TABLE(of, msm_ext_disp_audio_codec_rx_dt_match); + +static struct platform_driver msm_ext_disp_audio_codec_rx_driver = { + .driver = { + .name = "msm-ext-disp-audio-codec-rx", + .owner = THIS_MODULE, + .of_match_table = msm_ext_disp_audio_codec_rx_dt_match, + }, + .probe = msm_ext_disp_audio_codec_rx_plat_probe, + .remove = msm_ext_disp_audio_codec_rx_plat_remove, +}; + +static int __init msm_ext_disp_audio_codec_rx_init(void) +{ + int rc; + + rc = platform_driver_register(&msm_ext_disp_audio_codec_rx_driver); + if (rc) { + pr_err("%s: failed to register ext disp codec driver err:%d\n", + __func__, rc); + } + + return rc; +} +module_init(msm_ext_disp_audio_codec_rx_init); + +static void __exit msm_ext_disp_audio_codec_rx_exit(void) +{ + platform_driver_unregister(&msm_ext_disp_audio_codec_rx_driver); +} +module_exit(msm_ext_disp_audio_codec_rx_exit); + +MODULE_DESCRIPTION("MSM External Display Audio CODEC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/msm_stub.c b/sound/soc/codecs/msm_stub.c new file mode 100644 index 0000000000000000000000000000000000000000..68e55ae1da16d1c618204d47bc5bfea2e04275e9 --- /dev/null +++ b/sound/soc/codecs/msm_stub.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2011-2014, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include + +/* A dummy driver useful only to advertise hardware parameters */ +static struct snd_soc_dai_driver msm_stub_dais[] = { + { + .name = "msm-stub-rx", + .playback = { /* Support maximum range */ + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, + { + .name = "msm-stub-tx", + .capture = { /* Support maximum range */ + .stream_name = "Record", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + }, +}; + +static struct snd_soc_codec_driver soc_msm_stub = {}; + +static int msm_stub_dev_probe(struct platform_device *pdev) +{ + dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev)); + + return snd_soc_register_codec(&pdev->dev, + &soc_msm_stub, msm_stub_dais, ARRAY_SIZE(msm_stub_dais)); +} + +static int msm_stub_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} +static const struct of_device_id msm_stub_codec_dt_match[] = { + { .compatible = "qcom,msm-stub-codec", }, + {} +}; + +static struct platform_driver msm_stub_driver = { + .driver = { + .name = "msm-stub-codec", + .owner = THIS_MODULE, + .of_match_table = msm_stub_codec_dt_match, + }, + .probe = msm_stub_dev_probe, + .remove = msm_stub_dev_remove, +}; + +static int __init msm_stub_init(void) +{ + return platform_driver_register(&msm_stub_driver); +} +module_init(msm_stub_init); + +static void __exit msm_stub_exit(void) +{ + platform_driver_unregister(&msm_stub_driver); +} +module_exit(msm_stub_exit); + +MODULE_DESCRIPTION("Generic MSM CODEC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c new file mode 100644 index 0000000000000000000000000000000000000000..f51301d1ab0804d3a309de6bd4bf09a18f83bdbd --- /dev/null +++ b/sound/soc/codecs/wcd-dsp-mgr.c @@ -0,0 +1,1155 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd-dsp-utils.h" + +/* Forward declarations */ +static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type); + +/* Component related macros */ +#define WDSP_GET_COMPONENT(wdsp, x) (&(wdsp->cmpnts[x])) +#define WDSP_GET_CMPNT_TYPE_STR(x) wdsp_get_cmpnt_type_string(x) + +/* + * These #defines indicate the bit number in status field + * for each of the status. If bit is set, it indicates + * the status as done, else if bit is not set, it indicates + * the status is either failed or not done. + */ +#define WDSP_STATUS_INITIALIZED BIT(0) +#define WDSP_STATUS_CODE_DLOADED BIT(1) +#define WDSP_STATUS_DATA_DLOADED BIT(2) +#define WDSP_STATUS_BOOTED BIT(3) + +/* Helper macros for printing wdsp messages */ +#define WDSP_ERR(wdsp, fmt, ...) \ + dev_err(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__) +#define WDSP_DBG(wdsp, fmt, ...) \ + dev_dbg(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__) + +/* Helper macros for locking */ +#define WDSP_MGR_MUTEX_LOCK(wdsp, lock) \ +{ \ + WDSP_DBG(wdsp, "mutex_lock(%s)", \ + __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WDSP_MGR_MUTEX_UNLOCK(wdsp, lock) \ +{ \ + WDSP_DBG(wdsp, "mutex_unlock(%s)", \ + __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +/* Helper macros for using status mask */ +#define WDSP_SET_STATUS(wdsp, state) \ +{ \ + wdsp->status |= state; \ + WDSP_DBG(wdsp, "set 0x%lx, new_state = 0x%x", \ + state, wdsp->status); \ +} + +#define WDSP_CLEAR_STATUS(wdsp, state) \ +{ \ + wdsp->status &= (~state); \ + WDSP_DBG(wdsp, "clear 0x%lx, new_state = 0x%x", \ + state, wdsp->status); \ +} + +#define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state) + +/* SSR relate status macros */ +#define WDSP_SSR_STATUS_WDSP_READY BIT(0) +#define WDSP_SSR_STATUS_CDC_READY BIT(1) +#define WDSP_SSR_STATUS_READY \ + (WDSP_SSR_STATUS_WDSP_READY | WDSP_SSR_STATUS_CDC_READY) +#define WDSP_SSR_READY_WAIT_TIMEOUT (10 * HZ) + +enum wdsp_ssr_type { + + /* Init value, indicates there is no SSR in progress */ + WDSP_SSR_TYPE_NO_SSR = 0, + + /* + * Indicates WDSP crashed. The manager driver internally + * decides when to perform WDSP restart based on the + * users of wdsp. Hence there is no explicit WDSP_UP. + */ + WDSP_SSR_TYPE_WDSP_DOWN, + + /* Indicates codec hardware is down */ + WDSP_SSR_TYPE_CDC_DOWN, + + /* Indicates codec hardware is up, trigger to restart WDSP */ + WDSP_SSR_TYPE_CDC_UP, +}; + +struct wdsp_cmpnt { + + /* OF node of the phandle */ + struct device_node *np; + + /* + * Child component's dev_name, should be set in DT for the child's + * phandle if child's dev->of_node does not match the phandle->of_node + */ + const char *cdev_name; + + /* Child component's device node */ + struct device *cdev; + + /* Private data that component may want back on callbacks */ + void *priv_data; + + /* Child ops */ + struct wdsp_cmpnt_ops *ops; +}; + +struct wdsp_ramdump_data { + + /* Ramdump device */ + void *rd_dev; + + /* DMA address of the dump */ + dma_addr_t rd_addr; + + /* Virtual address of the dump */ + void *rd_v_addr; + + /* Data provided through error interrupt */ + struct wdsp_err_signal_arg err_data; +}; + +struct wdsp_mgr_priv { + + /* Manager driver's struct device pointer */ + struct device *mdev; + + /* Match struct for component framework */ + struct component_match *match; + + /* Manager's ops/function callbacks */ + struct wdsp_mgr_ops *ops; + + /* Array to store information for all expected components */ + struct wdsp_cmpnt cmpnts[WDSP_CMPNT_TYPE_MAX]; + + /* The filename of image to be downloaded */ + const char *img_fname; + + /* Keeps track of current state of manager driver */ + u32 status; + + /* Work to load the firmware image after component binding */ + struct work_struct load_fw_work; + + /* List of segments in image to be downloaded */ + struct list_head *seg_list; + + /* Base address of the image in memory */ + u32 base_addr; + + /* Instances using dsp */ + int dsp_users; + + /* Lock for serializing ops called by components */ + struct mutex api_mutex; + + struct wdsp_ramdump_data dump_data; + + /* SSR related */ + enum wdsp_ssr_type ssr_type; + struct mutex ssr_mutex; + struct work_struct ssr_work; + u16 ready_status; + struct completion ready_compl; +}; + +static char *wdsp_get_ssr_type_string(enum wdsp_ssr_type type) +{ + switch (type) { + case WDSP_SSR_TYPE_NO_SSR: + return "NO_SSR"; + case WDSP_SSR_TYPE_WDSP_DOWN: + return "WDSP_DOWN"; + case WDSP_SSR_TYPE_CDC_DOWN: + return "CDC_DOWN"; + case WDSP_SSR_TYPE_CDC_UP: + return "CDC_UP"; + default: + pr_err("%s: Invalid ssr_type %d\n", + __func__, type); + return "Invalid"; + } +} + +static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type) +{ + switch (type) { + case WDSP_CMPNT_CONTROL: + return "control"; + case WDSP_CMPNT_IPC: + return "ipc"; + case WDSP_CMPNT_TRANSPORT: + return "transport"; + default: + pr_err("%s: Invalid component type %d\n", + __func__, type); + return "Invalid"; + } +} + +static void __wdsp_clr_ready_locked(struct wdsp_mgr_priv *wdsp, + u16 value) +{ + wdsp->ready_status &= ~(value); + WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status); +} + +static void __wdsp_set_ready_locked(struct wdsp_mgr_priv *wdsp, + u16 value, bool mark_complete) +{ + wdsp->ready_status |= value; + WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status); + + if (mark_complete && + wdsp->ready_status == WDSP_SSR_STATUS_READY) { + WDSP_DBG(wdsp, "marking ready completion"); + complete(&wdsp->ready_compl); + } +} + +static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) + cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } +} + +static void wdsp_broadcast_event_downseq(struct wdsp_mgr_priv *wdsp, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) + cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } +} + +static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp, + enum wdsp_cmpnt_type type, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int ret; + + cmpnt = WDSP_GET_COMPONENT(wdsp, type); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) { + ret = cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } else { + WDSP_ERR(wdsp, "not valid event_handler for %s", + WDSP_GET_CMPNT_TYPE_STR(type)); + ret = -EINVAL; + } + + return ret; +} + +static void wdsp_deinit_components(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->deinit) + cmpnt->ops->deinit(cmpnt->cdev, cmpnt->priv_data); + } +} + +static int wdsp_init_components(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_cmpnt *cmpnt; + int fail_idx = WDSP_CMPNT_TYPE_MAX; + int i, ret = 0; + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + + /* Init is allowed to be NULL */ + if (!cmpnt->ops || !cmpnt->ops->init) + continue; + ret = cmpnt->ops->init(cmpnt->cdev, cmpnt->priv_data); + if (ret) { + WDSP_ERR(wdsp, "Init failed (%d) for component %s", + ret, WDSP_GET_CMPNT_TYPE_STR(i)); + fail_idx = i; + break; + } + } + + if (fail_idx < WDSP_CMPNT_TYPE_MAX) { + /* Undo init for already initialized components */ + for (i = fail_idx - 1; i >= 0; i--) { + struct wdsp_cmpnt *cmpnt = WDSP_GET_COMPONENT(wdsp, i); + + if (cmpnt->ops && cmpnt->ops->deinit) + cmpnt->ops->deinit(cmpnt->cdev, + cmpnt->priv_data); + } + } else { + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_INIT, NULL); + } + + return ret; +} + +static int wdsp_load_each_segment(struct wdsp_mgr_priv *wdsp, + struct wdsp_img_segment *seg) +{ + struct wdsp_img_section img_section; + int ret; + + WDSP_DBG(wdsp, + "base_addr 0x%x, split_fname %s, load_addr 0x%x, size 0x%zx", + wdsp->base_addr, seg->split_fname, seg->load_addr, seg->size); + + if (seg->load_addr < wdsp->base_addr) { + WDSP_ERR(wdsp, "Invalid addr 0x%x, base_addr = 0x%x", + seg->load_addr, wdsp->base_addr); + return -EINVAL; + } + + img_section.addr = seg->load_addr - wdsp->base_addr; + img_section.size = seg->size; + img_section.data = seg->data; + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT, + WDSP_EVENT_DLOAD_SECTION, + &img_section); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, + "Failed, err = %d for base_addr = 0x%x split_fname = %s, load_addr = 0x%x, size = 0x%zx", + ret, wdsp->base_addr, seg->split_fname, + seg->load_addr, seg->size); + return ret; +} + +static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, + unsigned int type) +{ + struct wdsp_cmpnt *ctl; + struct wdsp_img_segment *seg = NULL; + enum wdsp_event_type pre, post; + long status; + int ret; + + ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL); + + if (type == WDSP_ELF_FLAG_RE) { + pre = WDSP_EVENT_PRE_DLOAD_CODE; + post = WDSP_EVENT_POST_DLOAD_CODE; + status = WDSP_STATUS_CODE_DLOADED; + } else if (type == WDSP_ELF_FLAG_WRITE) { + pre = WDSP_EVENT_PRE_DLOAD_DATA; + post = WDSP_EVENT_POST_DLOAD_DATA; + status = WDSP_STATUS_DATA_DLOADED; + } else { + WDSP_ERR(wdsp, "Invalid type %u", type); + return -EINVAL; + } + + ret = wdsp_get_segment_list(ctl->cdev, wdsp->img_fname, + type, wdsp->seg_list, &wdsp->base_addr); + if (IS_ERR_VALUE(ret) || + list_empty(wdsp->seg_list)) { + WDSP_ERR(wdsp, "Error %d to get image segments for type %d", + ret, type); + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED, + NULL); + goto done; + } + + /* Notify all components that image is about to be downloaded */ + wdsp_broadcast_event_upseq(wdsp, pre, NULL); + + /* Go through the list of segments and download one by one */ + list_for_each_entry(seg, wdsp->seg_list, list) { + ret = wdsp_load_each_segment(wdsp, seg); + if (IS_ERR_VALUE(ret)) { + wdsp_broadcast_event_downseq(wdsp, + WDSP_EVENT_DLOAD_FAILED, + NULL); + goto dload_error; + } + } + + WDSP_SET_STATUS(wdsp, status); + + /* Notify all components that image is downloaded */ + wdsp_broadcast_event_downseq(wdsp, post, NULL); + +dload_error: + wdsp_flush_segment_list(wdsp->seg_list); +done: + return ret; +} + +static int wdsp_init_and_dload_code_sections(struct wdsp_mgr_priv *wdsp) +{ + int ret; + bool is_initialized; + + is_initialized = WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_INITIALIZED); + + if (!is_initialized) { + /* Components are not initialized yet, initialize them */ + ret = wdsp_init_components(wdsp); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "INIT failed, err = %d", ret); + goto done; + } + WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + } + + /* Download the read-execute sections of image */ + ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Error %d to download code sections", ret); + goto done; + } +done: + return ret; +} + +static void wdsp_load_fw_image(struct work_struct *work) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work); + if (!wdsp) { + pr_err("%s: Invalid private_data\n", __func__); + return; + } + + ret = wdsp_init_and_dload_code_sections(wdsp); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "dload code sections failed, err = %d", ret); +} + +static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) +{ + int ret; + + /* Make sure wdsp is in good state */ + if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_CODE_DLOADED)) { + WDSP_ERR(wdsp, "WDSP in invalid state 0x%x", wdsp->status); + ret = -EINVAL; + goto done; + } + + /* Download the read-write sections of image */ + ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_WRITE); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Data section download failed, err = %d", ret); + goto done; + } + + wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL); + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_BOOT, NULL); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to boot dsp, err = %d", ret); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_BOOTUP, NULL); + WDSP_SET_STATUS(wdsp, WDSP_STATUS_BOOTED); +done: + return ret; +} + +static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp) +{ + int ret; + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + /* + * If Disable happened while SSR is in progress, then set the SSR + * ready status indicating WDSP is now ready. Ignore the disable + * event here and let the SSR handler go through shutdown. + */ + if (wdsp->ssr_type != WDSP_SSR_TYPE_NO_SSR) { + __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY, true); + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + return 0; + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + + /* Make sure wdsp is in good state */ + if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { + WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status); + ret = -EINVAL; + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, NULL); + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_SHUTDOWN, NULL); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to shutdown dsp, err = %d", ret); + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED); + + /* Data sections are to be downloaded per boot */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); +done: + return ret; +} + +static int wdsp_register_cmpnt_ops(struct device *wdsp_dev, + struct device *cdev, + void *priv_data, + struct wdsp_cmpnt_ops *ops) +{ + struct wdsp_mgr_priv *wdsp; + struct wdsp_cmpnt *cmpnt; + int i, ret; + + if (!wdsp_dev || !cdev || !ops) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if ((cdev->of_node && cdev->of_node == cmpnt->np) || + (cmpnt->cdev_name && + !strcmp(dev_name(cdev), cmpnt->cdev_name))) { + break; + } + } + + if (i == WDSP_CMPNT_TYPE_MAX) { + WDSP_ERR(wdsp, "Failed to register component dev %s", + dev_name(cdev)); + ret = -EINVAL; + goto done; + } + + cmpnt->cdev = cdev; + cmpnt->ops = ops; + cmpnt->priv_data = priv_data; +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + return 0; +} + +static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev, + enum wdsp_cmpnt_type type) +{ + struct wdsp_mgr_priv *wdsp; + struct wdsp_cmpnt *cmpnt; + + if (!wdsp_dev || type >= WDSP_CMPNT_TYPE_MAX) + return NULL; + + wdsp = dev_get_drvdata(wdsp_dev); + cmpnt = WDSP_GET_COMPONENT(wdsp, type); + + return cmpnt->cdev; +} + +static void wdsp_collect_ramdumps(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_img_section img_section; + struct wdsp_err_signal_arg *data = &wdsp->dump_data.err_data; + struct ramdump_segment rd_seg; + int ret = 0; + + if (wdsp->ssr_type != WDSP_SSR_TYPE_WDSP_DOWN || + !data->mem_dumps_enabled) { + WDSP_DBG(wdsp, "cannot dump memory, ssr_type %s, dumps %s", + wdsp_get_ssr_type_string(wdsp->ssr_type), + !(data->mem_dumps_enabled) ? "disabled" : "enabled"); + goto done; + } + + if (data->dump_size == 0 || + data->remote_start_addr < wdsp->base_addr) { + WDSP_ERR(wdsp, "Invalid start addr 0x%x or dump_size 0x%zx", + data->remote_start_addr, data->dump_size); + goto done; + } + + if (!wdsp->dump_data.rd_dev) { + WDSP_ERR(wdsp, "Ramdump device is not setup"); + goto done; + } + + WDSP_DBG(wdsp, "base_addr 0x%x, dump_start_addr 0x%x, dump_size 0x%zx", + wdsp->base_addr, data->remote_start_addr, data->dump_size); + + /* Allocate memory for dumps */ + wdsp->dump_data.rd_v_addr = dma_alloc_coherent(wdsp->mdev, + data->dump_size, + &wdsp->dump_data.rd_addr, + GFP_KERNEL); + if (!wdsp->dump_data.rd_v_addr) + goto done; + + img_section.addr = data->remote_start_addr - wdsp->base_addr; + img_section.size = data->dump_size; + img_section.data = wdsp->dump_data.rd_v_addr; + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT, + WDSP_EVENT_READ_SECTION, + &img_section); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to read dumps, size 0x%zx at addr 0x%x", + img_section.size, img_section.addr); + goto err_read_dumps; + } + + rd_seg.address = (unsigned long) wdsp->dump_data.rd_v_addr; + rd_seg.size = img_section.size; + rd_seg.v_address = wdsp->dump_data.rd_v_addr; + + ret = do_ramdump(wdsp->dump_data.rd_dev, &rd_seg, 1); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "do_ramdump failed with error %d", ret); + +err_read_dumps: + dma_free_coherent(wdsp->mdev, data->dump_size, + wdsp->dump_data.rd_v_addr, wdsp->dump_data.rd_addr); +done: + return; +} + +static void wdsp_ssr_work_fn(struct work_struct *work) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + wdsp = container_of(work, struct wdsp_mgr_priv, ssr_work); + if (!wdsp) { + pr_err("%s: Invalid private_data\n", __func__); + return; + } + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + /* Issue ramdumps and shutdown only if DSP is currently booted */ + if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { + wdsp_collect_ramdumps(wdsp); + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_SHUTDOWN, NULL); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "Failed WDSP shutdown, err = %d", ret); + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, + NULL); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED); + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + ret = wait_for_completion_timeout(&wdsp->ready_compl, + WDSP_SSR_READY_WAIT_TIMEOUT); + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + if (ret == 0) { + WDSP_ERR(wdsp, "wait_for_ready timed out, status = 0x%x", + wdsp->ready_status); + goto done; + } + + /* Data sections are to downloaded per WDSP boot */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); + + /* + * Even though code section could possible be retained on DSP + * crash, go ahead and still re-download just to avoid any + * memory corruption from previous crash. + */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED); + + /* If codec restarted, then all components must be re-initialized */ + if (wdsp->ssr_type == WDSP_SSR_TYPE_CDC_UP) { + wdsp_deinit_components(wdsp); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + } + + ret = wdsp_init_and_dload_code_sections(wdsp); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to dload code sections err = %d", + ret); + goto done; + } + + /* SSR handling is finished, mark SSR type as NO_SSR */ + wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); +} + +static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg, + enum wdsp_ssr_type ssr_type) +{ + enum wdsp_ssr_type current_ssr_type; + struct wdsp_err_signal_arg *err_data; + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + current_ssr_type = wdsp->ssr_type; + WDSP_DBG(wdsp, "Current ssr_type %s, handling ssr_type %s", + wdsp_get_ssr_type_string(current_ssr_type), + wdsp_get_ssr_type_string(ssr_type)); + wdsp->ssr_type = ssr_type; + + if (arg) { + err_data = (struct wdsp_err_signal_arg *) arg; + memcpy(&wdsp->dump_data.err_data, err_data, + sizeof(*err_data)); + } else { + memset(&wdsp->dump_data.err_data, 0, + sizeof(wdsp->dump_data.err_data)); + } + + switch (ssr_type) { + + case WDSP_SSR_TYPE_WDSP_DOWN: + __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY); + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, + NULL); + schedule_work(&wdsp->ssr_work); + break; + + case WDSP_SSR_TYPE_CDC_DOWN: + __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY); + /* + * If DSP is booted when CDC_DOWN is received, it needs + * to be shutdown. + */ + if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { + __wdsp_clr_ready_locked(wdsp, + WDSP_SSR_STATUS_WDSP_READY); + wdsp_broadcast_event_downseq(wdsp, + WDSP_EVENT_PRE_SHUTDOWN, + NULL); + } + + schedule_work(&wdsp->ssr_work); + break; + + case WDSP_SSR_TYPE_CDC_UP: + __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY, true); + break; + + default: + WDSP_ERR(wdsp, "undefined ssr_type %d\n", ssr_type); + /* Revert back the ssr_type for undefined events */ + wdsp->ssr_type = current_ssr_type; + break; + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + + return 0; +} + +static int wdsp_signal_handler(struct device *wdsp_dev, + enum wdsp_signal signal, void *arg) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + if (!wdsp_dev) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + + WDSP_DBG(wdsp, "Raised signal %d", signal); + + switch (signal) { + case WDSP_IPC1_INTR: + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC, + WDSP_EVENT_IPC1_INTR, NULL); + break; + case WDSP_ERR_INTR: + ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_WDSP_DOWN); + break; + case WDSP_CDC_DOWN_SIGNAL: + ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_DOWN); + break; + case WDSP_CDC_UP_SIGNAL: + ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_UP); + break; + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "handling signal %d failed with error %d", + signal, ret); + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + + return ret; +} + +static int wdsp_vote_for_dsp(struct device *wdsp_dev, + bool vote) +{ + struct wdsp_mgr_priv *wdsp; + int ret = 0; + + if (!wdsp_dev) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + WDSP_DBG(wdsp, "request %s, current users = %d", + vote ? "enable" : "disable", wdsp->dsp_users); + + if (vote) { + wdsp->dsp_users++; + if (wdsp->dsp_users == 1) + ret = wdsp_enable_dsp(wdsp); + } else { + if (wdsp->dsp_users == 0) + goto done; + + wdsp->dsp_users--; + if (wdsp->dsp_users == 0) + ret = wdsp_disable_dsp(wdsp); + } + + if (IS_ERR_VALUE(ret)) + WDSP_DBG(wdsp, "wdsp %s failed, err = %d", + vote ? "enable" : "disable", ret); + +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + return ret; +} + +static int wdsp_suspend(struct device *wdsp_dev) +{ + return 0; +} + +static int wdsp_resume(struct device *wdsp_dev) +{ + return 0; +} + +static struct wdsp_mgr_ops wdsp_ops = { + .register_cmpnt_ops = wdsp_register_cmpnt_ops, + .get_dev_for_cmpnt = wdsp_get_dev_for_cmpnt, + .signal_handler = wdsp_signal_handler, + .vote_for_dsp = wdsp_vote_for_dsp, + .suspend = wdsp_suspend, + .resume = wdsp_resume, +}; + +static int wdsp_mgr_compare_of(struct device *dev, void *data) +{ + struct wdsp_cmpnt *cmpnt = data; + + /* + * First try to match based on of_node, if of_node is not + * present, try to match on the dev_name + */ + return ((dev->of_node && dev->of_node == cmpnt->np) || + (cmpnt->cdev_name && + !strcmp(dev_name(dev), cmpnt->cdev_name))); +} + +static int wdsp_mgr_bind(struct device *dev) +{ + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev); + struct wdsp_cmpnt *cmpnt; + int ret, idx; + + wdsp->ops = &wdsp_ops; + + /* Setup ramdump device */ + wdsp->dump_data.rd_dev = create_ramdump_device("wdsp", dev); + if (!wdsp->dump_data.rd_dev) + dev_info(dev, "%s: create_ramdump_device failed\n", __func__); + + ret = component_bind_all(dev, wdsp->ops); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret); + + /* Make sure all components registered ops */ + for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, idx); + if (!cmpnt->cdev || !cmpnt->ops) { + WDSP_ERR(wdsp, "%s did not register ops\n", + WDSP_GET_CMPNT_TYPE_STR(idx)); + ret = -EINVAL; + component_unbind_all(dev, wdsp->ops); + break; + } + } + + /* Schedule the work to download image if binding was successful. */ + if (!ret) + schedule_work(&wdsp->load_fw_work); + + return ret; +} + +static void wdsp_mgr_unbind(struct device *dev) +{ + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev); + struct wdsp_cmpnt *cmpnt; + int idx; + + component_unbind_all(dev, wdsp->ops); + + if (wdsp->dump_data.rd_dev) { + destroy_ramdump_device(wdsp->dump_data.rd_dev); + wdsp->dump_data.rd_dev = NULL; + } + + /* Clear all status bits */ + wdsp->status = 0x00; + + /* clean up the components */ + for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, idx); + cmpnt->cdev = NULL; + cmpnt->ops = NULL; + cmpnt->priv_data = NULL; + } +} + +static const struct component_master_ops wdsp_master_ops = { + .bind = wdsp_mgr_bind, + .unbind = wdsp_mgr_unbind, +}; + +static void *wdsp_mgr_parse_phandle(struct wdsp_mgr_priv *wdsp, + int index) +{ + struct device *mdev = wdsp->mdev; + struct device_node *np; + struct wdsp_cmpnt *cmpnt = NULL; + struct of_phandle_args pargs; + u32 value; + int ret; + + ret = of_parse_phandle_with_fixed_args(mdev->of_node, + "qcom,wdsp-components", 1, + index, &pargs); + if (ret) { + WDSP_ERR(wdsp, "parse_phandle at index %d failed %d", + index, ret); + return NULL; + } + + np = pargs.np; + value = pargs.args[0]; + + if (value >= WDSP_CMPNT_TYPE_MAX) { + WDSP_ERR(wdsp, "invalid phandle_arg to of_node %s", np->name); + goto done; + } + + cmpnt = WDSP_GET_COMPONENT(wdsp, value); + if (cmpnt->np || cmpnt->cdev_name) { + WDSP_ERR(wdsp, "cmpnt %d already added", value); + cmpnt = NULL; + goto done; + } + + cmpnt->np = np; + of_property_read_string(np, "qcom,wdsp-cmpnt-dev-name", + &cmpnt->cdev_name); +done: + of_node_put(np); + return cmpnt; +} + +static int wdsp_mgr_parse_dt_entries(struct wdsp_mgr_priv *wdsp) +{ + struct device *dev = wdsp->mdev; + void *match_data; + int ph_idx, ret; + + ret = of_property_read_string(dev->of_node, "qcom,img-filename", + &wdsp->img_fname); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Reading property %s failed, error = %d", + "qcom,img-filename", ret); + return ret; + } + + ret = of_count_phandle_with_args(dev->of_node, + "qcom,wdsp-components", + NULL); + if (ret == -ENOENT) { + WDSP_ERR(wdsp, "Property %s not defined in DT", + "qcom,wdsp-components"); + goto done; + } else if (ret != WDSP_CMPNT_TYPE_MAX * 2) { + WDSP_ERR(wdsp, "Invalid phandle + arg count %d, expected %d", + ret, WDSP_CMPNT_TYPE_MAX * 2); + ret = -EINVAL; + goto done; + } + + ret = 0; + + for (ph_idx = 0; ph_idx < WDSP_CMPNT_TYPE_MAX; ph_idx++) { + + match_data = wdsp_mgr_parse_phandle(wdsp, ph_idx); + if (!match_data) { + WDSP_ERR(wdsp, "component not found at idx %d", ph_idx); + ret = -EINVAL; + goto done; + } + + component_match_add(dev, &wdsp->match, + wdsp_mgr_compare_of, match_data); + } + +done: + return ret; +} + +static int wdsp_mgr_probe(struct platform_device *pdev) +{ + struct wdsp_mgr_priv *wdsp; + struct device *mdev = &pdev->dev; + int ret; + + wdsp = devm_kzalloc(mdev, sizeof(*wdsp), GFP_KERNEL); + if (!wdsp) + return -ENOMEM; + wdsp->mdev = mdev; + wdsp->seg_list = devm_kzalloc(mdev, sizeof(struct list_head), + GFP_KERNEL); + if (!wdsp->seg_list) { + devm_kfree(mdev, wdsp); + return -ENOMEM; + } + + ret = wdsp_mgr_parse_dt_entries(wdsp); + if (ret) + goto err_dt_parse; + + INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image); + INIT_LIST_HEAD(wdsp->seg_list); + mutex_init(&wdsp->api_mutex); + mutex_init(&wdsp->ssr_mutex); + wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; + wdsp->ready_status = WDSP_SSR_STATUS_READY; + INIT_WORK(&wdsp->ssr_work, wdsp_ssr_work_fn); + init_completion(&wdsp->ready_compl); + arch_setup_dma_ops(wdsp->mdev, 0, 0, NULL, 0); + dev_set_drvdata(mdev, wdsp); + + ret = component_master_add_with_match(mdev, &wdsp_master_ops, + wdsp->match); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to add master, err = %d", ret); + goto err_master_add; + } + + return 0; + +err_master_add: + mutex_destroy(&wdsp->api_mutex); + mutex_destroy(&wdsp->ssr_mutex); +err_dt_parse: + devm_kfree(mdev, wdsp->seg_list); + devm_kfree(mdev, wdsp); + dev_set_drvdata(mdev, NULL); + + return ret; +} + +static int wdsp_mgr_remove(struct platform_device *pdev) +{ + struct device *mdev = &pdev->dev; + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(mdev); + + component_master_del(mdev, &wdsp_master_ops); + + mutex_destroy(&wdsp->api_mutex); + mutex_destroy(&wdsp->ssr_mutex); + devm_kfree(mdev, wdsp->seg_list); + devm_kfree(mdev, wdsp); + dev_set_drvdata(mdev, NULL); + + return 0; +}; + +static const struct of_device_id wdsp_mgr_dt_match[] = { + {.compatible = "qcom,wcd-dsp-mgr" }, + { } +}; + +static struct platform_driver wdsp_mgr_driver = { + .driver = { + .name = "wcd-dsp-mgr", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(wdsp_mgr_dt_match), + }, + .probe = wdsp_mgr_probe, + .remove = wdsp_mgr_remove, +}; +module_platform_driver(wdsp_mgr_driver); + +MODULE_DESCRIPTION("WCD DSP manager driver"); +MODULE_DEVICE_TABLE(of, wdsp_mgr_dt_match); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd-dsp-utils.c b/sound/soc/codecs/wcd-dsp-utils.c new file mode 100644 index 0000000000000000000000000000000000000000..1f048917a6c8afc2d6fff13b6d6b8a8ca5e843e8 --- /dev/null +++ b/sound/soc/codecs/wcd-dsp-utils.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include "wcd-dsp-utils.h" + +static bool wdsp_is_valid_elf_hdr(const struct elf32_hdr *ehdr, + size_t fw_size) +{ + if (fw_size < sizeof(*ehdr)) { + pr_err("%s: Firmware too small\n", __func__); + goto elf_check_fail; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { + pr_err("%s: Not an ELF file\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { + pr_err("%s: Not an executable image\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_phnum == 0) { + pr_err("%s: no segments to load\n", __func__); + goto elf_check_fail; + } + + if (sizeof(struct elf32_phdr) * ehdr->e_phnum + + sizeof(struct elf32_hdr) > fw_size) { + pr_err("%s: Too small MDT file\n", __func__); + goto elf_check_fail; + } + + return true; + +elf_check_fail: + return false; +} + +static int wdsp_add_segment_to_list(struct device *dev, + const char *img_fname, + const struct elf32_phdr *phdr, + int phdr_idx, + struct list_head *seg_list) +{ + struct wdsp_img_segment *seg; + int ret = 0; + + /* Do not load segments with zero size */ + if (phdr->p_filesz == 0 || phdr->p_memsz == 0) + goto done; + + seg = kzalloc(sizeof(*seg), GFP_KERNEL); + if (!seg) { + ret = -ENOMEM; + goto done; + } + + snprintf(seg->split_fname, sizeof(seg->split_fname), + "%s.b%02d", img_fname, phdr_idx); + ret = request_firmware(&seg->split_fw, seg->split_fname, dev); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: firmware %s not found\n", + __func__, seg->split_fname); + goto bad_seg; + } + + seg->load_addr = phdr->p_paddr; + seg->size = phdr->p_filesz; + seg->data = (u8 *) seg->split_fw->data; + + list_add_tail(&seg->list, seg_list); +done: + return ret; +bad_seg: + kfree(seg); + return ret; +} + +/* + * wdsp_flush_segment_list: Flush the list of segments + * @seg_list: List of segments to be flushed + * This API will traverse through the list of segments provided in + * seg_list, release the firmware for each segment and delete the + * segment from the list. + */ +void wdsp_flush_segment_list(struct list_head *seg_list) +{ + struct wdsp_img_segment *seg, *next; + + list_for_each_entry_safe(seg, next, seg_list, list) { + release_firmware(seg->split_fw); + list_del(&seg->list); + kfree(seg); + } +} +EXPORT_SYMBOL(wdsp_flush_segment_list); + +/* + * wdsp_get_segment_list: Get the list of requested segments + * @dev: struct device pointer of caller + * @img_fname: Image name for the mdt and split firmware files + * @segment_type: Requested segment type, should be either + * WDSP_ELF_FLAG_RE or WDSP_ELF_FLAG_WRITE + * @seg_list: An initialized head for list of segmented to be returned + * @entry_point: Pointer to return the entry point of the image + * This API will parse the mdt file for img_fname and create + * an struct wdsp_img_segment for each segment that matches segment_type + * and add this structure to list pointed by seg_list + */ +int wdsp_get_segment_list(struct device *dev, + const char *img_fname, + unsigned int segment_type, + struct list_head *seg_list, + u32 *entry_point) +{ + const struct firmware *fw; + const struct elf32_hdr *ehdr; + const struct elf32_phdr *phdr; + const u8 *elf_ptr; + char mdt_name[WDSP_IMG_NAME_LEN_MAX]; + int ret, phdr_idx; + bool segment_match; + + if (!dev) { + ret = -EINVAL; + pr_err("%s: Invalid device handle\n", __func__); + goto done; + } + + if (!img_fname || !seg_list || !entry_point) { + ret = -EINVAL; + dev_err(dev, "%s: Invalid input params\n", + __func__); + goto done; + } + + if (segment_type != WDSP_ELF_FLAG_RE && + segment_type != WDSP_ELF_FLAG_WRITE) { + dev_err(dev, "%s: Invalid request for segment_type %d\n", + __func__, segment_type); + ret = -EINVAL; + goto done; + } + + snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", img_fname); + ret = request_firmware(&fw, mdt_name, dev); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: firmware %s not found\n", + __func__, mdt_name); + goto done; + } + + ehdr = (struct elf32_hdr *) fw->data; + *entry_point = ehdr->e_entry; + if (!wdsp_is_valid_elf_hdr(ehdr, fw->size)) { + dev_err(dev, "%s: fw mdt %s is invalid\n", + __func__, mdt_name); + ret = -EINVAL; + goto bad_elf; + } + + elf_ptr = fw->data + sizeof(*ehdr); + for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) { + phdr = (struct elf32_phdr *) elf_ptr; + segment_match = false; + + switch (segment_type) { + case WDSP_ELF_FLAG_RE: + /* + * Flag can be READ or EXECUTE or both but + * WRITE flag should not be set. + */ + if ((phdr->p_flags & segment_type) && + !(phdr->p_flags & WDSP_ELF_FLAG_WRITE)) + segment_match = true; + break; + case WDSP_ELF_FLAG_WRITE: + /* + * If WRITE flag is set, other flags do not + * matter. + */ + if (phdr->p_flags & segment_type) + segment_match = true; + break; + } + + if (segment_match) { + ret = wdsp_add_segment_to_list(dev, img_fname, phdr, + phdr_idx, seg_list); + if (IS_ERR_VALUE(ret)) { + wdsp_flush_segment_list(seg_list); + goto bad_elf; + } + } + elf_ptr = elf_ptr + sizeof(*phdr); + } + +bad_elf: + release_firmware(fw); +done: + return ret; +} +EXPORT_SYMBOL(wdsp_get_segment_list); diff --git a/sound/soc/codecs/wcd-dsp-utils.h b/sound/soc/codecs/wcd-dsp-utils.h new file mode 100644 index 0000000000000000000000000000000000000000..a530a1c3b67d00035fe0fd3577ad6e6a29b7e6b9 --- /dev/null +++ b/sound/soc/codecs/wcd-dsp-utils.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD_DSP_UTILS_H__ +#define __WCD_DSP_UTILS_H__ + +#define WDSP_IMG_NAME_LEN_MAX 64 + +#define WDSP_ELF_FLAG_EXECUTE (1 << 0) +#define WDSP_ELF_FLAG_WRITE (1 << 1) +#define WDSP_ELF_FLAG_READ (1 << 2) + +#define WDSP_ELF_FLAG_RE (WDSP_ELF_FLAG_READ | WDSP_ELF_FLAG_EXECUTE) + +struct wdsp_img_segment { + + /* Firmware for the slit image */ + const struct firmware *split_fw; + + /* Name of the split firmware file */ + char split_fname[WDSP_IMG_NAME_LEN_MAX]; + + /* Address where the segment is to be loaded */ + u32 load_addr; + + /* Buffer to hold the data to be loaded */ + u8 *data; + + /* Size of the data to be loaded */ + size_t size; + + /* List node pointing to next segment */ + struct list_head list; +}; + +int wdsp_get_segment_list(struct device *dev, const char *img_fname, + unsigned int segment_type, struct list_head *seg_list, + u32 *entry_point); +void wdsp_flush_segment_list(struct list_head *seg_list); + +#endif /* __WCD_DSP_UTILS_H__ */ diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c new file mode 100644 index 0000000000000000000000000000000000000000..cc54362fa88049d65580073972454968551c2c78 --- /dev/null +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -0,0 +1,2606 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd-mbhc-v2.h" +#include "wcdcal-hwdep.h" + +#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \ + SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \ + SND_JACK_MECHANICAL | SND_JACK_MICROPHONE2 | \ + SND_JACK_UNSUPPORTED) + +#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3 | \ + SND_JACK_BTN_4 | SND_JACK_BTN_5) +#define OCP_ATTEMPT 20 +#define HS_DETECT_PLUG_TIME_MS (3 * 1000) +#define SPECIAL_HS_DETECT_TIME_MS (2 * 1000) +#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250 +#define GND_MIC_SWAP_THRESHOLD 4 +#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100 +#define HS_VREF_MIN_VAL 1400 +#define FW_READ_ATTEMPTS 15 +#define FW_READ_TIMEOUT 4000000 +#define FAKE_REM_RETRY_ATTEMPTS 3 +#define MAX_IMPED 60000 + +#define WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS 50 +#define ANC_DETECT_RETRY_CNT 7 +#define WCD_MBHC_SPL_HS_CNT 1 + +static int det_extn_cable_en; +module_param(det_extn_cable_en, int, 0664); +MODULE_PARM_DESC(det_extn_cable_en, "enable/disable extn cable detect"); + +enum wcd_mbhc_cs_mb_en_flag { + WCD_MBHC_EN_CS = 0, + WCD_MBHC_EN_MB, + WCD_MBHC_EN_PULLUP, + WCD_MBHC_EN_NONE, +}; + +static void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc, + struct snd_soc_jack *jack, int status, int mask) +{ + snd_soc_jack_report(jack, status, mask); +} + +static void __hphocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status, + int irq) +{ + struct snd_soc_codec *codec = mbhc->codec; + + dev_dbg(codec->dev, "%s: clear ocp status %x\n", + __func__, jack_status); + + if (mbhc->hph_status & jack_status) { + mbhc->hph_status &= ~jack_status; + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, WCD_MBHC_JACK_MASK); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1); + /* + * reset retry counter as PA is turned off signifying + * start of new OCP detection session + */ + if (mbhc->intr_ids->hph_left_ocp) + mbhc->hphlocp_cnt = 0; + else + mbhc->hphrocp_cnt = 0; + mbhc->mbhc_cb->irq_control(codec, irq, true); + } +} + +static void hphrocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status) +{ + __hphocp_off_report(mbhc, SND_JACK_OC_HPHR, + mbhc->intr_ids->hph_right_ocp); +} + +static void hphlocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status) +{ + __hphocp_off_report(mbhc, SND_JACK_OC_HPHL, + mbhc->intr_ids->hph_left_ocp); +} + +static void wcd_program_hs_vref(struct wcd_mbhc *mbhc) +{ + struct wcd_mbhc_plug_type_cfg *plug_type_cfg; + struct snd_soc_codec *codec = mbhc->codec; + u32 reg_val; + + plug_type_cfg = WCD_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration); + reg_val = ((plug_type_cfg->v_hs_max - HS_VREF_MIN_VAL) / 100); + + dev_dbg(codec->dev, "%s: reg_val = %x\n", __func__, reg_val); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_VREF, reg_val); +} + +static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias) +{ + struct wcd_mbhc_btn_detect_cfg *btn_det; + struct snd_soc_codec *codec = mbhc->codec; + struct snd_soc_card *card = codec->component.card; + s16 *btn_low, *btn_high; + + if (mbhc->mbhc_cfg->calibration == NULL) { + dev_err(card->dev, "%s: calibration data is NULL\n", __func__); + return; + } + + btn_det = WCD_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration); + btn_low = btn_det->_v_btn_low; + btn_high = ((void *)&btn_det->_v_btn_low) + + (sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn); + + mbhc->mbhc_cb->set_btn_thr(codec, btn_low, btn_high, btn_det->num_btn, + micbias); +} + +static void wcd_enable_curr_micbias(const struct wcd_mbhc *mbhc, + const enum wcd_mbhc_cs_mb_en_flag cs_mb_en) +{ + + /* + * Some codecs handle micbias/pullup enablement in codec + * drivers itself and micbias is not needed for regular + * plug type detection. So if micbias_control callback function + * is defined, just return. + */ + if (mbhc->mbhc_cb->mbhc_micbias_control) + return; + + pr_debug("%s: enter, cs_mb_en: %d\n", __func__, cs_mb_en); + + switch (cs_mb_en) { + case WCD_MBHC_EN_CS: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + /* Program Button threshold registers as per CS */ + wcd_program_btn_threshold(mbhc, false); + break; + case WCD_MBHC_EN_MB: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + + /* Disable PULL_UP_EN & enable MICBIAS */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 2); + /* Program Button threshold registers as per MICBIAS */ + wcd_program_btn_threshold(mbhc, true); + break; + case WCD_MBHC_EN_PULLUP: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 1); + /* Program Button threshold registers as per MICBIAS */ + wcd_program_btn_threshold(mbhc, true); + break; + case WCD_MBHC_EN_NONE: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + break; + default: + pr_debug("%s: Invalid parameter", __func__); + break; + } + + pr_debug("%s: exit\n", __func__); +} + +static const char *wcd_mbhc_get_event_string(int event) +{ + switch (event) { + case WCD_EVENT_PRE_MICBIAS_2_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_MICBIAS_2_OFF); + case WCD_EVENT_POST_MICBIAS_2_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_MICBIAS_2_OFF); + case WCD_EVENT_PRE_MICBIAS_2_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_MICBIAS_2_ON); + case WCD_EVENT_POST_MICBIAS_2_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_MICBIAS_2_ON); + case WCD_EVENT_PRE_HPHL_PA_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHL_PA_ON); + case WCD_EVENT_POST_HPHL_PA_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_HPHL_PA_OFF); + case WCD_EVENT_PRE_HPHR_PA_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHR_PA_ON); + case WCD_EVENT_POST_HPHR_PA_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_HPHR_PA_OFF); + case WCD_EVENT_PRE_HPHR_PA_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHR_PA_OFF); + case WCD_EVENT_PRE_HPHL_PA_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHL_PA_OFF); + case WCD_EVENT_POST_DAPM_MICBIAS_2_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_ON); + case WCD_EVENT_PRE_DAPM_MICBIAS_2_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_ON); + case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_OFF); + case WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF); + case WCD_EVENT_OCP_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_OFF); + case WCD_EVENT_OCP_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_ON); + case WCD_EVENT_INVALID: + default: + return WCD_MBHC_STRINGIFY(WCD_EVENT_INVALID); + } +} + +static int wcd_event_notify(struct notifier_block *self, unsigned long val, + void *data) +{ + struct wcd_mbhc *mbhc = (struct wcd_mbhc *)data; + enum wcd_notify_event event = (enum wcd_notify_event)val; + struct snd_soc_codec *codec = mbhc->codec; + bool micbias2 = false; + bool micbias1 = false; + u8 fsm_en = 0; + + pr_debug("%s: event %s (%d)\n", __func__, + wcd_mbhc_get_event_string(event), event); + if (mbhc->mbhc_cb->micbias_enable_status) { + micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_2); + micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_1); + } + switch (event) { + /* MICBIAS usage change */ + case WCD_EVENT_POST_DAPM_MICBIAS_2_ON: + mbhc->is_hs_recording = true; + pr_debug("%s: is_capture: %d\n", __func__, + mbhc->is_hs_recording); + break; + case WCD_EVENT_POST_MICBIAS_2_ON: + if (!mbhc->micbias_enable) + goto out_micb_en; + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) { + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_PRECHARGE, + true); + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_SET_VAL, + true); + /* + * Special headset needs MICBIAS as 2.7V so wait for + * 50 msec for the MICBIAS to reach 2.7 volts. + */ + msleep(50); + } + if (mbhc->mbhc_cb->set_auto_zeroing) + mbhc->mbhc_cb->set_auto_zeroing(codec, true); + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_PRECHARGE, + false); +out_micb_en: + /* Disable current source if micbias enabled */ + if (mbhc->mbhc_cb->mbhc_micbias_control) { + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); + if (fsm_en) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, + 0); + } else { + mbhc->is_hs_recording = true; + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + } + /* configure cap settings properly when micbias is enabled */ + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true); + break; + case WCD_EVENT_PRE_MICBIAS_2_OFF: + /* + * Before MICBIAS_2 is turned off, if FSM is enabled, + * make sure current source is enabled so as to detect + * button press/release events + */ + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->micbias_enable) { + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); + if (fsm_en) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, + 3); + } + break; + /* MICBIAS usage change */ + case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF: + mbhc->is_hs_recording = false; + pr_debug("%s: is_capture: %d\n", __func__, + mbhc->is_hs_recording); + break; + case WCD_EVENT_POST_MICBIAS_2_OFF: + if (!mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->is_hs_recording = false; + if (mbhc->micbias_enable) { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + break; + } + + if (mbhc->mbhc_cb->set_auto_zeroing) + mbhc->mbhc_cb->set_auto_zeroing(codec, false); + if (mbhc->mbhc_cb->set_micbias_value && !mbhc->micbias_enable) + mbhc->mbhc_cb->set_micbias_value(codec); + /* Enable PULL UP if PA's are enabled */ + if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) || + (test_bit(WCD_MBHC_EVENT_PA_HPHR, + &mbhc->event_state))) + /* enable pullup and cs, disable mb */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP); + else + /* enable current source and disable mb, pullup*/ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + + /* configure cap settings properly when micbias is disabled */ + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false); + break; + case WCD_EVENT_PRE_HPHL_PA_OFF: + mutex_lock(&mbhc->hphl_pa_lock); + break; + case WCD_EVENT_POST_HPHL_PA_OFF: + clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + if (mbhc->hph_status & SND_JACK_OC_HPHL) + hphlocp_off_report(mbhc, SND_JACK_OC_HPHL); + clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, pullup & enable cs */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + mutex_unlock(&mbhc->hphl_pa_lock); + break; + case WCD_EVENT_PRE_HPHR_PA_OFF: + mutex_lock(&mbhc->hphr_pa_lock); + break; + case WCD_EVENT_POST_HPHR_PA_OFF: + clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + if (mbhc->hph_status & SND_JACK_OC_HPHR) + hphrocp_off_report(mbhc, SND_JACK_OC_HPHR); + clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, pullup & enable cs */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + mutex_unlock(&mbhc->hphr_pa_lock); + break; + case WCD_EVENT_PRE_HPHL_PA_ON: + set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, enable pullup & cs */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP); + break; + case WCD_EVENT_PRE_HPHR_PA_ON: + set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, enable pullup & cs */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP); + break; + case WCD_EVENT_OCP_OFF: + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_left_ocp, + false); + break; + case WCD_EVENT_OCP_ON: + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_left_ocp, + true); + break; + default: + break; + } + return 0; +} + +static int wcd_cancel_btn_work(struct wcd_mbhc *mbhc) +{ + int r; + + r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork); + /* + * if scheduled mbhc.mbhc_btn_dwork is canceled from here, + * we have to unlock from here instead btn_work + */ + if (r) + mbhc->mbhc_cb->lock_sleep(mbhc, false); + return r; +} + +static bool wcd_swch_level_remove(struct wcd_mbhc *mbhc) +{ + u16 result2 = 0; + + WCD_MBHC_REG_READ(WCD_MBHC_SWCH_LEVEL_REMOVE, result2); + return (result2) ? true : false; +} + +/* should be called under interrupt context that hold suspend */ +static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc, + struct work_struct *work) +{ + pr_debug("%s: scheduling correct_swch_plug\n", __func__); + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + mbhc->hs_detect_work_stop = false; + mbhc->mbhc_cb->lock_sleep(mbhc, true); + schedule_work(work); +} + +/* called under codec_resource_lock acquisition */ +static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc, + struct work_struct *work) +{ + pr_debug("%s: Canceling correct_plug_swch\n", __func__); + mbhc->hs_detect_work_stop = true; + WCD_MBHC_RSC_UNLOCK(mbhc); + if (cancel_work_sync(work)) { + pr_debug("%s: correct_plug_swch is canceled\n", + __func__); + mbhc->mbhc_cb->lock_sleep(mbhc, false); + } + WCD_MBHC_RSC_LOCK(mbhc); +} + +static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc) +{ + bool pa_turned_on = false; + u8 wg_time = 0; + + WCD_MBHC_REG_READ(WCD_MBHC_HPH_CNP_WG_TIME, wg_time); + wg_time += 1; + + mutex_lock(&mbhc->hphr_pa_lock); + if (test_and_clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK, + &mbhc->hph_pa_dac_state)) { + pr_debug("%s: HPHR clear flag and enable PA\n", __func__); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_PA_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 1); + pa_turned_on = true; + } + mutex_unlock(&mbhc->hphr_pa_lock); + mutex_lock(&mbhc->hphl_pa_lock); + if (test_and_clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK, + &mbhc->hph_pa_dac_state)) { + pr_debug("%s: HPHL clear flag and enable PA\n", __func__); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 1); + pa_turned_on = true; + } + mutex_unlock(&mbhc->hphl_pa_lock); + + if (pa_turned_on) { + pr_debug("%s: PA was turned on by MBHC and not by DAPM\n", + __func__); + usleep_range(wg_time * 1000, wg_time * 1000 + 50); + } +} + +static bool wcd_mbhc_is_hph_pa_on(struct wcd_mbhc *mbhc) +{ + bool hph_pa_on = false; + + WCD_MBHC_REG_READ(WCD_MBHC_HPH_PA_EN, hph_pa_on); + + return (hph_pa_on) ? true : false; +} + +static void wcd_mbhc_set_and_turnoff_hph_padac(struct wcd_mbhc *mbhc) +{ + u8 wg_time = 0; + + WCD_MBHC_REG_READ(WCD_MBHC_HPH_CNP_WG_TIME, wg_time); + wg_time += 1; + + /* If headphone PA is on, check if userspace receives + * removal event to sync-up PA's state + */ + if (wcd_mbhc_is_hph_pa_on(mbhc)) { + pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__); + set_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + set_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 0); + } else { + pr_debug("%s PA is off\n", __func__); + } + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 0); + usleep_range(wg_time * 1000, wg_time * 1000 + 50); +} + +int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + *zl = mbhc->zl; + *zr = mbhc->zr; + + if (*zl && *zr) + return 0; + else + return -EINVAL; +} + +static void wcd_mbhc_hs_elec_irq(struct wcd_mbhc *mbhc, int irq_type, + bool enable) +{ + int irq; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (irq_type == WCD_MBHC_ELEC_HS_INS) + irq = mbhc->intr_ids->mbhc_hs_ins_intr; + else if (irq_type == WCD_MBHC_ELEC_HS_REM) + irq = mbhc->intr_ids->mbhc_hs_rem_intr; + else { + pr_debug("%s: irq_type: %d, enable: %d\n", + __func__, irq_type, enable); + return; + } + + pr_debug("%s: irq: %d, enable: %d, intr_status:%lu\n", + __func__, irq, enable, mbhc->intr_status); + if ((test_bit(irq_type, &mbhc->intr_status)) != enable) { + mbhc->mbhc_cb->irq_control(mbhc->codec, irq, enable); + if (enable) + set_bit(irq_type, &mbhc->intr_status); + else + clear_bit(irq_type, &mbhc->intr_status); + } +} + +static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, + enum snd_jack_types jack_type) +{ + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + pr_debug("%s: enter insertion %d hph_status %x\n", + __func__, insertion, mbhc->hph_status); + if (!insertion) { + /* Report removal */ + mbhc->hph_status &= ~jack_type; + /* + * cancel possibly scheduled btn work and + * report release if we reported button press + */ + if (wcd_cancel_btn_work(mbhc)) { + pr_debug("%s: button press is canceled\n", __func__); + } else if (mbhc->buttons_pressed) { + pr_debug("%s: release of button press%d\n", + __func__, jack_type); + wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, 0, + mbhc->buttons_pressed); + mbhc->buttons_pressed &= + ~WCD_MBHC_JACK_BUTTON_MASK; + } + + if (mbhc->micbias_enable) { + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control( + mbhc->codec, MIC_BIAS_2, + MICB_DISABLE); + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + mbhc->codec, + MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) { + mbhc->mbhc_cb->set_micbias_value(mbhc->codec); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + } + mbhc->micbias_enable = false; + } + + mbhc->hph_type = WCD_MBHC_HPH_NONE; + mbhc->zl = mbhc->zr = 0; + pr_debug("%s: Reporting removal %d(%x)\n", __func__, + jack_type, mbhc->hph_status); + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, WCD_MBHC_JACK_MASK); + wcd_mbhc_set_and_turnoff_hph_padac(mbhc); + hphrocp_off_report(mbhc, SND_JACK_OC_HPHR); + hphlocp_off_report(mbhc, SND_JACK_OC_HPHL); + mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + } else { + /* + * Report removal of current jack type. + * Headphone to headset shouldn't report headphone + * removal. + */ + if (mbhc->mbhc_cfg->detect_extn_cable && + (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH || + jack_type == SND_JACK_LINEOUT) && + (mbhc->hph_status && mbhc->hph_status != jack_type)) { + + if (mbhc->micbias_enable) { + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control( + mbhc->codec, MIC_BIAS_2, + MICB_DISABLE); + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + mbhc->codec, + MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) { + mbhc->mbhc_cb->set_micbias_value( + mbhc->codec); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_MICB_CTRL, 0); + } + mbhc->micbias_enable = false; + } + mbhc->hph_type = WCD_MBHC_HPH_NONE; + mbhc->zl = mbhc->zr = 0; + pr_debug("%s: Reporting removal (%x)\n", + __func__, mbhc->hph_status); + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + 0, WCD_MBHC_JACK_MASK); + + if (mbhc->hph_status == SND_JACK_LINEOUT) { + + pr_debug("%s: Enable micbias\n", __func__); + /* Disable current source and enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + pr_debug("%s: set up elec removal detection\n", + __func__); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_ELECT_DETECTION_TYPE, + 0); + usleep_range(200, 210); + wcd_mbhc_hs_elec_irq(mbhc, + WCD_MBHC_ELEC_HS_REM, + true); + } + mbhc->hph_status &= ~(SND_JACK_HEADSET | + SND_JACK_LINEOUT | + SND_JACK_ANC_HEADPHONE | + SND_JACK_UNSUPPORTED); + } + + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET && + jack_type == SND_JACK_HEADPHONE) + mbhc->hph_status &= ~SND_JACK_HEADSET; + + /* Report insertion */ + if (jack_type == SND_JACK_HEADPHONE) + mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE; + else if (jack_type == SND_JACK_UNSUPPORTED) + mbhc->current_plug = MBHC_PLUG_TYPE_GND_MIC_SWAP; + else if (jack_type == SND_JACK_HEADSET) { + mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET; + mbhc->jiffies_atreport = jiffies; + } else if (jack_type == SND_JACK_LINEOUT) { + mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; + } else if (jack_type == SND_JACK_ANC_HEADPHONE) + mbhc->current_plug = MBHC_PLUG_TYPE_ANC_HEADPHONE; + + if (mbhc->impedance_detect && + mbhc->mbhc_cb->compute_impedance && + (mbhc->mbhc_cfg->linein_th != 0)) { + mbhc->mbhc_cb->compute_impedance(mbhc, + &mbhc->zl, &mbhc->zr); + if ((mbhc->zl > mbhc->mbhc_cfg->linein_th && + mbhc->zl < MAX_IMPED) && + (mbhc->zr > mbhc->mbhc_cfg->linein_th && + mbhc->zr < MAX_IMPED) && + (jack_type == SND_JACK_HEADPHONE)) { + jack_type = SND_JACK_LINEOUT; + mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; + if (mbhc->hph_status) { + mbhc->hph_status &= ~(SND_JACK_HEADSET | + SND_JACK_LINEOUT | + SND_JACK_UNSUPPORTED); + wcd_mbhc_jack_report(mbhc, + &mbhc->headset_jack, + mbhc->hph_status, + WCD_MBHC_JACK_MASK); + } + pr_debug("%s: Marking jack type as SND_JACK_LINEOUT\n", + __func__); + } + } + + mbhc->hph_status |= jack_type; + + pr_debug("%s: Reporting insertion %d(%x)\n", __func__, + jack_type, mbhc->hph_status); + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + (mbhc->hph_status | SND_JACK_MECHANICAL), + WCD_MBHC_JACK_MASK); + wcd_mbhc_clr_and_turnon_hph_padac(mbhc); + } + pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status); +} + +static bool wcd_mbhc_detect_anc_plug_type(struct wcd_mbhc *mbhc) +{ + bool anc_mic_found = false; + u16 val, hs_comp_res, btn_status = 0; + unsigned long retry = 0; + int valid_plug_cnt = 0, invalid_plug_cnt = 0; + int btn_status_cnt = 0; + bool is_check_btn_press = false; + + + if (mbhc->mbhc_cfg->anc_micbias < MIC_BIAS_1 || + mbhc->mbhc_cfg->anc_micbias > MIC_BIAS_4) + return false; + + if (!mbhc->mbhc_cb->mbhc_micbias_control) + return false; + + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, val); + + if (val) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + + mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec, + mbhc->mbhc_cfg->anc_micbias, + MICB_ENABLE); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x2); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + /* + * wait for button debounce time 20ms. If 4-pole plug is inserted + * into 5-pole jack, then there will be a button press interrupt + * during anc plug detection. In that case though Hs_comp_res is 0, + * it should not be declared as ANC plug type + */ + usleep_range(20000, 20100); + + /* + * After enabling FSM, to handle slow insertion scenarios, + * check hs_comp_result for few times to see if the IN3 voltage + * is below the Vref + */ + do { + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + goto exit; + } + pr_debug("%s: Retry attempt %lu\n", __func__, retry + 1); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + + if (!hs_comp_res) { + valid_plug_cnt++; + is_check_btn_press = true; + } else + invalid_plug_cnt++; + /* Wait 1ms before taking another reading */ + usleep_range(1000, 1100); + + WCD_MBHC_REG_READ(WCD_MBHC_FSM_STATUS, btn_status); + if (btn_status) + btn_status_cnt++; + + retry++; + } while (retry < ANC_DETECT_RETRY_CNT); + + pr_debug("%s: valid: %d, invalid: %d, btn_status_cnt: %d\n", + __func__, valid_plug_cnt, invalid_plug_cnt, btn_status_cnt); + + /* decision logic */ + if ((valid_plug_cnt > invalid_plug_cnt) && is_check_btn_press && + (btn_status_cnt == 0)) + anc_mic_found = true; +exit: + if (!val) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 0); + + mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec, + mbhc->mbhc_cfg->anc_micbias, + MICB_DISABLE); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x0); + pr_debug("%s: anc mic %sfound\n", __func__, + anc_mic_found ? "" : "not "); + return anc_mic_found; +} + +static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + bool anc_mic_found = false; + enum snd_jack_types jack_type; + + pr_debug("%s: enter current_plug(%d) new_plug(%d)\n", + __func__, mbhc->current_plug, plug_type); + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (mbhc->current_plug == plug_type) { + pr_debug("%s: cable already reported, exit\n", __func__); + goto exit; + } + + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) { + /* + * Nothing was reported previously + * report a headphone or unsupported + */ + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE); + } else if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) { + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE); + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET); + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED); + } else if (plug_type == MBHC_PLUG_TYPE_HEADSET) { + if (mbhc->mbhc_cfg->enable_anc_mic_detect) + anc_mic_found = wcd_mbhc_detect_anc_plug_type(mbhc); + + jack_type = SND_JACK_HEADSET; + if (anc_mic_found) + jack_type = SND_JACK_ANC_HEADPHONE; + + /* + * If Headphone was reported previously, this will + * only report the mic line + */ + wcd_mbhc_report_plug(mbhc, 1, jack_type); + } else if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) { + if (mbhc->mbhc_cfg->detect_extn_cable) { + /* High impedance device found. Report as LINEOUT */ + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); + pr_debug("%s: setup mic trigger for further detection\n", + __func__); + + /* Disable HW FSM and current source */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + /* Setup for insertion detection */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, + 1); + /* + * Enable HPHL trigger and MIC Schmitt triggers + * and request for elec insertion interrupts + */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, + 3); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + true); + } else { + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); + } + } else { + WARN(1, "Unexpected current plug_type %d, plug_type %d\n", + mbhc->current_plug, plug_type); + } +exit: + pr_debug("%s: leave\n", __func__); +} + +/* To determine if cross connection occurred */ +static int wcd_check_cross_conn(struct wcd_mbhc *mbhc) +{ + u16 swap_res = 0; + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_NONE; + s16 reg1 = 0; + bool hphl_sch_res = 0, hphr_sch_res = 0; + + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + return -EINVAL; + } + + /* If PA is enabled, dont check for cross-connection */ + if (mbhc->mbhc_cb->hph_pa_on_status) + if (mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec)) + return false; + + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_SCHMT_ISRC, reg1); + /* + * Check if there is any cross connection, + * Micbias and schmitt trigger (HPHL-HPHR) + * needs to be enabled. For some codecs like wcd9335, + * pull-up will already be enabled when this function + * is called for cross-connection identification. No + * need to enable micbias in that case. + */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 2); + + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, swap_res); + pr_debug("%s: swap_res%x\n", __func__, swap_res); + + /* + * Read reg hphl and hphr schmitt result with cross connection + * bit. These bits will both be "0" in case of cross connection + * otherwise, they stay at 1 + */ + WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch_res); + WCD_MBHC_REG_READ(WCD_MBHC_HPHR_SCHMT_RESULT, hphr_sch_res); + if (!(hphl_sch_res || hphr_sch_res)) { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + pr_debug("%s: Cross connection identified\n", __func__); + } else { + pr_debug("%s: No Cross connection found\n", __func__); + } + + /* Disable schmitt trigger and restore micbias */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, reg1); + pr_debug("%s: leave, plug type: %d\n", __func__, plug_type); + + return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false; +} + +static bool wcd_is_special_headset(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + int delay = 0, rc; + bool ret = false; + u16 hs_comp_res; + bool is_spl_hs = false; + + /* + * Increase micbias to 2.7V to detect headsets with + * threshold on microphone + */ + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) { + pr_debug("%s: callback fn micb_ctrl_thr_mic not defined\n", + __func__); + return false; + } else if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) { + rc = mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(codec, + MIC_BIAS_2, true); + if (rc) { + pr_err("%s: Micbias control for thr mic failed, rc: %d\n", + __func__, rc); + return false; + } + } + + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + + pr_debug("%s: special headset, start register writes\n", __func__); + + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + while (!is_spl_hs) { + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + break; + } + delay = delay + 50; + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) { + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_PRECHARGE, + true); + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_SET_VAL, + true); + } + /* Wait for 50msec for MICBIAS to settle down */ + msleep(50); + if (mbhc->mbhc_cb->set_auto_zeroing) + mbhc->mbhc_cb->set_auto_zeroing(codec, true); + /* Wait for 50msec for FSM to update result values */ + msleep(50); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + if (!(hs_comp_res)) { + pr_debug("%s: Special headset detected in %d msecs\n", + __func__, (delay * 2)); + is_spl_hs = true; + } + if (delay == SPECIAL_HS_DETECT_TIME_MS) { + pr_debug("%s: Spl headset did not get detect in 4 sec\n", + __func__); + break; + } + } + if (is_spl_hs) { + pr_debug("%s: Headset with threshold found\n", __func__); + mbhc->micbias_enable = true; + ret = true; + } + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_PRECHARGE, + false); + if (mbhc->mbhc_cb->set_micbias_value && !mbhc->micbias_enable) + mbhc->mbhc_cb->set_micbias_value(codec); + if (mbhc->mbhc_cb->set_auto_zeroing) + mbhc->mbhc_cb->set_auto_zeroing(codec, false); + + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic && + !mbhc->micbias_enable) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(codec, MIC_BIAS_2, + false); + + pr_debug("%s: leave, micb_enable: %d\n", __func__, + mbhc->micbias_enable); + return ret; +} + +static void wcd_mbhc_update_fsm_source(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + bool micbias2; + + micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_2); + switch (plug_type) { + case MBHC_PLUG_TYPE_HEADPHONE: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + break; + case MBHC_PLUG_TYPE_HEADSET: + case MBHC_PLUG_TYPE_ANC_HEADPHONE: + if (!mbhc->is_hs_recording && !micbias2) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + break; + default: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + break; + + }; +} + +static void wcd_enable_mbhc_supply(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + + struct snd_soc_codec *codec = mbhc->codec; + + /* + * Do not disable micbias if recording is going on or + * headset is inserted on the other side of the extn + * cable. If headset has been detected current source + * needs to be kept enabled for button detection to work. + * If the accessory type is invalid or unsupported, we + * dont need to enable either of them. + */ + if (det_extn_cable_en && mbhc->is_extn_cable && + mbhc->mbhc_cb && mbhc->mbhc_cb->extn_use_mb && + mbhc->mbhc_cb->extn_use_mb(codec)) { + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE || + plug_type == MBHC_PLUG_TYPE_HEADSET) + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + } else { + if (plug_type == MBHC_PLUG_TYPE_HEADSET) { + if (mbhc->is_hs_recording || mbhc->micbias_enable) + wcd_enable_curr_micbias(mbhc, + WCD_MBHC_EN_MB); + else if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, + &mbhc->event_state)) || + (test_bit(WCD_MBHC_EVENT_PA_HPHR, + &mbhc->event_state))) + wcd_enable_curr_micbias(mbhc, + WCD_MBHC_EN_PULLUP); + else + wcd_enable_curr_micbias(mbhc, + WCD_MBHC_EN_CS); + } else if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + } else { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE); + } + } +} + +static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc, + int *spl_hs_cnt) +{ + u16 hs_comp_res_1_8v = 0, hs_comp_res_2_7v = 0; + bool spl_hs = false; + + if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + goto exit; + + /* Read back hs_comp_res @ 1.8v Micbias */ + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res_1_8v); + if (!hs_comp_res_1_8v) { + spl_hs = false; + goto exit; + } + + /* Bump up MB2 to 2.7v */ + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, + mbhc->mbhc_cfg->mbhc_micbias, true); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + usleep_range(10000, 10100); + + /* Read back HS_COMP_RESULT */ + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res_2_7v); + if (!hs_comp_res_2_7v && hs_comp_res_1_8v) + spl_hs = true; + + if (spl_hs && spl_hs_cnt) + *spl_hs_cnt += 1; + + /* MB2 back to 1.8v */ + if (*spl_hs_cnt != WCD_MBHC_SPL_HS_CNT) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, + mbhc->mbhc_cfg->mbhc_micbias, false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + usleep_range(10000, 10100); + } + + if (spl_hs) + pr_debug("%s: Detected special HS (%d)\n", __func__, spl_hs); + +exit: + return spl_hs; +} + +static void wcd_correct_swch_plug(struct work_struct *work) +{ + struct wcd_mbhc *mbhc; + struct snd_soc_codec *codec; + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; + unsigned long timeout; + u16 hs_comp_res = 0, hphl_sch = 0, mic_sch = 0, btn_result = 0; + bool wrk_complete = false; + int pt_gnd_mic_swap_cnt = 0; + int no_gnd_mic_swap_cnt = 0; + bool is_pa_on = false, spl_hs = false; + bool micbias2 = false; + bool micbias1 = false; + int ret = 0; + int rc, spl_hs_count = 0; + + pr_debug("%s: enter\n", __func__); + + mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch); + codec = mbhc->codec; + + /* + * Enable micbias/pullup for detection in correct work. + * This work will get scheduled from detect_plug_type which + * will already request for pullup/micbias. If the pullup/micbias + * is handled with ref-counts by individual codec drivers, there is + * no need to enabale micbias/pullup here + */ + + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + + + if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) { + mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + goto correct_plug_type; + } + + /* Enable HW FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + /* + * Check for any button press interrupts before starting 3-sec + * loop. + */ + rc = wait_for_completion_timeout(&mbhc->btn_press_compl, + msecs_to_jiffies(WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS)); + + WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + + if (!rc) { + pr_debug("%s No btn press interrupt\n", __func__); + if (!btn_result && !hs_comp_res) + plug_type = MBHC_PLUG_TYPE_HEADSET; + else if (!btn_result && hs_comp_res) + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + else + plug_type = MBHC_PLUG_TYPE_INVALID; + } else { + if (!btn_result && !hs_comp_res) + plug_type = MBHC_PLUG_TYPE_HEADPHONE; + else + plug_type = MBHC_PLUG_TYPE_INVALID; + } + + pr_debug("%s: Valid plug found, plug type is %d\n", + __func__, plug_type); + if ((plug_type == MBHC_PLUG_TYPE_HEADSET || + plug_type == MBHC_PLUG_TYPE_HEADPHONE) && + (!wcd_swch_level_remove(mbhc))) { + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + +correct_plug_type: + + timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS); + while (!time_after(jiffies, timeout)) { + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + wcd_enable_curr_micbias(mbhc, + WCD_MBHC_EN_NONE); + if (mbhc->micbias_enable) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + mbhc->codec, MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) + mbhc->mbhc_cb->set_micbias_value( + mbhc->codec); + mbhc->micbias_enable = false; + } + goto exit; + } + if (mbhc->btn_press_intr) { + wcd_cancel_btn_work(mbhc); + mbhc->btn_press_intr = false; + } + /* Toggle FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + + /* allow sometime and re-check stop requested again */ + msleep(20); + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + wcd_enable_curr_micbias(mbhc, + WCD_MBHC_EN_NONE); + if (mbhc->micbias_enable) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + mbhc->codec, MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) + mbhc->mbhc_cb->set_micbias_value( + mbhc->codec); + mbhc->micbias_enable = false; + } + goto exit; + } + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + + pr_debug("%s: hs_comp_res: %x\n", __func__, hs_comp_res); + if (mbhc->mbhc_cb->hph_pa_on_status) + is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(codec); + + /* + * instead of hogging system by contineous polling, wait for + * sometime and re-check stop request again. + */ + msleep(180); + if (hs_comp_res && (spl_hs_count < WCD_MBHC_SPL_HS_CNT)) { + spl_hs = wcd_mbhc_check_for_spl_headset(mbhc, + &spl_hs_count); + + if (spl_hs_count == WCD_MBHC_SPL_HS_CNT) { + hs_comp_res = 0; + spl_hs = true; + mbhc->micbias_enable = true; + } + } + + if ((!hs_comp_res) && (!is_pa_on)) { + /* Check for cross connection*/ + ret = wcd_check_cross_conn(mbhc); + if (ret < 0) { + continue; + } else if (ret > 0) { + pt_gnd_mic_swap_cnt++; + no_gnd_mic_swap_cnt = 0; + if (pt_gnd_mic_swap_cnt < + GND_MIC_SWAP_THRESHOLD) { + continue; + } else if (pt_gnd_mic_swap_cnt > + GND_MIC_SWAP_THRESHOLD) { + /* + * This is due to GND/MIC switch didn't + * work, Report unsupported plug. + */ + pr_debug("%s: switch did not work\n", + __func__); + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + goto report; + } else { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + } + } else { + no_gnd_mic_swap_cnt++; + pt_gnd_mic_swap_cnt = 0; + plug_type = MBHC_PLUG_TYPE_HEADSET; + if ((no_gnd_mic_swap_cnt < + GND_MIC_SWAP_THRESHOLD) && + (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) { + continue; + } else { + no_gnd_mic_swap_cnt = 0; + } + } + if ((pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) && + (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) { + /* + * if switch is toggled, check again, + * otherwise report unsupported plug + */ + if (mbhc->mbhc_cfg->swap_gnd_mic && + mbhc->mbhc_cfg->swap_gnd_mic(codec)) { + pr_debug("%s: US_EU gpio present,flip switch\n" + , __func__); + continue; + } + } + } + + WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch); + WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch); + if (hs_comp_res && !(hphl_sch || mic_sch)) { + pr_debug("%s: cable is extension cable\n", __func__); + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + wrk_complete = true; + } else { + pr_debug("%s: cable might be headset: %d\n", __func__, + plug_type); + if (!(plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) { + plug_type = MBHC_PLUG_TYPE_HEADSET; + /* + * Report headset only if not already reported + * and if there is not button press without + * release + */ + if (((mbhc->current_plug != + MBHC_PLUG_TYPE_HEADSET) && + (mbhc->current_plug != + MBHC_PLUG_TYPE_ANC_HEADPHONE)) && + !wcd_swch_level_remove(mbhc) && + !mbhc->btn_press_intr) { + pr_debug("%s: cable is %sheadset\n", + __func__, + ((spl_hs_count == + WCD_MBHC_SPL_HS_CNT) ? + "special ":"")); + goto report; + } + } + wrk_complete = false; + } + } + if (!wrk_complete && mbhc->btn_press_intr) { + pr_debug("%s: Can be slow insertion of headphone\n", __func__); + wcd_cancel_btn_work(mbhc); + plug_type = MBHC_PLUG_TYPE_HEADPHONE; + } + /* + * If plug_tye is headset, we might have already reported either in + * detect_plug-type or in above while loop, no need to report again + */ + if (!wrk_complete && ((plug_type == MBHC_PLUG_TYPE_HEADSET) || + (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE))) { + pr_debug("%s: plug_type:0x%x already reported\n", + __func__, mbhc->current_plug); + goto enable_supply; + } + + if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH && + (!det_extn_cable_en)) { + if (wcd_is_special_headset(mbhc)) { + pr_debug("%s: Special headset found %d\n", + __func__, plug_type); + plug_type = MBHC_PLUG_TYPE_HEADSET; + goto report; + } + } + +report: + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + goto exit; + } + if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP && mbhc->btn_press_intr) { + pr_debug("%s: insertion of headphone with swap\n", __func__); + wcd_cancel_btn_work(mbhc); + plug_type = MBHC_PLUG_TYPE_HEADPHONE; + } + pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n", + __func__, plug_type, wrk_complete, + mbhc->btn_press_intr); + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); +enable_supply: + if (mbhc->mbhc_cb->mbhc_micbias_control) + wcd_mbhc_update_fsm_source(mbhc, plug_type); + else + wcd_enable_mbhc_supply(mbhc, plug_type); +exit: + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->micbias_enable) + mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2, + MICB_DISABLE); + if (mbhc->mbhc_cb->micbias_enable_status) { + micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_1); + micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_2); + } + + if (mbhc->mbhc_cfg->detect_extn_cable && + ((plug_type == MBHC_PLUG_TYPE_HEADPHONE) || + (plug_type == MBHC_PLUG_TYPE_HEADSET)) && + !mbhc->hs_detect_work_stop) { + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, true); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, micbias2); + + if (mbhc->mbhc_cb->hph_pull_down_ctrl) + mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true); + + mbhc->mbhc_cb->lock_sleep(mbhc, false); + pr_debug("%s: leave\n", __func__); +} + +/* called under codec_resource_lock acquisition */ +static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + enum wcd_mbhc_plug_type plug_type; + bool micbias1 = false; + int cross_conn; + int try = 0; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (mbhc->mbhc_cb->hph_pull_down_ctrl) + mbhc->mbhc_cb->hph_pull_down_ctrl(codec, false); + + if (mbhc->mbhc_cb->micbias_enable_status) + micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_1); + + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true); + + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2, + MICB_ENABLE); + else + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + + do { + cross_conn = wcd_check_cross_conn(mbhc); + try++; + } while (try < GND_MIC_SWAP_THRESHOLD); + + if (cross_conn > 0) { + pr_debug("%s: cross con found, start polling\n", + __func__); + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + if (!mbhc->current_plug) + mbhc->current_plug = plug_type; + pr_debug("%s: Plug found, plug type is %d\n", + __func__, plug_type); + } + + /* Re-initialize button press completion object */ + reinit_completion(&mbhc->btn_press_compl); + wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + pr_debug("%s: leave\n", __func__); +} + +static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) +{ + bool detection_type = 0; + bool micbias1 = false; + struct snd_soc_codec *codec = mbhc->codec; + + dev_dbg(codec->dev, "%s: enter\n", __func__); + + WCD_MBHC_RSC_LOCK(mbhc); + + mbhc->in_swch_irq_handler = true; + + /* cancel pending button press */ + if (wcd_cancel_btn_work(mbhc)) + pr_debug("%s: button press is canceled\n", __func__); + + WCD_MBHC_REG_READ(WCD_MBHC_MECH_DETECTION_TYPE, detection_type); + + /* Set the detection type appropriately */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MECH_DETECTION_TYPE, + !detection_type); + + pr_debug("%s: mbhc->current_plug: %d detection_type: %d\n", __func__, + mbhc->current_plug, detection_type); + wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + + if (mbhc->mbhc_cb->micbias_enable_status) + micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_1); + + if ((mbhc->current_plug == MBHC_PLUG_TYPE_NONE) && + detection_type) { + /* Make sure MASTER_BIAS_CTL is enabled */ + mbhc->mbhc_cb->mbhc_bias(codec, true); + + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_TAIL_CURR, true); + + if (!mbhc->mbhc_cfg->hs_ext_micbias && + mbhc->mbhc_cb->micb_internal) + /* + * Enable Tx2 RBias if the headset + * is using internal micbias + */ + mbhc->mbhc_cb->micb_internal(codec, 1, true); + + /* Remove micbias pulldown */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 0); + /* Apply trim if needed on the device */ + if (mbhc->mbhc_cb->trim_btn_reg) + mbhc->mbhc_cb->trim_btn_reg(codec); + /* Enable external voltage source to micbias if present */ + if (mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(mbhc, true); + mbhc->btn_press_intr = false; + wcd_mbhc_detect_plug_type(mbhc); + } else if ((mbhc->current_plug != MBHC_PLUG_TYPE_NONE) + && !detection_type) { + /* Disable external voltage source to micbias if present */ + if (mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(mbhc, false); + /* Disable HW FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_TAIL_CURR, false); + + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false); + + mbhc->btn_press_intr = false; + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, + false); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, + 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE); + } else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) { + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED); + } else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) { + /* make sure to turn off Rbias */ + if (mbhc->mbhc_cb->micb_internal) + mbhc->mbhc_cb->micb_internal(codec, 1, false); + + /* Pulldown micbias */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 1); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, + false); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, + 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET); + } else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH) { + mbhc->is_extn_cable = false; + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, + false); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, + 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_LINEOUT); + } else if (mbhc->current_plug == MBHC_PLUG_TYPE_ANC_HEADPHONE) { + mbhc->mbhc_cb->irq_control(codec, + mbhc->intr_ids->mbhc_hs_rem_intr, + false); + mbhc->mbhc_cb->irq_control(codec, + mbhc->intr_ids->mbhc_hs_ins_intr, + false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, + 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_ANC_HEADPHONE); + } + } else if (!detection_type) { + /* Disable external voltage source to micbias if present */ + if (mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(mbhc, false); + /* Disable HW FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + } + + mbhc->in_swch_irq_handler = false; + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); +} + +static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) +{ + int r = IRQ_HANDLED; + struct wcd_mbhc *mbhc = data; + + pr_debug("%s: enter\n", __func__); + if (unlikely((mbhc->mbhc_cb->lock_sleep(mbhc, true)) == false)) { + pr_warn("%s: failed to hold suspend\n", __func__); + r = IRQ_NONE; + } else { + /* Call handler */ + wcd_mbhc_swch_irq_handler(mbhc); + mbhc->mbhc_cb->lock_sleep(mbhc, false); + } + pr_debug("%s: leave %d\n", __func__, r); + return r; +} + +static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc) +{ + int mask = 0; + int btn; + + btn = mbhc->mbhc_cb->map_btn_code_to_num(mbhc->codec); + + switch (btn) { + case 0: + mask = SND_JACK_BTN_0; + break; + case 1: + mask = SND_JACK_BTN_1; + break; + case 2: + mask = SND_JACK_BTN_2; + break; + case 3: + mask = SND_JACK_BTN_3; + break; + case 4: + mask = SND_JACK_BTN_4; + break; + case 5: + mask = SND_JACK_BTN_5; + break; + default: + break; + } + + return mask; +} + +static irqreturn_t wcd_mbhc_hs_ins_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + bool detection_type = 0, hphl_sch = 0, mic_sch = 0; + u16 elect_result = 0; + static u16 hphl_trigerred; + static u16 mic_trigerred; + + pr_debug("%s: enter\n", __func__); + if (!mbhc->mbhc_cfg->detect_extn_cable) { + pr_debug("%s: Returning as Extension cable feature not enabled\n", + __func__); + return IRQ_HANDLED; + } + WCD_MBHC_RSC_LOCK(mbhc); + + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_DETECTION_TYPE, detection_type); + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, elect_result); + + pr_debug("%s: detection_type %d, elect_result %x\n", __func__, + detection_type, elect_result); + if (detection_type) { + /* check if both Left and MIC Schmitt triggers are triggered */ + WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch); + WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch); + if (hphl_sch && mic_sch) { + /* Go for plug type determination */ + pr_debug("%s: Go for plug type determination\n", + __func__); + goto determine_plug; + + } else { + if (mic_sch) { + mic_trigerred++; + pr_debug("%s: Insertion MIC trigerred %d\n", + __func__, mic_trigerred); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_ELECT_SCHMT_ISRC, + 0); + msleep(20); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_ELECT_SCHMT_ISRC, + 1); + } + if (hphl_sch) { + hphl_trigerred++; + pr_debug("%s: Insertion HPHL trigerred %d\n", + __func__, hphl_trigerred); + } + if (mic_trigerred && hphl_trigerred) { + /* Go for plug type determination */ + pr_debug("%s: Go for plug type determination\n", + __func__); + goto determine_plug; + } + } + } + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; + +determine_plug: + /* + * Disable HPHL trigger and MIC Schmitt triggers. + * Setup for insertion detection. + */ + pr_debug("%s: Disable insertion interrupt\n", __func__); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + false); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + hphl_trigerred = 0; + mic_trigerred = 0; + mbhc->is_extn_cable = true; + mbhc->btn_press_intr = false; + wcd_mbhc_detect_plug_type(mbhc); + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + u8 hs_comp_result = 0, hphl_sch = 0, mic_sch = 0; + static u16 hphl_trigerred; + static u16 mic_trigerred; + unsigned long timeout; + bool removed = true; + int retry = 0; + + pr_debug("%s: enter\n", __func__); + + WCD_MBHC_RSC_LOCK(mbhc); + + timeout = jiffies + + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS); + do { + retry++; + /* + * read the result register every 10ms to look for + * any change in HS_COMP_RESULT bit + */ + usleep_range(10000, 10100); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result); + pr_debug("%s: Check result reg for fake removal: hs_comp_res %x\n", + __func__, hs_comp_result); + if ((!hs_comp_result) && + retry > FAKE_REM_RETRY_ATTEMPTS) { + removed = false; + break; + } + } while (!time_after(jiffies, timeout)); + + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low ", __func__); + goto exit; + } + pr_debug("%s: headset %s actually removed\n", __func__, + removed ? "" : "not "); + + WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch); + WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result); + + if (removed) { + if (!(hphl_sch && mic_sch && hs_comp_result)) { + /* + * extension cable is still plugged in + * report it as LINEOUT device + */ + goto report_unplug; + } else { + if (!mic_sch) { + mic_trigerred++; + pr_debug("%s: Removal MIC trigerred %d\n", + __func__, mic_trigerred); + } + if (!hphl_sch) { + hphl_trigerred++; + pr_debug("%s: Removal HPHL trigerred %d\n", + __func__, hphl_trigerred); + } + if (mic_trigerred && hphl_trigerred) { + /* + * extension cable is still plugged in + * report it as LINEOUT device + */ + goto report_unplug; + } + } + } +exit: + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; + +report_unplug: + + /* cancel pending button press */ + if (wcd_cancel_btn_work(mbhc)) + pr_debug("%s: button press is canceled\n", __func__); + /* cancel correct work function */ + wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + + pr_debug("%s: Report extension cable\n", __func__); + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); + /* + * If PA is enabled HPHL schmitt trigger can + * be unreliable, make sure to disable it + */ + if (test_bit(WCD_MBHC_EVENT_PA_HPHL, + &mbhc->event_state)) + wcd_mbhc_set_and_turnoff_hph_padac(mbhc); + /* + * Disable HPHL trigger and MIC Schmitt triggers. + * Setup for insertion detection. + */ + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, + false); + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE); + /* Disable HW FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 3); + + /* Set the detection type appropriately */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, 1); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + true); + hphl_trigerred = 0; + mic_trigerred = 0; + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; +} + +static void wcd_btn_lpress_fn(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd_mbhc *mbhc; + s16 btn_result = 0; + + pr_debug("%s: Enter\n", __func__); + + dwork = to_delayed_work(work); + mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork); + + WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result); + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) { + pr_debug("%s: Reporting long button press event, btn_result: %d\n", + __func__, btn_result); + wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, + mbhc->buttons_pressed, mbhc->buttons_pressed); + } + pr_debug("%s: leave\n", __func__); + mbhc->mbhc_cb->lock_sleep(mbhc, false); +} + +static bool wcd_mbhc_fw_validate(const void *data, size_t size) +{ + u32 cfg_offset; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + struct firmware_cal fw; + + fw.data = (void *)data; + fw.size = size; + + if (fw.size < WCD_MBHC_CAL_MIN_SIZE) + return false; + + /* + * Previous check guarantees that there is enough fw data up + * to num_btn + */ + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(fw.data); + cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data); + if (fw.size < (cfg_offset + WCD_MBHC_CAL_BTN_SZ(btn_cfg))) + return false; + + return true; +} + +static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + int mask; + unsigned long msec_val; + + pr_debug("%s: enter\n", __func__); + complete(&mbhc->btn_press_compl); + WCD_MBHC_RSC_LOCK(mbhc); + /* send event to sw intr handler*/ + mbhc->is_btn_press = true; + wcd_cancel_btn_work(mbhc); + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low ", __func__); + goto done; + } + mbhc->btn_press_intr = true; + + msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport); + pr_debug("%s: msec_val = %ld\n", __func__, msec_val); + if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN) { + pr_debug("%s: Too short, ignore button press\n", __func__); + goto done; + } + + /* If switch interrupt already kicked in, ignore button press */ + if (mbhc->in_swch_irq_handler) { + pr_debug("%s: Swtich level changed, ignore button press\n", + __func__); + goto done; + } + if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET) { + pr_debug("%s: Plug isn't headset, ignore button press\n", + __func__); + goto done; + } + mask = wcd_mbhc_get_button_mask(mbhc); + mbhc->buttons_pressed |= mask; + mbhc->mbhc_cb->lock_sleep(mbhc, true); + if (schedule_delayed_work(&mbhc->mbhc_btn_dwork, + msecs_to_jiffies(400)) == 0) { + WARN(1, "Button pressed twice without release event\n"); + mbhc->mbhc_cb->lock_sleep(mbhc, false); + } +done: + pr_debug("%s: leave\n", __func__); + WCD_MBHC_RSC_UNLOCK(mbhc); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + int ret; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_LOCK(mbhc); + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low ", __func__); + goto exit; + } + + if (mbhc->btn_press_intr) { + mbhc->btn_press_intr = false; + } else { + pr_debug("%s: This release is for fake btn press\n", __func__); + goto exit; + } + + /* + * If current plug is headphone then there is no chance to + * get btn release interrupt, so connected cable should be + * headset not headphone. + */ + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { + wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); + goto exit; + + } + if (mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK) { + ret = wcd_cancel_btn_work(mbhc); + if (ret == 0) { + pr_debug("%s: Reporting long button release event\n", + __func__); + wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, + 0, mbhc->buttons_pressed); + } else { + if (mbhc->in_swch_irq_handler) { + pr_debug("%s: Switch irq kicked in, ignore\n", + __func__); + } else { + pr_debug("%s: Reporting btn press\n", + __func__); + wcd_mbhc_jack_report(mbhc, + &mbhc->button_jack, + mbhc->buttons_pressed, + mbhc->buttons_pressed); + pr_debug("%s: Reporting btn release\n", + __func__); + wcd_mbhc_jack_report(mbhc, + &mbhc->button_jack, + 0, mbhc->buttons_pressed); + } + } + mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK; + } +exit: + pr_debug("%s: leave\n", __func__); + WCD_MBHC_RSC_UNLOCK(mbhc); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + int val; + + pr_debug("%s: received HPHL OCP irq\n", __func__); + if (mbhc) { + if (mbhc->mbhc_cb->hph_register_recovery) { + if (mbhc->mbhc_cb->hph_register_recovery(mbhc)) { + WCD_MBHC_REG_READ(WCD_MBHC_HPHR_OCP_STATUS, + val); + if ((val != -EINVAL) && val) + mbhc->is_hph_ocp_pending = true; + goto done; + } + } + + if (mbhc->hphlocp_cnt < OCP_ATTEMPT) { + mbhc->hphlocp_cnt++; + pr_debug("%s: retry, hphlocp_cnt: %d\n", __func__, + mbhc->hphlocp_cnt); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1); + } else { + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_left_ocp, + false); + mbhc->hph_status |= SND_JACK_OC_HPHL; + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, + WCD_MBHC_JACK_MASK); + } + } else { + pr_err("%s: Bad wcd9xxx_spmi private data\n", __func__); + } +done: + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + + pr_debug("%s: received HPHR OCP irq\n", __func__); + + if (!mbhc) { + pr_err("%s: Bad mbhc private data\n", __func__); + goto done; + } + + if (mbhc->is_hph_ocp_pending) { + mbhc->is_hph_ocp_pending = false; + goto done; + } + + if (mbhc->mbhc_cb->hph_register_recovery) { + if (mbhc->mbhc_cb->hph_register_recovery(mbhc)) + /* register corruption, hence reset registers */ + goto done; + } + if (mbhc->hphrocp_cnt < OCP_ATTEMPT) { + mbhc->hphrocp_cnt++; + pr_debug("%s: retry, hphrocp_cnt: %d\n", __func__, + mbhc->hphrocp_cnt); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1); + } else { + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_right_ocp, + false); + mbhc->hph_status |= SND_JACK_OC_HPHR; + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, WCD_MBHC_JACK_MASK); + } +done: + return IRQ_HANDLED; +} + +static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) +{ + int ret = 0; + struct snd_soc_codec *codec = mbhc->codec; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_LOCK(mbhc); + + /* enable HS detection */ + if (mbhc->mbhc_cb->hph_pull_up_control) + mbhc->mbhc_cb->hph_pull_up_control(codec, I_DEFAULT); + else + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3); + + if (mbhc->mbhc_cfg->moisture_en && mbhc->mbhc_cb->mbhc_moisture_config) + mbhc->mbhc_cb->mbhc_moisture_config(mbhc); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PLUG_TYPE, mbhc->hphl_swh); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_GND_PLUG_TYPE, mbhc->gnd_swh); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1); + if (mbhc->mbhc_cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl) + mbhc->mbhc_cb->mbhc_gnd_det_ctrl(codec, true); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); + + /* Insertion debounce set to 96ms */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_INSREM_DBNC, 6); + /* Button Debounce set to 16ms */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_DBNC, 2); + + /* Enable micbias ramp */ + if (mbhc->mbhc_cb->mbhc_micb_ramp_control) + mbhc->mbhc_cb->mbhc_micb_ramp_control(codec, true); + /* enable bias */ + mbhc->mbhc_cb->mbhc_bias(codec, true); + /* enable MBHC clock */ + if (mbhc->mbhc_cb->clk_setup) + mbhc->mbhc_cb->clk_setup(codec, true); + + /* program HS_VREF value */ + wcd_program_hs_vref(mbhc); + + wcd_program_btn_threshold(mbhc, false); + + INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug); + + init_completion(&mbhc->btn_press_compl); + + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return ret; +} + +static void wcd_mbhc_fw_read(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd_mbhc *mbhc; + struct snd_soc_codec *codec; + const struct firmware *fw; + struct firmware_cal *fw_data = NULL; + int ret = -1, retry = 0; + bool use_default_cal = false; + + dwork = to_delayed_work(work); + mbhc = container_of(dwork, struct wcd_mbhc, mbhc_firmware_dwork); + codec = mbhc->codec; + + while (retry < FW_READ_ATTEMPTS) { + retry++; + pr_debug("%s:Attempt %d to request MBHC firmware\n", + __func__, retry); + if (mbhc->mbhc_cb->get_hwdep_fw_cal) + fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(mbhc, + WCD9XXX_MBHC_CAL); + if (!fw_data) + ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin", + codec->dev); + /* + * if request_firmware and hwdep cal both fail then + * sleep for 4sec for the userspace to send data to kernel + * retry for few times before bailing out + */ + if ((ret != 0) && !fw_data) { + usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT + + WCD_MBHC_USLEEP_RANGE_MARGIN_US); + } else { + pr_debug("%s: MBHC Firmware read successful\n", + __func__); + break; + } + } + if (!fw_data) + pr_debug("%s: using request_firmware\n", __func__); + else + pr_debug("%s: using hwdep cal\n", __func__); + + if (ret != 0 && !fw_data) { + pr_err("%s: Cannot load MBHC firmware use default cal\n", + __func__); + use_default_cal = true; + } + if (!use_default_cal) { + const void *data; + size_t size; + + if (fw_data) { + data = fw_data->data; + size = fw_data->size; + } else { + data = fw->data; + size = fw->size; + } + if (wcd_mbhc_fw_validate(data, size) == false) { + pr_err("%s: Invalid MBHC cal data size use default cal\n", + __func__); + if (!fw_data) + release_firmware(fw); + } else { + if (fw_data) { + mbhc->mbhc_cfg->calibration = + (void *)fw_data->data; + mbhc->mbhc_cal = fw_data; + } else { + mbhc->mbhc_cfg->calibration = + (void *)fw->data; + mbhc->mbhc_fw = fw; + } + } + + } + + (void) wcd_mbhc_initialise(mbhc); +} + +int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc) +{ + enum snd_jack_types type; + int i, ret, result = 0; + int *btn_key_code; + + btn_key_code = mbhc->mbhc_cfg->key_code; + + for (i = 0 ; i < WCD_MBHC_KEYCODE_NUM ; i++) { + if (btn_key_code[i] != 0) { + switch (i) { + case 0: + type = SND_JACK_BTN_0; + break; + case 1: + type = SND_JACK_BTN_1; + break; + case 2: + type = SND_JACK_BTN_2; + break; + case 3: + type = SND_JACK_BTN_3; + break; + case 4: + type = SND_JACK_BTN_4; + break; + case 5: + type = SND_JACK_BTN_5; + break; + default: + WARN_ONCE(1, "Wrong button number:%d\n", i); + result = -1; + return result; + } + ret = snd_jack_set_key(mbhc->button_jack.jack, + type, + btn_key_code[i]); + if (ret) { + pr_err("%s: Failed to set code for %d\n", + __func__, btn_key_code[i]); + result = -1; + return result; + } + input_set_capability( + mbhc->button_jack.jack->input_dev, + EV_KEY, btn_key_code[i]); + pr_debug("%s: set btn%d key code:%d\n", __func__, + i, btn_key_code[i]); + } + } + if (btn_key_code[0]) + mbhc->is_btn_already_regd = true; + return result; +} + +int wcd_mbhc_start(struct wcd_mbhc *mbhc, + struct wcd_mbhc_config *mbhc_cfg) +{ + int rc = 0; + + pr_debug("%s: enter\n", __func__); + /* update the mbhc config */ + mbhc->mbhc_cfg = mbhc_cfg; + + /* Set btn key code */ + if ((!mbhc->is_btn_already_regd) && wcd_mbhc_set_keycode(mbhc)) + pr_err("Set btn key code error!!!\n"); + + if (!mbhc->mbhc_cfg->read_fw_bin || + (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) || + (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) { + rc = wcd_mbhc_initialise(mbhc); + } else { + if (!mbhc->mbhc_fw || !mbhc->mbhc_cal) + schedule_delayed_work(&mbhc->mbhc_firmware_dwork, + usecs_to_jiffies(FW_READ_TIMEOUT)); + else + pr_err("%s: Skipping to read mbhc fw, 0x%pK %pK\n", + __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); + } + pr_debug("%s: leave %d\n", __func__, rc); + return rc; +} +EXPORT_SYMBOL(wcd_mbhc_start); + +void wcd_mbhc_stop(struct wcd_mbhc *mbhc) +{ + pr_debug("%s: enter\n", __func__); + if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE) { + if (mbhc->mbhc_cb && mbhc->mbhc_cb->skip_imped_detect) + mbhc->mbhc_cb->skip_imped_detect(mbhc->codec); + } + mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + mbhc->hph_status = 0; + if (mbhc->mbhc_cb && mbhc->mbhc_cb->irq_control) { + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_left_ocp, + false); + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_right_ocp, + false); + } + if (mbhc->mbhc_fw || mbhc->mbhc_cal) { + cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork); + if (!mbhc->mbhc_cal) + release_firmware(mbhc->mbhc_fw); + mbhc->mbhc_fw = NULL; + mbhc->mbhc_cal = NULL; + } + pr_debug("%s: leave\n", __func__); +} +EXPORT_SYMBOL(wcd_mbhc_stop); + +/* + * wcd_mbhc_init : initialize MBHC internal structures. + * + * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used + */ +int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, + const struct wcd_mbhc_cb *mbhc_cb, + const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, + struct wcd_mbhc_register *wcd_mbhc_regs, + bool impedance_det_en) +{ + int ret = 0; + int hph_swh = 0; + int gnd_swh = 0; + struct snd_soc_card *card = codec->component.card; + const char *hph_switch = "qcom,msm-mbhc-hphl-swh"; + const char *gnd_switch = "qcom,msm-mbhc-gnd-swh"; + + pr_debug("%s: enter\n", __func__); + + ret = of_property_read_u32(card->dev->of_node, hph_switch, &hph_swh); + if (ret) { + dev_err(card->dev, + "%s: missing %s in dt node\n", __func__, hph_switch); + goto err; + } + + ret = of_property_read_u32(card->dev->of_node, gnd_switch, &gnd_swh); + if (ret) { + dev_err(card->dev, + "%s: missing %s in dt node\n", __func__, gnd_switch); + goto err; + } + + mbhc->in_swch_irq_handler = false; + mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + mbhc->is_btn_press = false; + mbhc->codec = codec; + mbhc->intr_ids = mbhc_cdc_intr_ids; + mbhc->impedance_detect = impedance_det_en; + mbhc->hphl_swh = hph_swh; + mbhc->gnd_swh = gnd_swh; + mbhc->micbias_enable = false; + mbhc->mbhc_cb = mbhc_cb; + mbhc->btn_press_intr = false; + mbhc->is_hs_recording = false; + mbhc->is_extn_cable = false; + mbhc->hph_type = WCD_MBHC_HPH_NONE; + mbhc->wcd_mbhc_regs = wcd_mbhc_regs; + + if (mbhc->intr_ids == NULL) { + pr_err("%s: Interrupt mapping not provided\n", __func__); + return -EINVAL; + } + if (!mbhc->wcd_mbhc_regs) { + dev_err(codec->dev, "%s: mbhc registers are not defined\n", + __func__); + return -EINVAL; + } + + /* Check if IRQ and other required callbacks are defined or not */ + if (!mbhc_cb || !mbhc_cb->request_irq || !mbhc_cb->irq_control || + !mbhc_cb->free_irq || !mbhc_cb->map_btn_code_to_num || + !mbhc_cb->lock_sleep || !mbhc_cb->mbhc_bias || + !mbhc_cb->set_btn_thr) { + dev_err(codec->dev, "%s: required mbhc callbacks are not defined\n", + __func__); + return -EINVAL; + } + + if (mbhc->headset_jack.jack == NULL) { + ret = snd_soc_card_jack_new(codec->component.card, + "Headset Jack", WCD_MBHC_JACK_MASK, + &mbhc->headset_jack, NULL, 0); + if (ret) { + pr_err("%s: Failed to create new jack\n", __func__); + return ret; + } + + ret = snd_soc_card_jack_new(codec->component.card, + "Button Jack", + WCD_MBHC_JACK_BUTTON_MASK, + &mbhc->button_jack, NULL, 0); + if (ret) { + pr_err("Failed to create new jack\n"); + return ret; + } + + ret = snd_jack_set_key(mbhc->button_jack.jack, + SND_JACK_BTN_0, + KEY_MEDIA); + if (ret) { + pr_err("%s: Failed to set code for btn-0\n", + __func__); + return ret; + } + + set_bit(INPUT_PROP_NO_DUMMY_RELEASE, + mbhc->button_jack.jack->input_dev->propbit); + + INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork, + wcd_mbhc_fw_read); + INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn); + } + mutex_init(&mbhc->hphl_pa_lock); + mutex_init(&mbhc->hphr_pa_lock); + + /* Register event notifier */ + mbhc->nblock.notifier_call = wcd_event_notify; + if (mbhc->mbhc_cb->register_notifier) { + ret = mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, + true); + if (ret) { + pr_err("%s: Failed to register notifier %d\n", + __func__, ret); + return ret; + } + } + + init_waitqueue_head(&mbhc->wait_btn_press); + mutex_init(&mbhc->codec_resource_lock); + + ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->mbhc_sw_intr, + wcd_mbhc_mech_plug_detect_irq, + "mbhc sw intr", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d, ret = %d\n", __func__, + mbhc->intr_ids->mbhc_sw_intr, ret); + goto err_mbhc_sw_irq; + } + + ret = mbhc->mbhc_cb->request_irq(codec, + mbhc->intr_ids->mbhc_btn_press_intr, + wcd_mbhc_btn_press_handler, + "Button Press detect", + mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->mbhc_btn_press_intr); + goto err_btn_press_irq; + } + + ret = mbhc->mbhc_cb->request_irq(codec, + mbhc->intr_ids->mbhc_btn_release_intr, + wcd_mbhc_release_handler, + "Button Release detect", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->mbhc_btn_release_intr); + goto err_btn_release_irq; + } + + ret = mbhc->mbhc_cb->request_irq(codec, + mbhc->intr_ids->mbhc_hs_ins_intr, + wcd_mbhc_hs_ins_irq, + "Elect Insert", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->mbhc_hs_ins_intr); + goto err_mbhc_hs_ins_irq; + } + mbhc->mbhc_cb->irq_control(codec, mbhc->intr_ids->mbhc_hs_ins_intr, + false); + clear_bit(WCD_MBHC_ELEC_HS_INS, &mbhc->intr_status); + + ret = mbhc->mbhc_cb->request_irq(codec, + mbhc->intr_ids->mbhc_hs_rem_intr, + wcd_mbhc_hs_rem_irq, + "Elect Remove", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->mbhc_hs_rem_intr); + goto err_mbhc_hs_rem_irq; + } + mbhc->mbhc_cb->irq_control(codec, mbhc->intr_ids->mbhc_hs_rem_intr, + false); + clear_bit(WCD_MBHC_ELEC_HS_REM, &mbhc->intr_status); + + ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->hph_left_ocp, + wcd_mbhc_hphl_ocp_irq, "HPH_L OCP detect", + mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->hph_left_ocp); + goto err_hphl_ocp_irq; + } + + ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->hph_right_ocp, + wcd_mbhc_hphr_ocp_irq, "HPH_R OCP detect", + mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->hph_right_ocp); + goto err_hphr_ocp_irq; + } + + pr_debug("%s: leave ret %d\n", __func__, ret); + return ret; + +err_hphr_ocp_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_left_ocp, mbhc); +err_hphl_ocp_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_rem_intr, mbhc); +err_mbhc_hs_rem_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_ins_intr, mbhc); +err_mbhc_hs_ins_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_release_intr, + mbhc); +err_btn_release_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_press_intr, + mbhc); +err_btn_press_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_sw_intr, mbhc); +err_mbhc_sw_irq: + if (mbhc->mbhc_cb->register_notifier) + mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false); + mutex_destroy(&mbhc->codec_resource_lock); +err: + pr_debug("%s: leave ret %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(wcd_mbhc_init); + +void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_sw_intr, mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_press_intr, + mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_release_intr, + mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_ins_intr, mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_rem_intr, mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_left_ocp, mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_right_ocp, mbhc); + if (mbhc->mbhc_cb && mbhc->mbhc_cb->register_notifier) + mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false); + mutex_destroy(&mbhc->codec_resource_lock); + mutex_destroy(&mbhc->hphl_pa_lock); + mutex_destroy(&mbhc->hphr_pa_lock); +} +EXPORT_SYMBOL(wcd_mbhc_deinit); + +MODULE_DESCRIPTION("wcd MBHC v2 module"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h new file mode 100644 index 0000000000000000000000000000000000000000..c9bd4fe24e605efc035604624a4ca0f05ab961bd --- /dev/null +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -0,0 +1,540 @@ +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __WCD_MBHC_V2_H__ +#define __WCD_MBHC_V2_H__ + +#include +#include +#include "wcdcal-hwdep.h" + +#define TOMBAK_MBHC_NC 0 +#define TOMBAK_MBHC_NO 1 +#define WCD_MBHC_DEF_BUTTONS 8 +#define WCD_MBHC_KEYCODE_NUM 8 +#define WCD_MBHC_USLEEP_RANGE_MARGIN_US 100 +#define WCD_MBHC_THR_HS_MICB_MV 2700 +/* z value defined in Ohms */ +#define WCD_MONO_HS_MIN_THR 2 +#define WCD_MBHC_STRINGIFY(s) __stringify(s) + +enum { + WCD_MBHC_ELEC_HS_INS, + WCD_MBHC_ELEC_HS_REM, +}; + +struct wcd_mbhc; +enum wcd_mbhc_register_function { + WCD_MBHC_L_DET_EN, + WCD_MBHC_GND_DET_EN, + WCD_MBHC_MECH_DETECTION_TYPE, + WCD_MBHC_MIC_CLAMP_CTL, + WCD_MBHC_ELECT_DETECTION_TYPE, + WCD_MBHC_HS_L_DET_PULL_UP_CTRL, + WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, + WCD_MBHC_HPHL_PLUG_TYPE, + WCD_MBHC_GND_PLUG_TYPE, + WCD_MBHC_SW_HPH_LP_100K_TO_GND, + WCD_MBHC_ELECT_SCHMT_ISRC, + WCD_MBHC_FSM_EN, + WCD_MBHC_INSREM_DBNC, + WCD_MBHC_BTN_DBNC, + WCD_MBHC_HS_VREF, + WCD_MBHC_HS_COMP_RESULT, + WCD_MBHC_MIC_SCHMT_RESULT, + WCD_MBHC_HPHL_SCHMT_RESULT, + WCD_MBHC_HPHR_SCHMT_RESULT, + WCD_MBHC_OCP_FSM_EN, + WCD_MBHC_BTN_RESULT, + WCD_MBHC_BTN_ISRC_CTL, + WCD_MBHC_ELECT_RESULT, + WCD_MBHC_MICB_CTRL, /* Pull-up and micb control */ + WCD_MBHC_HPH_CNP_WG_TIME, + WCD_MBHC_HPHR_PA_EN, + WCD_MBHC_HPHL_PA_EN, + WCD_MBHC_HPH_PA_EN, + WCD_MBHC_SWCH_LEVEL_REMOVE, + WCD_MBHC_PULLDOWN_CTRL, + WCD_MBHC_ANC_DET_EN, + WCD_MBHC_FSM_STATUS, + WCD_MBHC_MUX_CTL, + WCD_MBHC_HPHL_OCP_DET_EN, + WCD_MBHC_HPHR_OCP_DET_EN, + WCD_MBHC_HPHL_OCP_STATUS, + WCD_MBHC_HPHR_OCP_STATUS, + WCD_MBHC_REG_FUNC_MAX, +}; + +enum wcd_mbhc_plug_type { + MBHC_PLUG_TYPE_INVALID = -1, + MBHC_PLUG_TYPE_NONE, + MBHC_PLUG_TYPE_HEADSET, + MBHC_PLUG_TYPE_HEADPHONE, + MBHC_PLUG_TYPE_HIGH_HPH, + MBHC_PLUG_TYPE_GND_MIC_SWAP, + MBHC_PLUG_TYPE_ANC_HEADPHONE, +}; + +enum pa_dac_ack_flags { + WCD_MBHC_HPHL_PA_OFF_ACK = 0, + WCD_MBHC_HPHR_PA_OFF_ACK, +}; + +enum wcd_mbhc_btn_det_mem { + WCD_MBHC_BTN_DET_V_BTN_LOW, + WCD_MBHC_BTN_DET_V_BTN_HIGH +}; + +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 { + MBHC_COMMON_MICB_PRECHARGE, + MBHC_COMMON_MICB_SET_VAL, + MBHC_COMMON_MICB_TAIL_CURR, +}; + +enum wcd_notify_event { + WCD_EVENT_INVALID, + /* events for micbias ON and OFF */ + WCD_EVENT_PRE_MICBIAS_2_OFF, + WCD_EVENT_POST_MICBIAS_2_OFF, + WCD_EVENT_PRE_MICBIAS_2_ON, + WCD_EVENT_POST_MICBIAS_2_ON, + WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF, + WCD_EVENT_POST_DAPM_MICBIAS_2_OFF, + WCD_EVENT_PRE_DAPM_MICBIAS_2_ON, + WCD_EVENT_POST_DAPM_MICBIAS_2_ON, + /* events for PA ON and OFF */ + WCD_EVENT_PRE_HPHL_PA_ON, + WCD_EVENT_POST_HPHL_PA_OFF, + WCD_EVENT_PRE_HPHR_PA_ON, + WCD_EVENT_POST_HPHR_PA_OFF, + WCD_EVENT_PRE_HPHL_PA_OFF, + WCD_EVENT_PRE_HPHR_PA_OFF, + WCD_EVENT_OCP_OFF, + WCD_EVENT_OCP_ON, + WCD_EVENT_LAST, +}; + +enum wcd_mbhc_event_state { + WCD_MBHC_EVENT_PA_HPHL, + WCD_MBHC_EVENT_PA_HPHR, +}; +struct wcd_mbhc_general_cfg { + u8 t_ldoh; + u8 t_bg_fast_settle; + u8 t_shutdown_plug_rem; + u8 mbhc_nsa; + u8 mbhc_navg; + u8 v_micbias_l; + u8 v_micbias; + u8 mbhc_reserved; + u16 settle_wait; + u16 t_micbias_rampup; + u16 t_micbias_rampdown; + u16 t_supply_bringup; +} __packed; + +struct wcd_mbhc_plug_detect_cfg { + u32 mic_current; + u32 hph_current; + u16 t_mic_pid; + u16 t_ins_complete; + u16 t_ins_retry; + u16 v_removal_delta; + u8 micbias_slow_ramp; + u8 reserved0; + u8 reserved1; + u8 reserved2; +} __packed; + +struct wcd_mbhc_plug_type_cfg { + u8 av_detect; + u8 mono_detect; + u8 num_ins_tries; + u8 reserved0; + s16 v_no_mic; + s16 v_av_min; + s16 v_av_max; + s16 v_hs_min; + s16 v_hs_max; + u16 reserved1; +} __packed; + +struct wcd_mbhc_btn_detect_cfg { + s8 c[8]; + u8 nc; + u8 n_meas; + u8 mbhc_nsc; + u8 n_btn_meas; + u8 n_btn_con; + u8 num_btn; + u8 reserved0; + u8 reserved1; + u16 t_poll; + u16 t_bounce_wait; + u16 t_rel_timeout; + s16 v_btn_press_delta_sta; + s16 v_btn_press_delta_cic; + u16 t_btn0_timeout; + s16 _v_btn_low[0]; /* v_btn_low[num_btn] */ + s16 _v_btn_high[0]; /* v_btn_high[num_btn] */ + u8 _n_ready[2]; + u8 _n_cic[2]; + u8 _gain[2]; +} __packed; + +struct wcd_mbhc_imped_detect_cfg { + u8 _hs_imped_detect; + u8 _n_rload; + u8 _hph_keep_on; + u8 _repeat_rload_calc; + u16 _t_dac_ramp_time; + u16 _rhph_high; + u16 _rhph_low; + u16 _rload[0]; /* rload[n_rload] */ + u16 _alpha[0]; /* alpha[n_rload] */ + u16 _beta[3]; +} __packed; + +enum wcd_mbhc_hph_type { + WCD_MBHC_HPH_NONE = 0, + WCD_MBHC_HPH_MONO, + WCD_MBHC_HPH_STEREO, +}; + +/* + * These enum definitions are directly mapped to the register + * definitions + */ +enum mbhc_moisture_vref { + V_OFF, + V_45_MV, + V_100_MV, + V_225_MV, +}; + +enum mbhc_hs_pullup_iref { + I_DEFAULT = -1, + I_OFF = 0, + I_1P0_UA, + I_2P0_UA, + I_3P0_UA, +}; + +enum mbhc_moisture_rref { + R_OFF, + R_24_KOHM, + R_84_KOHM, + R_184_KOHM, +}; + +struct wcd_mbhc_config { + bool read_fw_bin; + void *calibration; + bool detect_extn_cable; + bool mono_stero_detection; + bool (*swap_gnd_mic)(struct snd_soc_codec *codec); + bool hs_ext_micbias; + bool gnd_det_en; + int key_code[WCD_MBHC_KEYCODE_NUM]; + uint32_t linein_th; + bool moisture_en; + int mbhc_micbias; + int anc_micbias; + bool enable_anc_mic_detect; +}; + +struct wcd_mbhc_intr { + int mbhc_sw_intr; + int mbhc_btn_press_intr; + int mbhc_btn_release_intr; + int mbhc_hs_ins_intr; + int mbhc_hs_rem_intr; + int hph_left_ocp; + int hph_right_ocp; +}; + +struct wcd_mbhc_register { + const char *id; + u16 reg; + u8 mask; + u8 offset; + u8 invert; +}; + +#define WCD_MBHC_REGISTER(rid, rreg, rmask, rshift, rinvert) \ +{ .id = rid, .reg = rreg, .mask = rmask, .offset = rshift, .invert = rinvert } + +#define WCD_MBHC_RSC_LOCK(mbhc) \ +{ \ + pr_debug("%s: Acquiring BCL\n", __func__); \ + mutex_lock(&mbhc->codec_resource_lock); \ + pr_debug("%s: Acquiring BCL done\n", __func__); \ +} + +#define WCD_MBHC_RSC_UNLOCK(mbhc) \ +{ \ + pr_debug("%s: Release BCL\n", __func__); \ + mutex_unlock(&mbhc->codec_resource_lock); \ +} + +#define WCD_MBHC_RSC_ASSERT_LOCKED(mbhc) \ +{ \ + WARN_ONCE(!mutex_is_locked(&mbhc->codec_resource_lock), \ + "%s: BCL should have acquired\n", __func__); \ +} + +/* + * Macros to update and read mbhc register bits. Check for + * "0" before updating or reading the register, because it + * is possible that one codec wants to write to that bit and + * other codec does not. + */ +#define WCD_MBHC_REG_UPDATE_BITS(function, val) \ +do { \ + if (mbhc->wcd_mbhc_regs[function].reg) { \ + snd_soc_update_bits(mbhc->codec, \ + mbhc->wcd_mbhc_regs[function].reg, \ + mbhc->wcd_mbhc_regs[function].mask, \ + val << (mbhc->wcd_mbhc_regs[function].offset)); \ + } \ +} while (0) + +#define WCD_MBHC_REG_READ(function, val) \ +do { \ + if (mbhc->wcd_mbhc_regs[function].reg) { \ + val = (((snd_soc_read(mbhc->codec, \ + mbhc->wcd_mbhc_regs[function].reg)) & \ + (mbhc->wcd_mbhc_regs[function].mask)) >> \ + (mbhc->wcd_mbhc_regs[function].offset)); \ + } else { \ + val = -EINVAL; \ + } \ +} while (0) + +struct wcd_mbhc_cb { + int (*enable_mb_source)(struct wcd_mbhc *, bool); + void (*trim_btn_reg)(struct snd_soc_codec *); + void (*compute_impedance)(struct wcd_mbhc *, uint32_t *, uint32_t *); + void (*set_micbias_value)(struct snd_soc_codec *); + void (*set_auto_zeroing)(struct snd_soc_codec *, bool); + struct firmware_cal * (*get_hwdep_fw_cal)(struct wcd_mbhc *, + enum wcd_cal_type); + void (*set_cap_mode)(struct snd_soc_codec *, bool, bool); + int (*register_notifier)(struct wcd_mbhc *, + struct notifier_block *nblock, + bool enable); + int (*request_irq)(struct snd_soc_codec *, + int, irq_handler_t, const char *, void *); + void (*irq_control)(struct snd_soc_codec *, + int irq, bool enable); + int (*free_irq)(struct snd_soc_codec *, + int irq, void *); + void (*clk_setup)(struct snd_soc_codec *, bool); + int (*map_btn_code_to_num)(struct snd_soc_codec *); + bool (*lock_sleep)(struct wcd_mbhc *, bool); + bool (*micbias_enable_status)(struct wcd_mbhc *, int); + void (*mbhc_bias)(struct snd_soc_codec *, bool); + void (*mbhc_common_micb_ctrl)(struct snd_soc_codec *, + int event, bool); + void (*micb_internal)(struct snd_soc_codec *, + int micb_num, bool); + bool (*hph_pa_on_status)(struct snd_soc_codec *); + void (*set_btn_thr)(struct snd_soc_codec *, s16 *, s16 *, + int num_btn, bool); + void (*hph_pull_up_control)(struct snd_soc_codec *, + enum mbhc_hs_pullup_iref); + int (*mbhc_micbias_control)(struct snd_soc_codec *, int, int req); + void (*mbhc_micb_ramp_control)(struct snd_soc_codec *, bool); + void (*skip_imped_detect)(struct snd_soc_codec *); + bool (*extn_use_mb)(struct snd_soc_codec *); + int (*mbhc_micb_ctrl_thr_mic)(struct snd_soc_codec *, int, bool); + void (*mbhc_gnd_det_ctrl)(struct snd_soc_codec *, bool); + void (*hph_pull_down_ctrl)(struct snd_soc_codec *, bool); + void (*mbhc_moisture_config)(struct wcd_mbhc *); + bool (*hph_register_recovery)(struct wcd_mbhc *); +}; + +struct wcd_mbhc { + /* Delayed work to report long button press */ + struct delayed_work mbhc_btn_dwork; + int buttons_pressed; + struct wcd_mbhc_config *mbhc_cfg; + const struct wcd_mbhc_cb *mbhc_cb; + + u32 hph_status; /* track headhpone status */ + u8 hphlocp_cnt; /* headphone left ocp retry */ + u8 hphrocp_cnt; /* headphone right ocp retry */ + + wait_queue_head_t wait_btn_press; + bool is_btn_press; + u8 current_plug; + bool in_swch_irq_handler; + bool hphl_swh; /*track HPHL switch NC / NO */ + bool gnd_swh; /*track GND switch NC / NO */ + u8 micbias1_cap_mode; /* track ext cap setting */ + u8 micbias2_cap_mode; /* track ext cap setting */ + bool hs_detect_work_stop; + bool micbias_enable; + bool btn_press_intr; + bool is_hs_recording; + bool is_extn_cable; + bool skip_imped_detection; + bool is_btn_already_regd; + + struct snd_soc_codec *codec; + /* Work to perform MBHC Firmware Read */ + struct delayed_work mbhc_firmware_dwork; + const struct firmware *mbhc_fw; + struct firmware_cal *mbhc_cal; + + /* track PA/DAC state to sync with userspace */ + unsigned long hph_pa_dac_state; + unsigned long event_state; + unsigned long jiffies_atreport; + + /* impedance of hphl and hphr */ + uint32_t zl, zr; + bool impedance_detect; + + /* Holds type of Headset - Mono/Stereo */ + enum wcd_mbhc_hph_type hph_type; + + struct snd_soc_jack headset_jack; + struct snd_soc_jack button_jack; + struct mutex codec_resource_lock; + + /* Holds codec specific interrupt mapping */ + const struct wcd_mbhc_intr *intr_ids; + + /* Work to correct accessory type */ + struct work_struct correct_plug_swch; + struct notifier_block nblock; + + struct wcd_mbhc_register *wcd_mbhc_regs; + + struct completion btn_press_compl; + struct mutex hphl_pa_lock; + struct mutex hphr_pa_lock; + + unsigned long intr_status; + bool is_hph_ocp_pending; +}; +#define WCD_MBHC_CAL_SIZE(buttons, rload) ( \ + sizeof(struct wcd_mbhc_general_cfg) + \ + sizeof(struct wcd_mbhc_plug_detect_cfg) + \ + ((sizeof(s16) + sizeof(s16)) * buttons) + \ + sizeof(struct wcd_mbhc_plug_type_cfg) + \ + sizeof(struct wcd_mbhc_btn_detect_cfg) + \ + sizeof(struct wcd_mbhc_imped_detect_cfg) + \ + ((sizeof(u16) + sizeof(u16)) * rload) \ + ) + + +#define WCD_MBHC_CAL_GENERAL_PTR(cali) ( \ + (struct wcd_mbhc_general_cfg *) cali) +#define WCD_MBHC_CAL_PLUG_DET_PTR(cali) ( \ + (struct wcd_mbhc_plug_detect_cfg *) \ + &(WCD_MBHC_CAL_GENERAL_PTR(cali)[1])) +#define WCD_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \ + (struct wcd_mbhc_plug_type_cfg *) \ + &(WCD_MBHC_CAL_PLUG_DET_PTR(cali)[1])) +#define WCD_MBHC_CAL_BTN_DET_PTR(cali) ( \ + (struct wcd_mbhc_btn_detect_cfg *) \ + &(WCD_MBHC_CAL_PLUG_TYPE_PTR(cali)[1])) +#define WCD_MBHC_CAL_IMPED_DET_PTR(cali) ( \ + (struct wcd_mbhc_imped_detect_cfg *) \ + (((void *)&WCD_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \ + (WCD_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \ + (sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \ + sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \ + ) + +#define WCD_MBHC_CAL_MIN_SIZE ( \ + sizeof(struct wcd_mbhc_general_cfg) + \ + sizeof(struct wcd_mbhc_plug_detect_cfg) + \ + sizeof(struct wcd_mbhc_plug_type_cfg) + \ + sizeof(struct wcd_mbhc_btn_detect_cfg) + \ + sizeof(struct wcd_mbhc_imped_detect_cfg) + \ + (sizeof(u16)*2) \ + ) + +#define WCD_MBHC_CAL_BTN_SZ(cfg_ptr) ( \ + sizeof(struct wcd_mbhc_btn_detect_cfg) + \ + (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \ + sizeof(cfg_ptr->_v_btn_high[0])))) + +#define WCD_MBHC_CAL_IMPED_MIN_SZ ( \ + sizeof(struct wcd_mbhc_imped_detect_cfg) + sizeof(u16) * 2) + +#define WCD_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \ + sizeof(struct wcd_mbhc_imped_detect_cfg) + \ + (cfg_ptr->_n_rload * \ + (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0])))) + +#ifdef CONFIG_SND_SOC_WCD_MBHC +int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc); +int wcd_mbhc_start(struct wcd_mbhc *mbhc, + struct wcd_mbhc_config *mbhc_cfg); +void wcd_mbhc_stop(struct wcd_mbhc *mbhc); +int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, + const struct wcd_mbhc_cb *mbhc_cb, + const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, + struct wcd_mbhc_register *mbhc_reg, + bool impedance_det_en); +int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr); +void wcd_mbhc_deinit(struct wcd_mbhc *mbhc); +#else +static inline void wcd_mbhc_stop(struct wcd_mbhc *mbhc) +{ +} +static inline int wcd_mbhc_init(struct wcd_mbhc *mbhc, + struct snd_soc_codec *codec, + const struct wcd_mbhc_cb *mbhc_cb, + const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, + struct wcd_mbhc_register *mbhc_reg, + bool impedance_det_en) +{ + return 0; +} +static inline int wcd_mbhc_start(struct wcd_mbhc *mbhc, + struct wcd_mbhc_config *mbhc_cfg) +{ + return 0; +} +static inline int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, + uint32_t *zl, + uint32_t *zr) +{ + *zl = 0; + *zr = 0; + return -EINVAL; +} +static inline void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) +{ +} +#endif + +#endif /* __WCD_MBHC_V2_H__ */ diff --git a/sound/soc/codecs/wcd-spi-registers.h b/sound/soc/codecs/wcd-spi-registers.h new file mode 100644 index 0000000000000000000000000000000000000000..4e579696cc49f31f0bb5b56cb1ff2a8cf1beb693 --- /dev/null +++ b/sound/soc/codecs/wcd-spi-registers.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD_SPI_REGISTERS_H__ +#define __WCD_SPI_REGISTERS_H__ + +#include + +#define WCD_SPI_SLAVE_SANITY (0x00) +#define WCD_SPI_SLAVE_DEVICE_ID (0x04) +#define WCD_SPI_SLAVE_STATUS (0x08) +#define WCD_SPI_SLAVE_CONFIG (0x0c) +#define WCD_SPI_SLAVE_SW_RESET (0x10) +#define WCD_SPI_SLAVE_IRQ_STATUS (0x14) +#define WCD_SPI_SLAVE_IRQ_EN (0x18) +#define WCD_SPI_SLAVE_IRQ_CLR (0x1c) +#define WCD_SPI_SLAVE_IRQ_FORCE (0x20) +#define WCD_SPI_SLAVE_TX (0x24) +#define WCD_SPI_SLAVE_TEST_BUS_DATA (0x2c) +#define WCD_SPI_SLAVE_TEST_BUS_CTRL (0x30) +#define WCD_SPI_SLAVE_SW_RST_IRQ (0x34) +#define WCD_SPI_SLAVE_CHAR_CFG (0x38) +#define WCD_SPI_SLAVE_CHAR_DATA_MOSI (0x3c) +#define WCD_SPI_SLAVE_CHAR_DATA_CS_N (0x40) +#define WCD_SPI_SLAVE_CHAR_DATA_MISO (0x44) +#define WCD_SPI_SLAVE_TRNS_BYTE_CNT (0x4c) +#define WCD_SPI_SLAVE_TRNS_LEN (0x50) +#define WCD_SPI_SLAVE_FIFO_LEVEL (0x54) +#define WCD_SPI_SLAVE_GENERICS (0x58) +#define WCD_SPI_SLAVE_EXT_BASE_ADDR (0x5c) +#define WCD_SPI_MAX_REGISTER (0x5F) + +#endif /* End __WCD_SPI_REGISTERS_H__ */ diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c new file mode 100644 index 0000000000000000000000000000000000000000..b3fbc89f6d35bb6adc2020b90bd3d49a495a8e98 --- /dev/null +++ b/sound/soc/codecs/wcd-spi.c @@ -0,0 +1,1351 @@ +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd-spi-registers.h" + +/* Byte manipulations */ +#define SHIFT_1_BYTES (8) +#define SHIFT_2_BYTES (16) +#define SHIFT_3_BYTES (24) + +/* Command opcodes */ +#define WCD_SPI_CMD_NOP (0x00) +#define WCD_SPI_CMD_WREN (0x06) +#define WCD_SPI_CMD_CLKREQ (0xDA) +#define WCD_SPI_CMD_RDSR (0x05) +#define WCD_SPI_CMD_IRR (0x81) +#define WCD_SPI_CMD_IRW (0x82) +#define WCD_SPI_CMD_MIOR (0x83) +#define WCD_SPI_CMD_FREAD (0x0B) +#define WCD_SPI_CMD_MIOW (0x02) +#define WCD_SPI_WRITE_FRAME_OPCODE \ + (WCD_SPI_CMD_MIOW << SHIFT_3_BYTES) +#define WCD_SPI_READ_FRAME_OPCODE \ + (WCD_SPI_CMD_MIOR << SHIFT_3_BYTES) +#define WCD_SPI_FREAD_FRAME_OPCODE \ + (WCD_SPI_CMD_FREAD << SHIFT_3_BYTES) + +/* Command lengths */ +#define WCD_SPI_OPCODE_LEN (0x01) +#define WCD_SPI_CMD_NOP_LEN (0x01) +#define WCD_SPI_CMD_WREN_LEN (0x01) +#define WCD_SPI_CMD_CLKREQ_LEN (0x04) +#define WCD_SPI_CMD_IRR_LEN (0x04) +#define WCD_SPI_CMD_IRW_LEN (0x06) +#define WCD_SPI_WRITE_SINGLE_LEN (0x08) +#define WCD_SPI_READ_SINGLE_LEN (0x13) +#define WCD_SPI_CMD_FREAD_LEN (0x13) + +/* Command delays */ +#define WCD_SPI_CLKREQ_DELAY_USECS (500) +#define WCD_SPI_CLK_OFF_TIMER_MS (3000) + +/* Command masks */ +#define WCD_CMD_ADDR_MASK \ + (0xFF | \ + (0xFF << SHIFT_1_BYTES) | \ + (0xFF << SHIFT_2_BYTES)) + +/* Clock ctrl request related */ +#define WCD_SPI_CLK_ENABLE true +#define WCD_SPI_CLK_DISABLE false +#define WCD_SPI_CLK_FLAG_DELAYED (1 << 0) +#define WCD_SPI_CLK_FLAG_IMMEDIATE (1 << 1) + +/* Internal addresses */ +#define WCD_SPI_ADDR_IPC_CTL_HOST (0x012014) + +/* Word sizes and min/max lengths */ +#define WCD_SPI_WORD_BYTE_CNT (4) +#define WCD_SPI_RW_MULTI_MIN_LEN (16) + +/* Max size is closest multiple of 16 less than 64Kbytes */ +#define WCD_SPI_RW_MULTI_MAX_LEN ((64 * 1024) - 16) + +/* Alignment requirements */ +#define WCD_SPI_RW_MIN_ALIGN WCD_SPI_WORD_BYTE_CNT +#define WCD_SPI_RW_MULTI_ALIGN (16) + +/* Status mask bits */ +#define WCD_SPI_CLK_STATE_ENABLED BIT(0) + +/* Locking related */ +#define WCD_SPI_MUTEX_LOCK(spi, lock) \ +{ \ + dev_vdbg(&spi->dev, "%s: mutex_lock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WCD_SPI_MUTEX_UNLOCK(spi, lock) \ +{ \ + dev_vdbg(&spi->dev, "%s: mutex_unlock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +struct wcd_spi_debug_data { + struct dentry *dir; + u32 addr; + u32 size; +}; + +struct wcd_spi_priv { + struct spi_device *spi; + u32 mem_base_addr; + + struct regmap *regmap; + + /* Message for single transfer */ + struct spi_message msg1; + struct spi_transfer xfer1; + + /* Message for two transfers */ + struct spi_message msg2; + struct spi_transfer xfer2[2]; + + /* Register access related */ + u32 reg_bytes; + u32 val_bytes; + + /* Clock requests related */ + struct mutex clk_mutex; + int clk_users; + unsigned long status_mask; + struct delayed_work clk_dwork; + + /* Transaction related */ + struct mutex xfer_mutex; + + struct device *m_dev; + struct wdsp_mgr_ops *m_ops; + + /* Debugfs related information */ + struct wcd_spi_debug_data debug_data; +}; + +enum xfer_request { + WCD_SPI_XFER_WRITE, + WCD_SPI_XFER_READ, +}; + + +static char *wcd_spi_xfer_req_str(enum xfer_request req) +{ + if (req == WCD_SPI_XFER_WRITE) + return "xfer_write"; + else if (req == WCD_SPI_XFER_READ) + return "xfer_read"; + else + return "xfer_invalid"; +} + +static void wcd_spi_reinit_xfer(struct spi_transfer *xfer) +{ + xfer->tx_buf = NULL; + xfer->rx_buf = NULL; + xfer->delay_usecs = 0; + xfer->len = 0; +} + +static int wcd_spi_read_single(struct spi_device *spi, + u32 remote_addr, u32 *val) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 *tx_buf; + u32 frame = 0; + int ret; + + dev_dbg(&spi->dev, "%s: remote_addr = 0x%x\n", + __func__, remote_addr); + + tx_buf = kzalloc(WCD_SPI_READ_SINGLE_LEN, + GFP_KERNEL | GFP_DMA); + if (!tx_buf) + return -ENOMEM; + + frame |= WCD_SPI_READ_FRAME_OPCODE; + frame |= remote_addr & WCD_CMD_ADDR_MASK; + + wcd_spi_reinit_xfer(tx_xfer); + frame = cpu_to_be32(frame); + memcpy(tx_buf, &frame, sizeof(frame)); + tx_xfer->tx_buf = tx_buf; + tx_xfer->len = WCD_SPI_READ_SINGLE_LEN; + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->rx_buf = val; + rx_xfer->len = sizeof(*val); + + ret = spi_sync(spi, &wcd_spi->msg2); + kfree(tx_buf); + + return ret; +} + +static int wcd_spi_read_multi(struct spi_device *spi, + u32 remote_addr, u8 *data, + size_t len) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 *tx_buf; + u8 *rx_buf; + u32 frame = 0; + int ret; + + dev_dbg(&spi->dev, "%s: addr 0x%x, len = %zd\n", + __func__, remote_addr, len); + + frame |= WCD_SPI_FREAD_FRAME_OPCODE; + frame |= remote_addr & WCD_CMD_ADDR_MASK; + + tx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len, + GFP_KERNEL | GFP_DMA); + if (!tx_buf) + return -ENOMEM; + + rx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len, + GFP_KERNEL | GFP_DMA); + if (!rx_buf) { + kfree(tx_buf); + return -ENOMEM; + } + + wcd_spi_reinit_xfer(xfer); + frame = cpu_to_be32(frame); + memcpy(tx_buf, &frame, sizeof(frame)); + xfer->tx_buf = tx_buf; + xfer->rx_buf = rx_buf; + xfer->len = WCD_SPI_CMD_FREAD_LEN + len; + + ret = spi_sync(spi, &wcd_spi->msg1); + if (ret) { + dev_err(&spi->dev, "%s: failed, err = %d\n", + __func__, ret); + goto done; + } + + memcpy(data, rx_buf + WCD_SPI_CMD_FREAD_LEN, len); +done: + kfree(tx_buf); + kfree(rx_buf); + return ret; +} + +static int wcd_spi_write_single(struct spi_device *spi, + u32 remote_addr, u32 val) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 buf[WCD_SPI_WRITE_SINGLE_LEN]; + u32 frame = 0; + + dev_dbg(&spi->dev, "%s: remote_addr = 0x%x, val = 0x%x\n", + __func__, remote_addr, val); + + memset(buf, 0, WCD_SPI_WRITE_SINGLE_LEN); + frame |= WCD_SPI_WRITE_FRAME_OPCODE; + frame |= (remote_addr & WCD_CMD_ADDR_MASK); + + frame = cpu_to_be32(frame); + memcpy(buf, &frame, sizeof(frame)); + memcpy(buf + sizeof(frame), &val, sizeof(val)); + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = buf; + xfer->len = WCD_SPI_WRITE_SINGLE_LEN; + + return spi_sync(spi, &wcd_spi->msg1); +} + +static int wcd_spi_write_multi(struct spi_device *spi, + u32 remote_addr, u8 *data, + size_t len) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u32 frame = 0; + u8 *tx_buf; + int xfer_len, ret; + + dev_dbg(&spi->dev, "%s: addr = 0x%x len = %zd\n", + __func__, remote_addr, len); + + frame |= WCD_SPI_WRITE_FRAME_OPCODE; + frame |= (remote_addr & WCD_CMD_ADDR_MASK); + + frame = cpu_to_be32(frame); + xfer_len = len + sizeof(frame); + + tx_buf = kzalloc(xfer_len, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + + memcpy(tx_buf, &frame, sizeof(frame)); + memcpy(tx_buf + sizeof(frame), data, len); + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = tx_buf; + xfer->len = xfer_len; + + ret = spi_sync(spi, &wcd_spi->msg1); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, + "%s: Failed, addr = 0x%x, len = %zd\n", + __func__, remote_addr, len); + kfree(tx_buf); + + return ret; +} + +static int wcd_spi_transfer_split(struct spi_device *spi, + struct wcd_spi_msg *data_msg, + enum xfer_request xfer_req) +{ + u32 addr = data_msg->remote_addr; + u8 *data = data_msg->data; + int remain_size = data_msg->len; + int to_xfer, loop_cnt, ret = 0; + + /* Perform single writes until multi word alignment is met */ + loop_cnt = 1; + while (remain_size && + !IS_ALIGNED(addr, WCD_SPI_RW_MULTI_ALIGN)) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, addr, + (*(u32 *)data)); + else + ret = wcd_spi_read_single(spi, addr, + (u32 *)data); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) start-word addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_WORD_BYTE_CNT; + data += WCD_SPI_WORD_BYTE_CNT; + remain_size -= WCD_SPI_WORD_BYTE_CNT; + loop_cnt++; + } + + /* Perform multi writes for max allowed multi writes */ + loop_cnt = 1; + while (remain_size >= WCD_SPI_RW_MULTI_MAX_LEN) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_multi(spi, addr, data, + WCD_SPI_RW_MULTI_MAX_LEN); + else + ret = wcd_spi_read_multi(spi, addr, data, + WCD_SPI_RW_MULTI_MAX_LEN); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) max-write addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_RW_MULTI_MAX_LEN; + data += WCD_SPI_RW_MULTI_MAX_LEN; + remain_size -= WCD_SPI_RW_MULTI_MAX_LEN; + loop_cnt++; + } + + /* + * Perform write for max possible data that is multiple + * of the minimum size for multi-write commands. + */ + to_xfer = remain_size - (remain_size % WCD_SPI_RW_MULTI_MIN_LEN); + if (remain_size >= WCD_SPI_RW_MULTI_MIN_LEN && + to_xfer > 0) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_multi(spi, addr, data, to_xfer); + else + ret = wcd_spi_read_multi(spi, addr, data, to_xfer); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: %s fail write addr (0x%x), size (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + addr, to_xfer); + goto done; + } + + addr += to_xfer; + data += to_xfer; + remain_size -= to_xfer; + } + + /* Perform single writes for the last remaining data */ + loop_cnt = 1; + while (remain_size > 0) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, addr, (*((u32 *)data))); + else + ret = wcd_spi_read_single(spi, addr, (u32 *) data); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) end-write addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_WORD_BYTE_CNT; + data += WCD_SPI_WORD_BYTE_CNT; + remain_size -= WCD_SPI_WORD_BYTE_CNT; + loop_cnt++; + } + +done: + return ret; +} + +static int wcd_spi_cmd_nop(struct spi_device *spi) +{ + u8 nop = WCD_SPI_CMD_NOP; + + return spi_write(spi, &nop, WCD_SPI_CMD_NOP_LEN); +} + +static int wcd_spi_cmd_clkreq(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 cmd[WCD_SPI_CMD_CLKREQ_LEN] = { + WCD_SPI_CMD_CLKREQ, + 0xBA, 0x80, 0x00}; + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = cmd; + xfer->len = WCD_SPI_CMD_CLKREQ_LEN; + xfer->delay_usecs = WCD_SPI_CLKREQ_DELAY_USECS; + + return spi_sync(spi, &wcd_spi->msg1); +} + +static int wcd_spi_cmd_wr_en(struct spi_device *spi) +{ + u8 wr_en = WCD_SPI_CMD_WREN; + + return spi_write(spi, &wr_en, WCD_SPI_CMD_WREN_LEN); +} + +static int wcd_spi_cmd_rdsr(struct spi_device *spi, + u32 *rdsr_status) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 rdsr_cmd; + u32 status; + int ret; + + rdsr_cmd = WCD_SPI_CMD_RDSR; + wcd_spi_reinit_xfer(tx_xfer); + tx_xfer->tx_buf = &rdsr_cmd; + tx_xfer->len = sizeof(rdsr_cmd); + + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->rx_buf = &status; + rx_xfer->len = sizeof(status); + + ret = spi_sync(spi, &wcd_spi->msg2); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: RDSR failed, err = %d\n", + __func__, ret); + goto done; + } + + *rdsr_status = be32_to_cpu(status); + + dev_dbg(&spi->dev, "%s: RDSR success, value = 0x%x\n", + __func__, *rdsr_status); +done: + return ret; +} + +static int wcd_spi_clk_enable(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + u32 rd_status; + + ret = wcd_spi_cmd_nop(spi); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: NOP1 failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_spi_cmd_clkreq(spi); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: CLK_REQ failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_spi_cmd_nop(spi); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: NOP2 failed, err = %d\n", + __func__, ret); + goto done; + } + wcd_spi_cmd_rdsr(spi, &rd_status); + /* + * Read status zero means reads are not + * happenning on the bus, possibly because + * clock request failed. + */ + if (rd_status) { + set_bit(WCD_SPI_CLK_STATE_ENABLED, + &wcd_spi->status_mask); + } else { + dev_err(&spi->dev, "%s: RDSR status is zero\n", + __func__); + ret = -EIO; + } +done: + return ret; +} + +static int wcd_spi_clk_disable(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + ret = wcd_spi_write_single(spi, WCD_SPI_ADDR_IPC_CTL_HOST, 0x01); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: Failed, err = %d\n", + __func__, ret); + else + clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask); + + return ret; +} + +static int wcd_spi_clk_ctrl(struct spi_device *spi, + bool request, u32 flags) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret = 0; + const char *delay_str; + + delay_str = (flags == WCD_SPI_CLK_FLAG_DELAYED) ? + "delayed" : "immediate"; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + + /* Reject any unbalanced disable request */ + if (wcd_spi->clk_users < 0 || + (!request && wcd_spi->clk_users == 0)) { + dev_err(&spi->dev, "%s: Unbalanced clk_users %d for %s\n", + __func__, wcd_spi->clk_users, + request ? "enable" : "disable"); + ret = -EINVAL; + + /* Reset the clk_users to 0 */ + wcd_spi->clk_users = 0; + + goto done; + } + + if (request == WCD_SPI_CLK_ENABLE) { + /* Cancel the disable clk work */ + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + cancel_delayed_work_sync(&wcd_spi->clk_dwork); + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + + wcd_spi->clk_users++; + + /* + * If clk state is already set, + * then clk wasnt really disabled + */ + if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) + goto done; + else if (wcd_spi->clk_users == 1) + ret = wcd_spi_clk_enable(spi); + + } else { + wcd_spi->clk_users--; + + /* Clock is still voted for */ + if (wcd_spi->clk_users > 0) + goto done; + + /* + * If we are here, clk_users must be 0 and needs + * to be disabled. Call the disable based on the + * flags. + */ + if (flags == WCD_SPI_CLK_FLAG_DELAYED) { + schedule_delayed_work(&wcd_spi->clk_dwork, + msecs_to_jiffies(WCD_SPI_CLK_OFF_TIMER_MS)); + } else { + ret = wcd_spi_clk_disable(spi); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, + "%s: Failed to disable clk err = %d\n", + __func__, ret); + } + } + +done: + dev_dbg(&spi->dev, "%s: updated clk_users = %d, request_%s %s\n", + __func__, wcd_spi->clk_users, request ? "enable" : "disable", + request ? "" : delay_str); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + + return ret; +} + +static int wcd_spi_init(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (IS_ERR_VALUE(ret)) + goto done; + + ret = wcd_spi_cmd_wr_en(spi); + if (IS_ERR_VALUE(ret)) + goto err_wr_en; + + /* + * In case spi_init is called after component deinit, + * it is possible hardware register state is also reset. + * Sync the regcache here so hardware state is updated + * to reflect the cache. + */ + regcache_sync(wcd_spi->regmap); + + regmap_write(wcd_spi->regmap, WCD_SPI_SLAVE_CONFIG, + 0x0F3D0800); + + /* Write the MTU to max allowed size */ + regmap_update_bits(wcd_spi->regmap, + WCD_SPI_SLAVE_TRNS_LEN, + 0xFFFF0000, 0xFFFF0000); +err_wr_en: + wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); +done: + return ret; +} + +static void wcd_spi_clk_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd_spi_priv *wcd_spi; + struct spi_device *spi; + int ret; + + dwork = to_delayed_work(work); + wcd_spi = container_of(dwork, struct wcd_spi_priv, clk_dwork); + spi = wcd_spi->spi; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + ret = wcd_spi_clk_disable(spi); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, + "%s: Failed to disable clk, err = %d\n", + __func__, ret); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); +} + +static int __wcd_spi_data_xfer(struct spi_device *spi, + struct wcd_spi_msg *msg, + enum xfer_request xfer_req) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + /* Check for minimum alignment requirements */ + if (!IS_ALIGNED(msg->remote_addr, WCD_SPI_RW_MIN_ALIGN)) { + dev_err(&spi->dev, + "%s addr 0x%x is not aligned to 0x%x\n", + __func__, msg->remote_addr, WCD_SPI_RW_MIN_ALIGN); + return -EINVAL; + } else if (msg->len % WCD_SPI_WORD_BYTE_CNT) { + dev_err(&spi->dev, + "%s len 0x%zx is not multiple of %d\n", + __func__, msg->len, WCD_SPI_WORD_BYTE_CNT); + return -EINVAL; + } + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->xfer_mutex); + if (msg->len == WCD_SPI_WORD_BYTE_CNT) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, msg->remote_addr, + (*((u32 *)msg->data))); + else + ret = wcd_spi_read_single(spi, msg->remote_addr, + (u32 *) msg->data); + } else { + ret = wcd_spi_transfer_split(spi, msg, xfer_req); + } + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->xfer_mutex); + + return ret; +} + +static int wcd_spi_data_xfer(struct spi_device *spi, + struct wcd_spi_msg *msg, + enum xfer_request req) +{ + int ret, ret1; + + if (msg->len <= 0) { + dev_err(&spi->dev, "%s: Invalid size %zd\n", + __func__, msg->len); + return -EINVAL; + } + + /* Request for clock */ + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: clk enable failed %d\n", + __func__, ret); + goto done; + } + + /* Perform the transaction */ + ret = __wcd_spi_data_xfer(spi, msg, req); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, + "%s: Failed %s, addr = 0x%x, size = 0x%zx, err = %d\n", + __func__, wcd_spi_xfer_req_str(req), + msg->remote_addr, msg->len, ret); + + /* Release the clock even if xfer failed */ + ret1 = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_DELAYED); + if (IS_ERR_VALUE(ret1)) + dev_err(&spi->dev, "%s: clk disable failed %d\n", + __func__, ret1); +done: + return ret; +} + +/* + * wcd_spi_data_write: Write data to WCD SPI + * @spi: spi_device struct + * @msg: msg that needs to be written to WCD + * + * This API writes length of data to address specified. These details + * about the write are encapsulated in @msg. Write size should be multiple + * of 4 bytes and write address should be 4-byte aligned. + */ +int wcd_spi_data_write(struct spi_device *spi, + struct wcd_spi_msg *msg) +{ + if (!spi || !msg) { + pr_err("%s: Invalid %s\n", __func__, + (!spi) ? "spi device" : "msg"); + return -EINVAL; + } + + dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x, len = %zu\n", + __func__, msg->remote_addr, msg->len); + return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_WRITE); +} +EXPORT_SYMBOL(wcd_spi_data_write); + +/* + * wcd_spi_data_read: Read data from WCD SPI + * @spi: spi_device struct + * @msg: msg that needs to be read from WCD + * + * This API reads length of data from address specified. These details + * about the read are encapsulated in @msg. Read size should be multiple + * of 4 bytes and read address should be 4-byte aligned. + */ +int wcd_spi_data_read(struct spi_device *spi, + struct wcd_spi_msg *msg) +{ + if (!spi || !msg) { + pr_err("%s: Invalid %s\n", __func__, + (!spi) ? "spi device" : "msg"); + return -EINVAL; + } + + dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x,len = %zu\n", + __func__, msg->remote_addr, msg->len); + return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_READ); +} +EXPORT_SYMBOL(wcd_spi_data_read); + +static int wdsp_spi_dload_section(struct spi_device *spi, + void *data) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wdsp_img_section *sec = data; + struct wcd_spi_msg msg; + int ret; + + dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n", + __func__, sec->addr, sec->size); + + msg.remote_addr = sec->addr + wcd_spi->mem_base_addr; + msg.data = sec->data; + msg.len = sec->size; + + ret = __wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_WRITE); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n", + __func__, msg.remote_addr, msg.len); + return ret; +} + +static int wdsp_spi_read_section(struct spi_device *spi, void *data) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wdsp_img_section *sec = data; + struct wcd_spi_msg msg; + int ret; + + msg.remote_addr = sec->addr + wcd_spi->mem_base_addr; + msg.data = sec->data; + msg.len = sec->size; + + dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n", + __func__, msg.remote_addr, msg.len); + + ret = wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_READ); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n", + __func__, msg.remote_addr, msg.len); + return ret; +} + +static int wdsp_spi_event_handler(struct device *dev, void *priv_data, + enum wdsp_event_type event, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + int ret = 0; + + dev_dbg(&spi->dev, "%s: event type %d\n", + __func__, event); + + switch (event) { + case WDSP_EVENT_PRE_DLOAD_CODE: + case WDSP_EVENT_PRE_DLOAD_DATA: + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: clk_req failed %d\n", + __func__, ret); + break; + + case WDSP_EVENT_POST_DLOAD_CODE: + case WDSP_EVENT_POST_DLOAD_DATA: + case WDSP_EVENT_DLOAD_FAILED: + + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: clk unvote failed %d\n", + __func__, ret); + break; + + case WDSP_EVENT_DLOAD_SECTION: + ret = wdsp_spi_dload_section(spi, data); + break; + + case WDSP_EVENT_READ_SECTION: + ret = wdsp_spi_read_section(spi, data); + break; + + default: + dev_dbg(&spi->dev, "%s: Unhandled event %d\n", + __func__, event); + break; + } + + return ret; +} + +static int wcd_spi_bus_gwrite(void *context, const void *reg, + size_t reg_len, const void *val, + size_t val_len) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + u8 tx_buf[WCD_SPI_CMD_IRW_LEN]; + + if (!reg || !val || reg_len != wcd_spi->reg_bytes || + val_len != wcd_spi->val_bytes) { + dev_err(&spi->dev, + "%s: Invalid input, reg_len = %zd, val_len = %zd", + __func__, reg_len, val_len); + return -EINVAL; + } + + tx_buf[0] = WCD_SPI_CMD_IRW; + tx_buf[1] = *((u8 *)reg); + memcpy(&tx_buf[WCD_SPI_OPCODE_LEN + reg_len], + val, val_len); + + return spi_write(spi, tx_buf, WCD_SPI_CMD_IRW_LEN); +} + +static int wcd_spi_bus_write(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + if (count < (wcd_spi->reg_bytes + wcd_spi->val_bytes)) { + dev_err(&spi->dev, "%s: Invalid size %zd\n", + __func__, count); + WARN_ON(1); + return -EINVAL; + } + + return wcd_spi_bus_gwrite(context, data, wcd_spi->reg_bytes, + data + wcd_spi->reg_bytes, + count - wcd_spi->reg_bytes); +} + +static int wcd_spi_bus_read(void *context, const void *reg, + size_t reg_len, void *val, + size_t val_len) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 tx_buf[WCD_SPI_CMD_IRR_LEN]; + + if (!reg || !val || reg_len != wcd_spi->reg_bytes || + val_len != wcd_spi->val_bytes) { + dev_err(&spi->dev, + "%s: Invalid input, reg_len = %zd, val_len = %zd", + __func__, reg_len, val_len); + return -EINVAL; + } + + memset(tx_buf, 0, WCD_SPI_OPCODE_LEN); + tx_buf[0] = WCD_SPI_CMD_IRR; + tx_buf[1] = *((u8 *)reg); + + wcd_spi_reinit_xfer(tx_xfer); + tx_xfer->tx_buf = tx_buf; + tx_xfer->rx_buf = NULL; + tx_xfer->len = WCD_SPI_CMD_IRR_LEN; + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->tx_buf = NULL; + rx_xfer->rx_buf = val; + rx_xfer->len = val_len; + + return spi_sync(spi, &wcd_spi->msg2); +} + +static struct regmap_bus wcd_spi_regmap_bus = { + .write = wcd_spi_bus_write, + .gather_write = wcd_spi_bus_gwrite, + .read = wcd_spi_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static int wcd_spi_state_show(struct seq_file *f, void *ptr) +{ + struct spi_device *spi = f->private; + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + const char *clk_state, *clk_mutex, *xfer_mutex; + + if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) + clk_state = "enabled"; + else + clk_state = "disabled"; + + clk_mutex = mutex_is_locked(&wcd_spi->clk_mutex) ? + "locked" : "unlocked"; + + xfer_mutex = mutex_is_locked(&wcd_spi->xfer_mutex) ? + "locked" : "unlocked"; + + seq_printf(f, "clk_state = %s\nclk_users = %d\n" + "clk_mutex = %s\nxfer_mutex = %s\n", + clk_state, wcd_spi->clk_users, clk_mutex, + xfer_mutex); + return 0; +} + +static int wcd_spi_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, wcd_spi_state_show, inode->i_private); +} + +static const struct file_operations state_fops = { + .open = wcd_spi_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t wcd_spi_debugfs_mem_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct spi_device *spi = file->private_data; + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data; + struct wcd_spi_msg msg; + ssize_t buf_size, read_count = 0; + char *buf; + int ret; + + if (*ppos < 0 || !count) + return -EINVAL; + + if (dbg_data->size == 0 || dbg_data->addr == 0) { + dev_err(&spi->dev, + "%s: Invalid request, size = %u, addr = 0x%x\n", + __func__, dbg_data->size, dbg_data->addr); + return 0; + } + + buf_size = count < dbg_data->size ? count : dbg_data->size; + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + msg.data = buf; + msg.remote_addr = dbg_data->addr; + msg.len = buf_size; + msg.flags = 0; + + ret = wcd_spi_data_read(spi, &msg); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: Failed to read %zu bytes from addr 0x%x\n", + __func__, buf_size, msg.remote_addr); + goto done; + } + + read_count = simple_read_from_buffer(ubuf, count, ppos, buf, buf_size); + +done: + kfree(buf); + if (ret < 0) + return ret; + else + return read_count; +} + +static const struct file_operations mem_read_fops = { + .open = simple_open, + .read = wcd_spi_debugfs_mem_read, +}; + +static int wcd_spi_debugfs_init(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data; + int rc = 0; + + dbg_data->dir = debugfs_create_dir("wcd_spi", NULL); + if (IS_ERR_OR_NULL(dbg_data->dir)) { + dbg_data->dir = NULL; + rc = -ENODEV; + goto done; + } + + debugfs_create_file("state", 0444, dbg_data->dir, spi, &state_fops); + debugfs_create_u32("addr", 0644, dbg_data->dir, + &dbg_data->addr); + debugfs_create_u32("size", 0644, dbg_data->dir, + &dbg_data->size); + + debugfs_create_file("mem_read", 0444, dbg_data->dir, + spi, &mem_read_fops); +done: + return rc; +} + + +static const struct reg_default wcd_spi_defaults[] = { + {WCD_SPI_SLAVE_SANITY, 0xDEADBEEF}, + {WCD_SPI_SLAVE_DEVICE_ID, 0x00500000}, + {WCD_SPI_SLAVE_STATUS, 0x80100000}, + {WCD_SPI_SLAVE_CONFIG, 0x0F200808}, + {WCD_SPI_SLAVE_SW_RESET, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_STATUS, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_EN, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_CLR, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_FORCE, 0x00000000}, + {WCD_SPI_SLAVE_TX, 0x00000000}, + {WCD_SPI_SLAVE_TEST_BUS_DATA, 0x00000000}, + {WCD_SPI_SLAVE_TEST_BUS_CTRL, 0x00000000}, + {WCD_SPI_SLAVE_SW_RST_IRQ, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_CFG, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_MOSI, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_CS_N, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_MISO, 0x00000000}, + {WCD_SPI_SLAVE_TRNS_BYTE_CNT, 0x00000000}, + {WCD_SPI_SLAVE_TRNS_LEN, 0x00000000}, + {WCD_SPI_SLAVE_FIFO_LEVEL, 0x00000000}, + {WCD_SPI_SLAVE_GENERICS, 0x80000000}, + {WCD_SPI_SLAVE_EXT_BASE_ADDR, 0x00000000}, +}; + +static bool wcd_spi_is_volatile_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case WCD_SPI_SLAVE_SANITY: + case WCD_SPI_SLAVE_STATUS: + case WCD_SPI_SLAVE_IRQ_STATUS: + case WCD_SPI_SLAVE_TX: + case WCD_SPI_SLAVE_SW_RST_IRQ: + case WCD_SPI_SLAVE_TRNS_BYTE_CNT: + case WCD_SPI_SLAVE_FIFO_LEVEL: + case WCD_SPI_SLAVE_GENERICS: + return true; + } + + return false; +} + +static bool wcd_spi_is_readable_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case WCD_SPI_SLAVE_SW_RESET: + case WCD_SPI_SLAVE_IRQ_CLR: + case WCD_SPI_SLAVE_IRQ_FORCE: + return false; + } + + return true; +} + +static struct regmap_config wcd_spi_regmap_cfg = { + .reg_bits = 8, + .val_bits = 32, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd_spi_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd_spi_defaults), + .max_register = WCD_SPI_MAX_REGISTER, + .volatile_reg = wcd_spi_is_volatile_reg, + .readable_reg = wcd_spi_is_readable_reg, +}; + +static int wdsp_spi_init(struct device *dev, void *priv_data) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + + ret = wcd_spi_init(spi); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: Init failed, err = %d\n", + __func__, ret); + return ret; +} + +static int wdsp_spi_deinit(struct device *dev, void *priv_data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + /* + * Deinit means the hardware is reset. Mark the cache + * as dirty here, so init will sync the cache + */ + regcache_mark_dirty(wcd_spi->regmap); + + return 0; +} + +static struct wdsp_cmpnt_ops wdsp_spi_ops = { + .init = wdsp_spi_init, + .deinit = wdsp_spi_deinit, + .event_handler = wdsp_spi_event_handler, +}; + +static int wcd_spi_component_bind(struct device *dev, + struct device *master, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret = 0; + + wcd_spi->m_dev = master; + wcd_spi->m_ops = data; + + if (wcd_spi->m_ops && + wcd_spi->m_ops->register_cmpnt_ops) + ret = wcd_spi->m_ops->register_cmpnt_ops(master, dev, + wcd_spi, + &wdsp_spi_ops); + if (ret) { + dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n", + __func__, ret); + goto done; + } + + wcd_spi->reg_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.reg_bits, 8); + wcd_spi->val_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.val_bits, 8); + + wcd_spi->regmap = devm_regmap_init(&spi->dev, &wcd_spi_regmap_bus, + &spi->dev, &wcd_spi_regmap_cfg); + if (IS_ERR(wcd_spi->regmap)) { + ret = PTR_ERR(wcd_spi->regmap); + dev_err(&spi->dev, "%s: Failed to allocate regmap, err = %d\n", + __func__, ret); + goto done; + } + + if (wcd_spi_debugfs_init(spi)) + dev_err(&spi->dev, "%s: Failed debugfs init\n", __func__); + + spi_message_init(&wcd_spi->msg1); + spi_message_add_tail(&wcd_spi->xfer1, &wcd_spi->msg1); + + spi_message_init(&wcd_spi->msg2); + spi_message_add_tail(&wcd_spi->xfer2[0], &wcd_spi->msg2); + spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2); +done: + return ret; +} + +static void wcd_spi_component_unbind(struct device *dev, + struct device *master, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + wcd_spi->m_dev = NULL; + wcd_spi->m_ops = NULL; + + spi_transfer_del(&wcd_spi->xfer1); + spi_transfer_del(&wcd_spi->xfer2[0]); + spi_transfer_del(&wcd_spi->xfer2[1]); +} + +static const struct component_ops wcd_spi_component_ops = { + .bind = wcd_spi_component_bind, + .unbind = wcd_spi_component_unbind, +}; + +static int wcd_spi_probe(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi; + int ret = 0; + + wcd_spi = devm_kzalloc(&spi->dev, sizeof(*wcd_spi), + GFP_KERNEL); + if (!wcd_spi) + return -ENOMEM; + + ret = of_property_read_u32(spi->dev.of_node, + "qcom,mem-base-addr", + &wcd_spi->mem_base_addr); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: Missing %s DT entry", + __func__, "qcom,mem-base-addr"); + goto err_ret; + } + + dev_dbg(&spi->dev, + "%s: mem_base_addr 0x%x\n", __func__, wcd_spi->mem_base_addr); + + mutex_init(&wcd_spi->clk_mutex); + mutex_init(&wcd_spi->xfer_mutex); + INIT_DELAYED_WORK(&wcd_spi->clk_dwork, wcd_spi_clk_work); + + wcd_spi->spi = spi; + spi_set_drvdata(spi, wcd_spi); + + ret = component_add(&spi->dev, &wcd_spi_component_ops); + if (ret) { + dev_err(&spi->dev, "%s: component_add failed err = %d\n", + __func__, ret); + goto err_component_add; + } + + return ret; + +err_component_add: + mutex_destroy(&wcd_spi->clk_mutex); + mutex_destroy(&wcd_spi->xfer_mutex); +err_ret: + devm_kfree(&spi->dev, wcd_spi); + spi_set_drvdata(spi, NULL); + return ret; +} + +static int wcd_spi_remove(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + component_del(&spi->dev, &wcd_spi_component_ops); + + mutex_destroy(&wcd_spi->clk_mutex); + mutex_destroy(&wcd_spi->xfer_mutex); + + devm_kfree(&spi->dev, wcd_spi); + spi_set_drvdata(spi, NULL); + + return 0; +} + +static const struct of_device_id wcd_spi_of_match[] = { + { .compatible = "qcom,wcd-spi-v2", }, + { } +}; +MODULE_DEVICE_TABLE(of, wcd_spi_of_match); + +static struct spi_driver wcd_spi_driver = { + .driver = { + .name = "wcd-spi-v2", + .of_match_table = wcd_spi_of_match, + }, + .probe = wcd_spi_probe, + .remove = wcd_spi_remove, +}; + +module_spi_driver(wcd_spi_driver); + +MODULE_DESCRIPTION("WCD SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9330-tables.c b/sound/soc/codecs/wcd9330-tables.c new file mode 100644 index 0000000000000000000000000000000000000000..1866fb3cf27eb98e4672bcc618cf886e73917317 --- /dev/null +++ b/sound/soc/codecs/wcd9330-tables.c @@ -0,0 +1,1675 @@ +/* Copyright (c) 2014, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "wcd9330.h" + +const u8 tomtom_reg_readable[WCD9330_MAX_REGISTER + 1] = { + [TOMTOM_A_CHIP_CTL] = 1, + [TOMTOM_A_CHIP_STATUS] = 1, + [TOMTOM_A_CHIP_ID_BYTE_0] = 1, + [TOMTOM_A_CHIP_ID_BYTE_1] = 1, + [TOMTOM_A_CHIP_ID_BYTE_2] = 1, + [TOMTOM_A_CHIP_ID_BYTE_3] = 1, + [TOMTOM_A_CHIP_I2C_SLAVE_ID] = 1, + [TOMTOM_A_SLAVE_ID_1] = 1, + [TOMTOM_A_SLAVE_ID_2] = 1, + [TOMTOM_A_SLAVE_ID_3] = 1, + [TOMTOM_A_PIN_CTL_OE0] = 1, + [TOMTOM_A_PIN_CTL_OE1] = 1, + [TOMTOM_A_PIN_CTL_OE2] = 1, + [TOMTOM_A_PIN_CTL_DATA0] = 1, + [TOMTOM_A_PIN_CTL_DATA1] = 1, + [TOMTOM_A_PIN_CTL_DATA2] = 1, + [TOMTOM_A_HDRIVE_GENERIC] = 1, + [TOMTOM_A_HDRIVE_OVERRIDE] = 1, + [TOMTOM_A_ANA_CSR_WAIT_STATE] = 1, + [TOMTOM_A_PROCESS_MONITOR_CTL0] = 1, + [TOMTOM_A_PROCESS_MONITOR_CTL1] = 1, + [TOMTOM_A_PROCESS_MONITOR_CTL2] = 1, + [TOMTOM_A_PROCESS_MONITOR_CTL3] = 1, + [TOMTOM_A_QFUSE_CTL] = 1, + [TOMTOM_A_QFUSE_STATUS] = 1, + [TOMTOM_A_QFUSE_DATA_OUT0] = 1, + [TOMTOM_A_QFUSE_DATA_OUT1] = 1, + [TOMTOM_A_QFUSE_DATA_OUT2] = 1, + [TOMTOM_A_QFUSE_DATA_OUT3] = 1, + [TOMTOM_A_QFUSE_DATA_OUT4] = 1, + [TOMTOM_A_QFUSE_DATA_OUT5] = 1, + [TOMTOM_A_QFUSE_DATA_OUT6] = 1, + [TOMTOM_A_QFUSE_DATA_OUT7] = 1, + [TOMTOM_A_CDC_CTL] = 1, + [TOMTOM_A_LEAKAGE_CTL] = 1, + [TOMTOM_A_SVASS_MEM_PTR0] = 1, + [TOMTOM_A_SVASS_MEM_PTR1] = 1, + [TOMTOM_A_SVASS_MEM_PTR2] = 1, + [TOMTOM_A_SVASS_MEM_CTL] = 1, + [TOMTOM_A_SVASS_MEM_BANK] = 1, + [TOMTOM_A_DMIC_B1_CTL] = 1, + [TOMTOM_A_DMIC_B2_CTL] = 1, + [TOMTOM_A_SVASS_CLKRST_CTL] = 1, + [TOMTOM_A_SVASS_CPAR_CFG] = 1, + [TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD] = 1, + [TOMTOM_A_SVASS_CPAR_WDOG_CFG] = 1, + [TOMTOM_A_SVASS_CFG] = 1, + [TOMTOM_A_SVASS_SPE_CFG] = 1, + [TOMTOM_A_SVASS_STATUS] = 1, + [TOMTOM_A_SVASS_INT_MASK] = 1, + [TOMTOM_A_SVASS_INT_STATUS] = 1, + [TOMTOM_A_SVASS_INT_CLR] = 0, + [TOMTOM_A_SVASS_DEBUG] = 1, + [TOMTOM_A_SVASS_SPE_BKUP_INT] = 0, + [TOMTOM_A_SVASS_MEM_ACC] = 1, + [TOMTOM_A_MEM_LEAKAGE_CTL] = 1, + [TOMTOM_A_SVASS_SPE_INBOX_TRG] = 0, + [TOMTOM_A_SVASS_SPE_INBOX_0] = 0, + [TOMTOM_A_SVASS_SPE_INBOX_1] = 0, + [TOMTOM_A_SVASS_SPE_INBOX_2] = 0, + [TOMTOM_A_SVASS_SPE_INBOX_3] = 0, + [TOMTOM_A_SVASS_SPE_INBOX_4] = 0, + [TOMTOM_A_SVASS_SPE_INBOX_5] = 0, + [TOMTOM_A_SVASS_SPE_INBOX_6] = 0, + [TOMTOM_A_SVASS_SPE_INBOX_7] = 0, + [TOMTOM_A_SVASS_SPE_INBOX_8] = 0, + [TOMTOM_A_SVASS_SPE_INBOX_9] = 0, + [TOMTOM_A_SVASS_SPE_INBOX_10] = 0, + [TOMTOM_A_SVASS_SPE_INBOX_11] = 0, + [TOMTOM_A_SVASS_SPE_OUTBOX_0] = 1, + [TOMTOM_A_SVASS_SPE_OUTBOX_1] = 1, + [TOMTOM_A_SVASS_SPE_OUTBOX_2] = 1, + [TOMTOM_A_SVASS_SPE_OUTBOX_3] = 1, + [TOMTOM_A_SVASS_SPE_OUTBOX_4] = 1, + [TOMTOM_A_SVASS_SPE_OUTBOX_5] = 1, + [TOMTOM_A_SVASS_SPE_OUTBOX_6] = 1, + [TOMTOM_A_SVASS_SPE_OUTBOX_7] = 1, + [TOMTOM_A_SVASS_SPE_OUTBOX_8] = 1, + [TOMTOM_A_SVASS_SPE_OUTBOX_9] = 1, + [TOMTOM_A_SVASS_SPE_OUTBOX_10] = 1, + [TOMTOM_A_SVASS_SPE_OUTBOX_11] = 1, + [TOMTOM_A_INTR_MODE] = 1, + [TOMTOM_A_INTR1_MASK0] = 1, + [TOMTOM_A_INTR1_MASK1] = 1, + [TOMTOM_A_INTR1_MASK2] = 1, + [TOMTOM_A_INTR1_MASK3] = 1, + [TOMTOM_A_INTR1_STATUS0] = 1, + [TOMTOM_A_INTR1_STATUS1] = 1, + [TOMTOM_A_INTR1_STATUS2] = 1, + [TOMTOM_A_INTR1_STATUS3] = 1, + [TOMTOM_A_INTR1_CLEAR0] = 0, + [TOMTOM_A_INTR1_CLEAR1] = 0, + [TOMTOM_A_INTR1_CLEAR2] = 0, + [TOMTOM_A_INTR1_CLEAR3] = 0, + [TOMTOM_A_INTR1_LEVEL0] = 1, + [TOMTOM_A_INTR1_LEVEL1] = 1, + [TOMTOM_A_INTR1_LEVEL2] = 1, + [TOMTOM_A_INTR1_LEVEL3] = 1, + [TOMTOM_A_INTR1_TEST0] = 1, + [TOMTOM_A_INTR1_TEST1] = 1, + [TOMTOM_A_INTR1_TEST2] = 1, + [TOMTOM_A_INTR1_TEST3] = 1, + [TOMTOM_A_INTR1_SET0] = 1, + [TOMTOM_A_INTR1_SET1] = 1, + [TOMTOM_A_INTR1_SET2] = 1, + [TOMTOM_A_INTR1_SET3] = 1, + [TOMTOM_A_INTR2_MASK0] = 1, + [TOMTOM_A_INTR2_STATUS0] = 1, + [TOMTOM_A_INTR2_CLEAR0] = 0, + [TOMTOM_A_INTR2_LEVEL0] = 1, + [TOMTOM_A_INTR2_TEST0] = 1, + [TOMTOM_A_INTR2_SET0] = 1, + [TOMTOM_A_CDC_TX_I2S_SCK_MODE] = 1, + [TOMTOM_A_CDC_TX_I2S_WS_MODE] = 1, + [TOMTOM_A_CDC_DMIC_DATA0_MODE] = 1, + [TOMTOM_A_CDC_DMIC_CLK0_MODE] = 1, + [TOMTOM_A_CDC_DMIC_DATA1_MODE] = 1, + [TOMTOM_A_CDC_DMIC_CLK1_MODE] = 1, + [TOMTOM_A_CDC_RX_I2S_SCK_MODE] = 1, + [TOMTOM_A_CDC_RX_I2S_WS_MODE] = 1, + [TOMTOM_A_CDC_DMIC_DATA2_MODE] = 1, + [TOMTOM_A_CDC_DMIC_CLK2_MODE] = 1, + [TOMTOM_A_CDC_INTR1_MODE] = 1, + [TOMTOM_A_CDC_SB_NRZ_SEL_MODE] = 1, + [TOMTOM_A_CDC_INTR2_MODE] = 1, + [TOMTOM_A_CDC_RF_PA_ON_MODE] = 1, + [TOMTOM_A_CDC_BOOST_MODE] = 1, + [TOMTOM_A_CDC_JTCK_MODE] = 1, + [TOMTOM_A_CDC_JTDI_MODE] = 1, + [TOMTOM_A_CDC_JTMS_MODE] = 1, + [TOMTOM_A_CDC_JTDO_MODE] = 1, + [TOMTOM_A_CDC_JTRST_MODE] = 1, + [TOMTOM_A_CDC_BIST_MODE_MODE] = 1, + [TOMTOM_A_CDC_MAD_MAIN_CTL_1] = 1, + [TOMTOM_A_CDC_MAD_MAIN_CTL_2] = 1, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_1] = 1, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_2] = 1, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_3] = 1, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_4] = 1, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_5] = 1, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_6] = 1, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_7] = 1, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_8] = 1, + [TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR] = 1, + [TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL] = 1, + [TOMTOM_A_CDC_MAD_ULTR_CTL_1] = 1, + [TOMTOM_A_CDC_MAD_ULTR_CTL_2] = 1, + [TOMTOM_A_CDC_MAD_ULTR_CTL_3] = 1, + [TOMTOM_A_CDC_MAD_ULTR_CTL_4] = 1, + [TOMTOM_A_CDC_MAD_ULTR_CTL_5] = 1, + [TOMTOM_A_CDC_MAD_ULTR_CTL_6] = 1, + [TOMTOM_A_CDC_MAD_ULTR_CTL_7] = 1, + [TOMTOM_A_CDC_MAD_BEACON_CTL_1] = 1, + [TOMTOM_A_CDC_MAD_BEACON_CTL_2] = 1, + [TOMTOM_A_CDC_MAD_BEACON_CTL_3] = 1, + [TOMTOM_A_CDC_MAD_BEACON_CTL_4] = 1, + [TOMTOM_A_CDC_MAD_BEACON_CTL_5] = 1, + [TOMTOM_A_CDC_MAD_BEACON_CTL_6] = 1, + [TOMTOM_A_CDC_MAD_BEACON_CTL_7] = 1, + [TOMTOM_A_CDC_MAD_BEACON_CTL_8] = 1, + [TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR] = 1, + [TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL] = 1, + [TOMTOM_A_CDC_MAD_INP_SEL] = 1, + [TOMTOM_A_BIAS_REF_CTL] = 1, + [TOMTOM_A_BIAS_CENTRAL_BG_CTL] = 1, + [TOMTOM_A_BIAS_PRECHRG_CTL] = 1, + [TOMTOM_A_BIAS_CURR_CTL_1] = 1, + [TOMTOM_A_BIAS_CURR_CTL_2] = 1, + [TOMTOM_A_BIAS_OSC_BG_CTL] = 1, + [TOMTOM_A_CLK_BUFF_EN1] = 1, + [TOMTOM_A_CLK_BUFF_EN2] = 1, + [TOMTOM_A_LDO_L_MODE_1] = 1, + [TOMTOM_A_LDO_L_MODE_2] = 1, + [TOMTOM_A_LDO_L_CTRL_1] = 1, + [TOMTOM_A_LDO_L_CTRL_2] = 1, + [TOMTOM_A_LDO_L_CTRL_3] = 1, + [TOMTOM_A_LDO_L_CTRL_4] = 1, + [TOMTOM_A_LDO_H_MODE_1] = 1, + [TOMTOM_A_LDO_H_MODE_2] = 1, + [TOMTOM_A_LDO_H_LOOP_CTL] = 1, + [TOMTOM_A_LDO_H_COMP_1] = 1, + [TOMTOM_A_LDO_H_COMP_2] = 1, + [TOMTOM_A_LDO_H_BIAS_1] = 1, + [TOMTOM_A_LDO_H_BIAS_2] = 1, + [TOMTOM_A_LDO_H_BIAS_3] = 1, + [TOMTOM_A_VBAT_CLK] = 1, + [TOMTOM_A_VBAT_LOOP] = 1, + [TOMTOM_A_VBAT_REF] = 1, + [TOMTOM_A_VBAT_ADC_TEST] = 1, + [TOMTOM_A_VBAT_FE] = 1, + [TOMTOM_A_VBAT_BIAS_1] = 1, + [TOMTOM_A_VBAT_BIAS_2] = 1, + [TOMTOM_A_VBAT_ADC_DATA_MSB] = 1, + [TOMTOM_A_VBAT_ADC_DATA_LSB] = 1, + [TOMTOM_A_FLL_NREF] = 1, + [TOMTOM_A_FLL_KDCO_TUNE] = 1, + [TOMTOM_A_FLL_LOCK_THRESH] = 1, + [TOMTOM_A_FLL_LOCK_DET_COUNT] = 1, + [TOMTOM_A_FLL_DAC_THRESHOLD] = 1, + [TOMTOM_A_FLL_TEST_DCO_FREERUN] = 1, + [TOMTOM_A_FLL_TEST_ENABLE] = 1, + [TOMTOM_A_MICB_CFILT_1_CTL] = 1, + [TOMTOM_A_MICB_CFILT_1_VAL] = 1, + [TOMTOM_A_MICB_CFILT_1_PRECHRG] = 1, + [TOMTOM_A_MICB_1_CTL] = 1, + [TOMTOM_A_MICB_1_INT_RBIAS] = 1, + [TOMTOM_A_MICB_1_MBHC] = 1, + [TOMTOM_A_MICB_CFILT_2_CTL] = 1, + [TOMTOM_A_MICB_CFILT_2_VAL] = 1, + [TOMTOM_A_MICB_CFILT_2_PRECHRG] = 1, + [TOMTOM_A_MICB_2_CTL] = 1, + [TOMTOM_A_MICB_2_INT_RBIAS] = 1, + [TOMTOM_A_MICB_2_MBHC] = 1, + [TOMTOM_A_MICB_CFILT_3_CTL] = 1, + [TOMTOM_A_MICB_CFILT_3_VAL] = 1, + [TOMTOM_A_MICB_CFILT_3_PRECHRG] = 1, + [TOMTOM_A_MICB_3_CTL] = 1, + [TOMTOM_A_MICB_3_INT_RBIAS] = 1, + [TOMTOM_A_MICB_3_MBHC] = 1, + [TOMTOM_A_MICB_4_CTL] = 1, + [TOMTOM_A_MICB_4_INT_RBIAS] = 1, + [TOMTOM_A_MICB_4_MBHC] = 1, + [TOMTOM_A_SPKR_DRV2_EN] = 1, + [TOMTOM_A_SPKR_DRV2_GAIN] = 1, + [TOMTOM_A_SPKR_DRV2_DAC_CTL] = 1, + [TOMTOM_A_SPKR_DRV2_OCP_CTL] = 1, + [TOMTOM_A_SPKR_DRV2_CLIP_DET] = 1, + [TOMTOM_A_SPKR_DRV2_DBG_DAC] = 1, + [TOMTOM_A_SPKR_DRV2_DBG_PA] = 1, + [TOMTOM_A_SPKR_DRV2_DBG_PWRSTG] = 1, + [TOMTOM_A_SPKR_DRV2_BIAS_LDO] = 1, + [TOMTOM_A_SPKR_DRV2_BIAS_INT] = 1, + [TOMTOM_A_SPKR_DRV2_BIAS_PA] = 1, + [TOMTOM_A_SPKR_DRV2_STATUS_OCP] = 1, + [TOMTOM_A_SPKR_DRV2_STATUS_PA] = 1, + [TOMTOM_A_MBHC_INSERT_DETECT] = 1, + [TOMTOM_A_MBHC_INSERT_DET_STATUS] = 1, + [TOMTOM_A_TX_COM_BIAS] = 1, + [TOMTOM_A_MBHC_INSERT_DETECT2] = 1, + [TOMTOM_A_MBHC_SCALING_MUX_1] = 1, + [TOMTOM_A_MBHC_SCALING_MUX_2] = 1, + [TOMTOM_A_MAD_ANA_CTRL] = 1, + [TOMTOM_A_TX_SUP_SWITCH_CTRL_1] = 1, + [TOMTOM_A_TX_SUP_SWITCH_CTRL_2] = 1, + [TOMTOM_A_TX_1_GAIN] = 1, + [TOMTOM_A_TX_1_2_TEST_EN] = 1, + [TOMTOM_A_TX_2_GAIN] = 1, + [TOMTOM_A_TX_1_2_ADC_IB] = 1, + [TOMTOM_A_TX_1_2_ATEST_REFCTRL] = 1, + [TOMTOM_A_TX_1_2_TEST_CTL] = 1, + [TOMTOM_A_TX_1_2_TEST_BLOCK_EN] = 1, + [TOMTOM_A_TX_1_2_TXFE_CLKDIV] = 1, + [TOMTOM_A_TX_1_2_SAR_ERR_CH1] = 1, + [TOMTOM_A_TX_1_2_SAR_ERR_CH2] = 1, + [TOMTOM_A_TX_3_GAIN] = 1, + [TOMTOM_A_TX_3_4_TEST_EN] = 1, + [TOMTOM_A_TX_4_GAIN] = 1, + [TOMTOM_A_TX_3_4_ADC_IB] = 1, + [TOMTOM_A_TX_3_4_ATEST_REFCTRL] = 1, + [TOMTOM_A_TX_3_4_TEST_CTL] = 1, + [TOMTOM_A_TX_3_4_TEST_BLOCK_EN] = 1, + [TOMTOM_A_TX_3_4_TXFE_CKDIV] = 1, + [TOMTOM_A_TX_3_4_SAR_ERR_CH3] = 1, + [TOMTOM_A_TX_3_4_SAR_ERR_CH4] = 1, + [TOMTOM_A_TX_5_GAIN] = 1, + [TOMTOM_A_TX_5_6_TEST_EN] = 1, + [TOMTOM_A_TX_6_GAIN] = 1, + [TOMTOM_A_TX_5_6_ADC_IB] = 1, + [TOMTOM_A_TX_5_6_ATEST_REFCTRL] = 1, + [TOMTOM_A_TX_5_6_TEST_CTL] = 1, + [TOMTOM_A_TX_5_6_TEST_BLOCK_EN] = 1, + [TOMTOM_A_TX_5_6_TXFE_CKDIV] = 1, + [TOMTOM_A_TX_5_6_SAR_ERR_CH5] = 1, + [TOMTOM_A_TX_5_6_SAR_ERR_CH6] = 1, + [TOMTOM_A_TX_7_MBHC_EN] = 1, + [TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL] = 1, + [TOMTOM_A_TX_7_MBHC_ADC] = 1, + [TOMTOM_A_TX_7_MBHC_TEST_CTL] = 1, + [TOMTOM_A_TX_7_MBHC_SAR_ERR] = 1, + [TOMTOM_A_TX_7_TXFE_CLKDIV] = 1, + [TOMTOM_A_RCO_CTRL] = 1, + [TOMTOM_A_RCO_CALIBRATION_CTRL1] = 1, + [TOMTOM_A_RCO_CALIBRATION_CTRL2] = 1, + [TOMTOM_A_RCO_CALIBRATION_CTRL3] = 1, + [TOMTOM_A_RCO_TEST_CTRL] = 1, + [TOMTOM_A_RCO_CALIBRATION_RESULT1] = 1, + [TOMTOM_A_RCO_CALIBRATION_RESULT2] = 1, + [TOMTOM_A_BUCK_MODE_1] = 1, + [TOMTOM_A_BUCK_MODE_2] = 1, + [TOMTOM_A_BUCK_MODE_3] = 1, + [TOMTOM_A_BUCK_MODE_4] = 1, + [TOMTOM_A_BUCK_MODE_5] = 1, + [TOMTOM_A_BUCK_CTRL_VCL_1] = 1, + [TOMTOM_A_BUCK_CTRL_VCL_2] = 1, + [TOMTOM_A_BUCK_CTRL_VCL_3] = 1, + [TOMTOM_A_BUCK_CTRL_CCL_1] = 1, + [TOMTOM_A_BUCK_CTRL_CCL_2] = 1, + [TOMTOM_A_BUCK_CTRL_CCL_3] = 1, + [TOMTOM_A_BUCK_CTRL_CCL_4] = 1, + [TOMTOM_A_BUCK_CTRL_PWM_DRVR_1] = 1, + [TOMTOM_A_BUCK_CTRL_PWM_DRVR_2] = 1, + [TOMTOM_A_BUCK_CTRL_PWM_DRVR_3] = 1, + [TOMTOM_A_BUCK_TMUX_A_D] = 1, + [TOMTOM_A_NCP_BUCKREF] = 1, + [TOMTOM_A_NCP_EN] = 1, + [TOMTOM_A_NCP_CLK] = 1, + [TOMTOM_A_NCP_STATIC] = 1, + [TOMTOM_A_NCP_VTH_LOW] = 1, + [TOMTOM_A_NCP_VTH_HIGH] = 1, + [TOMTOM_A_NCP_ATEST] = 1, + [TOMTOM_A_NCP_DTEST] = 1, + [TOMTOM_A_NCP_DLY1] = 1, + [TOMTOM_A_NCP_DLY2] = 1, + [TOMTOM_A_RX_AUX_SW_CTL] = 1, + [TOMTOM_A_RX_PA_AUX_IN_CONN] = 1, + [TOMTOM_A_RX_COM_TIMER_DIV] = 1, + [TOMTOM_A_RX_COM_OCP_CTL] = 1, + [TOMTOM_A_RX_COM_OCP_COUNT] = 1, + [TOMTOM_A_RX_COM_DAC_CTL] = 1, + [TOMTOM_A_RX_COM_BIAS] = 1, + [TOMTOM_A_RX_HPH_AUTO_CHOP] = 1, + [TOMTOM_A_RX_HPH_CHOP_CTL] = 1, + [TOMTOM_A_RX_HPH_BIAS_PA] = 1, + [TOMTOM_A_RX_HPH_BIAS_LDO] = 1, + [TOMTOM_A_RX_HPH_BIAS_CNP] = 1, + [TOMTOM_A_RX_HPH_BIAS_WG_OCP] = 1, + [TOMTOM_A_RX_HPH_OCP_CTL] = 1, + [TOMTOM_A_RX_HPH_CNP_EN] = 1, + [TOMTOM_A_RX_HPH_CNP_WG_CTL] = 1, + [TOMTOM_A_RX_HPH_CNP_WG_TIME] = 1, + [TOMTOM_A_RX_HPH_L_GAIN] = 1, + [TOMTOM_A_RX_HPH_L_TEST] = 1, + [TOMTOM_A_RX_HPH_L_PA_CTL] = 1, + [TOMTOM_A_RX_HPH_L_DAC_CTL] = 1, + [TOMTOM_A_RX_HPH_L_ATEST] = 1, + [TOMTOM_A_RX_HPH_L_STATUS] = 1, + [TOMTOM_A_RX_HPH_R_GAIN] = 1, + [TOMTOM_A_RX_HPH_R_TEST] = 1, + [TOMTOM_A_RX_HPH_R_PA_CTL] = 1, + [TOMTOM_A_RX_HPH_R_DAC_CTL] = 1, + [TOMTOM_A_RX_HPH_R_ATEST] = 1, + [TOMTOM_A_RX_HPH_R_STATUS] = 1, + [TOMTOM_A_RX_EAR_BIAS_PA] = 1, + [TOMTOM_A_RX_EAR_BIAS_CMBUFF] = 1, + [TOMTOM_A_RX_EAR_EN] = 1, + [TOMTOM_A_RX_EAR_GAIN] = 1, + [TOMTOM_A_RX_EAR_CMBUFF] = 1, + [TOMTOM_A_RX_EAR_ICTL] = 1, + [TOMTOM_A_RX_EAR_CCOMP] = 1, + [TOMTOM_A_RX_EAR_VCM] = 1, + [TOMTOM_A_RX_EAR_CNP] = 1, + [TOMTOM_A_RX_EAR_DAC_CTL_ATEST] = 1, + [TOMTOM_A_RX_EAR_STATUS] = 1, + [TOMTOM_A_RX_LINE_BIAS_PA] = 1, + [TOMTOM_A_RX_BUCK_BIAS1] = 1, + [TOMTOM_A_RX_BUCK_BIAS2] = 1, + [TOMTOM_A_RX_LINE_COM] = 1, + [TOMTOM_A_RX_LINE_CNP_EN] = 1, + [TOMTOM_A_RX_LINE_CNP_WG_CTL] = 1, + [TOMTOM_A_RX_LINE_CNP_WG_TIME] = 1, + [TOMTOM_A_RX_LINE_1_GAIN] = 1, + [TOMTOM_A_RX_LINE_1_TEST] = 1, + [TOMTOM_A_RX_LINE_1_DAC_CTL] = 1, + [TOMTOM_A_RX_LINE_1_STATUS] = 1, + [TOMTOM_A_RX_LINE_2_GAIN] = 1, + [TOMTOM_A_RX_LINE_2_TEST] = 1, + [TOMTOM_A_RX_LINE_2_DAC_CTL] = 1, + [TOMTOM_A_RX_LINE_2_STATUS] = 1, + [TOMTOM_A_RX_LINE_3_GAIN] = 1, + [TOMTOM_A_RX_LINE_3_TEST] = 1, + [TOMTOM_A_RX_LINE_3_DAC_CTL] = 1, + [TOMTOM_A_RX_LINE_3_STATUS] = 1, + [TOMTOM_A_RX_LINE_4_GAIN] = 1, + [TOMTOM_A_RX_LINE_4_TEST] = 1, + [TOMTOM_A_RX_LINE_4_DAC_CTL] = 1, + [TOMTOM_A_RX_LINE_4_STATUS] = 1, + [TOMTOM_A_RX_LINE_CNP_DBG] = 1, + [TOMTOM_A_SPKR_DRV1_EN] = 1, + [TOMTOM_A_SPKR_DRV1_GAIN] = 1, + [TOMTOM_A_SPKR_DRV1_DAC_CTL] = 1, + [TOMTOM_A_SPKR_DRV1_OCP_CTL] = 1, + [TOMTOM_A_SPKR_DRV1_CLIP_DET] = 1, + [TOMTOM_A_SPKR_DRV1_IEC] = 1, + [TOMTOM_A_SPKR_DRV1_DBG_DAC] = 1, + [TOMTOM_A_SPKR_DRV1_DBG_PA] = 1, + [TOMTOM_A_SPKR_DRV1_DBG_PWRSTG] = 1, + [TOMTOM_A_SPKR_DRV1_BIAS_LDO] = 1, + [TOMTOM_A_SPKR_DRV1_BIAS_INT] = 1, + [TOMTOM_A_SPKR_DRV1_BIAS_PA] = 1, + [TOMTOM_A_SPKR_DRV1_STATUS_OCP] = 1, + [TOMTOM_A_SPKR_DRV1_STATUS_PA] = 1, + [TOMTOM_A_SPKR1_PROT_EN] = 1, + [TOMTOM_A_SPKR1_PROT_ADC_TEST_EN] = 1, + [TOMTOM_A_SPKR1_PROT_ATEST] = 1, + [TOMTOM_A_SPKR1_PROT_LDO_CTRL] = 1, + [TOMTOM_A_SPKR1_PROT_ISENSE_CTRL] = 1, + [TOMTOM_A_SPKR1_PROT_VSENSE_CTRL] = 1, + [TOMTOM_A_SPKR2_PROT_EN] = 1, + [TOMTOM_A_SPKR2_PROT_ADC_TEST_EN] = 1, + [TOMTOM_A_SPKR2_PROT_ATEST] = 1, + [TOMTOM_A_SPKR2_PROT_LDO_CTRL] = 1, + [TOMTOM_A_SPKR2_PROT_ISENSE_CTRL] = 1, + [TOMTOM_A_SPKR2_PROT_VSENSE_CTRL] = 1, + [TOMTOM_A_MBHC_HPH] = 1, + [TOMTOM_A_CDC_ANC1_B1_CTL] = 1, + [TOMTOM_A_CDC_ANC2_B1_CTL] = 1, + [TOMTOM_A_CDC_ANC1_SHIFT] = 1, + [TOMTOM_A_CDC_ANC2_SHIFT] = 1, + [TOMTOM_A_CDC_ANC1_IIR_B1_CTL] = 1, + [TOMTOM_A_CDC_ANC2_IIR_B1_CTL] = 1, + [TOMTOM_A_CDC_ANC1_IIR_B2_CTL] = 1, + [TOMTOM_A_CDC_ANC2_IIR_B2_CTL] = 1, + [TOMTOM_A_CDC_ANC1_IIR_B3_CTL] = 1, + [TOMTOM_A_CDC_ANC2_IIR_B3_CTL] = 1, + [TOMTOM_A_CDC_ANC1_LPF_B1_CTL] = 1, + [TOMTOM_A_CDC_ANC2_LPF_B1_CTL] = 1, + [TOMTOM_A_CDC_ANC1_LPF_B2_CTL] = 1, + [TOMTOM_A_CDC_ANC2_LPF_B2_CTL] = 1, + [TOMTOM_A_CDC_ANC1_SPARE] = 1, + [TOMTOM_A_CDC_ANC2_SPARE] = 1, + [TOMTOM_A_CDC_ANC1_SMLPF_CTL] = 1, + [TOMTOM_A_CDC_ANC2_SMLPF_CTL] = 1, + [TOMTOM_A_CDC_ANC1_DCFLT_CTL] = 1, + [TOMTOM_A_CDC_ANC2_DCFLT_CTL] = 1, + [TOMTOM_A_CDC_ANC1_GAIN_CTL] = 1, + [TOMTOM_A_CDC_ANC2_GAIN_CTL] = 1, + [TOMTOM_A_CDC_ANC1_B2_CTL] = 1, + [TOMTOM_A_CDC_ANC2_B2_CTL] = 1, + [TOMTOM_A_CDC_TX1_VOL_CTL_TIMER] = 1, + [TOMTOM_A_CDC_TX2_VOL_CTL_TIMER] = 1, + [TOMTOM_A_CDC_TX3_VOL_CTL_TIMER] = 1, + [TOMTOM_A_CDC_TX4_VOL_CTL_TIMER] = 1, + [TOMTOM_A_CDC_TX5_VOL_CTL_TIMER] = 1, + [TOMTOM_A_CDC_TX6_VOL_CTL_TIMER] = 1, + [TOMTOM_A_CDC_TX7_VOL_CTL_TIMER] = 1, + [TOMTOM_A_CDC_TX8_VOL_CTL_TIMER] = 1, + [TOMTOM_A_CDC_TX9_VOL_CTL_TIMER] = 1, + [TOMTOM_A_CDC_TX10_VOL_CTL_TIMER] = 1, + [TOMTOM_A_CDC_TX1_VOL_CTL_GAIN] = 1, + [TOMTOM_A_CDC_TX2_VOL_CTL_GAIN] = 1, + [TOMTOM_A_CDC_TX3_VOL_CTL_GAIN] = 1, + [TOMTOM_A_CDC_TX4_VOL_CTL_GAIN] = 1, + [TOMTOM_A_CDC_TX5_VOL_CTL_GAIN] = 1, + [TOMTOM_A_CDC_TX6_VOL_CTL_GAIN] = 1, + [TOMTOM_A_CDC_TX7_VOL_CTL_GAIN] = 1, + [TOMTOM_A_CDC_TX8_VOL_CTL_GAIN] = 1, + [TOMTOM_A_CDC_TX9_VOL_CTL_GAIN] = 1, + [TOMTOM_A_CDC_TX10_VOL_CTL_GAIN] = 1, + [TOMTOM_A_CDC_TX1_VOL_CTL_CFG] = 1, + [TOMTOM_A_CDC_TX2_VOL_CTL_CFG] = 1, + [TOMTOM_A_CDC_TX3_VOL_CTL_CFG] = 1, + [TOMTOM_A_CDC_TX4_VOL_CTL_CFG] = 1, + [TOMTOM_A_CDC_TX5_VOL_CTL_CFG] = 1, + [TOMTOM_A_CDC_TX6_VOL_CTL_CFG] = 1, + [TOMTOM_A_CDC_TX7_VOL_CTL_CFG] = 1, + [TOMTOM_A_CDC_TX8_VOL_CTL_CFG] = 1, + [TOMTOM_A_CDC_TX9_VOL_CTL_CFG] = 1, + [TOMTOM_A_CDC_TX10_VOL_CTL_CFG] = 1, + [TOMTOM_A_CDC_TX1_MUX_CTL] = 1, + [TOMTOM_A_CDC_TX2_MUX_CTL] = 1, + [TOMTOM_A_CDC_TX3_MUX_CTL] = 1, + [TOMTOM_A_CDC_TX4_MUX_CTL] = 1, + [TOMTOM_A_CDC_TX5_MUX_CTL] = 1, + [TOMTOM_A_CDC_TX6_MUX_CTL] = 1, + [TOMTOM_A_CDC_TX7_MUX_CTL] = 1, + [TOMTOM_A_CDC_TX8_MUX_CTL] = 1, + [TOMTOM_A_CDC_TX9_MUX_CTL] = 1, + [TOMTOM_A_CDC_TX10_MUX_CTL] = 1, + [TOMTOM_A_CDC_TX1_CLK_FS_CTL] = 1, + [TOMTOM_A_CDC_TX2_CLK_FS_CTL] = 1, + [TOMTOM_A_CDC_TX3_CLK_FS_CTL] = 1, + [TOMTOM_A_CDC_TX4_CLK_FS_CTL] = 1, + [TOMTOM_A_CDC_TX5_CLK_FS_CTL] = 1, + [TOMTOM_A_CDC_TX6_CLK_FS_CTL] = 1, + [TOMTOM_A_CDC_TX7_CLK_FS_CTL] = 1, + [TOMTOM_A_CDC_TX8_CLK_FS_CTL] = 1, + [TOMTOM_A_CDC_TX9_CLK_FS_CTL] = 1, + [TOMTOM_A_CDC_TX10_CLK_FS_CTL] = 1, + [TOMTOM_A_CDC_TX1_DMIC_CTL] = 1, + [TOMTOM_A_CDC_TX2_DMIC_CTL] = 1, + [TOMTOM_A_CDC_TX3_DMIC_CTL] = 1, + [TOMTOM_A_CDC_TX4_DMIC_CTL] = 1, + [TOMTOM_A_CDC_TX5_DMIC_CTL] = 1, + [TOMTOM_A_CDC_TX6_DMIC_CTL] = 1, + [TOMTOM_A_CDC_TX7_DMIC_CTL] = 1, + [TOMTOM_A_CDC_TX8_DMIC_CTL] = 1, + [TOMTOM_A_CDC_TX9_DMIC_CTL] = 1, + [TOMTOM_A_CDC_TX10_DMIC_CTL] = 1, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL0] = 1, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL1] = 1, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL2] = 1, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL3] = 1, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL4] = 1, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL5] = 1, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL6] = 1, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL7] = 1, + [TOMTOM_A_CDC_DEBUG_B1_CTL] = 1, + [TOMTOM_A_CDC_DEBUG_B2_CTL] = 1, + [TOMTOM_A_CDC_DEBUG_B3_CTL] = 1, + [TOMTOM_A_CDC_DEBUG_B4_CTL] = 1, + [TOMTOM_A_CDC_DEBUG_B5_CTL] = 1, + [TOMTOM_A_CDC_DEBUG_B6_CTL] = 1, + [TOMTOM_A_CDC_DEBUG_B7_CTL] = 1, + [TOMTOM_A_CDC_SRC1_PDA_CFG] = 1, + [TOMTOM_A_CDC_SRC2_PDA_CFG] = 1, + [TOMTOM_A_CDC_SRC1_FS_CTL] = 1, + [TOMTOM_A_CDC_SRC2_FS_CTL] = 1, + [TOMTOM_A_CDC_RX1_B1_CTL] = 1, + [TOMTOM_A_CDC_RX2_B1_CTL] = 1, + [TOMTOM_A_CDC_RX3_B1_CTL] = 1, + [TOMTOM_A_CDC_RX4_B1_CTL] = 1, + [TOMTOM_A_CDC_RX5_B1_CTL] = 1, + [TOMTOM_A_CDC_RX6_B1_CTL] = 1, + [TOMTOM_A_CDC_RX7_B1_CTL] = 1, + [TOMTOM_A_CDC_RX1_B2_CTL] = 1, + [TOMTOM_A_CDC_RX2_B2_CTL] = 1, + [TOMTOM_A_CDC_RX3_B2_CTL] = 1, + [TOMTOM_A_CDC_RX4_B2_CTL] = 1, + [TOMTOM_A_CDC_RX5_B2_CTL] = 1, + [TOMTOM_A_CDC_RX6_B2_CTL] = 1, + [TOMTOM_A_CDC_RX7_B2_CTL] = 1, + [TOMTOM_A_CDC_RX1_B3_CTL] = 1, + [TOMTOM_A_CDC_RX2_B3_CTL] = 1, + [TOMTOM_A_CDC_RX3_B3_CTL] = 1, + [TOMTOM_A_CDC_RX4_B3_CTL] = 1, + [TOMTOM_A_CDC_RX5_B3_CTL] = 1, + [TOMTOM_A_CDC_RX6_B3_CTL] = 1, + [TOMTOM_A_CDC_RX7_B3_CTL] = 1, + [TOMTOM_A_CDC_RX1_B4_CTL] = 1, + [TOMTOM_A_CDC_RX2_B4_CTL] = 1, + [TOMTOM_A_CDC_RX3_B4_CTL] = 1, + [TOMTOM_A_CDC_RX4_B4_CTL] = 1, + [TOMTOM_A_CDC_RX5_B4_CTL] = 1, + [TOMTOM_A_CDC_RX6_B4_CTL] = 1, + [TOMTOM_A_CDC_RX7_B4_CTL] = 1, + [TOMTOM_A_CDC_RX1_B5_CTL] = 1, + [TOMTOM_A_CDC_RX2_B5_CTL] = 1, + [TOMTOM_A_CDC_RX3_B5_CTL] = 1, + [TOMTOM_A_CDC_RX4_B5_CTL] = 1, + [TOMTOM_A_CDC_RX5_B5_CTL] = 1, + [TOMTOM_A_CDC_RX6_B5_CTL] = 1, + [TOMTOM_A_CDC_RX7_B5_CTL] = 1, + [TOMTOM_A_CDC_RX1_B6_CTL] = 1, + [TOMTOM_A_CDC_RX2_B6_CTL] = 1, + [TOMTOM_A_CDC_RX3_B6_CTL] = 1, + [TOMTOM_A_CDC_RX4_B6_CTL] = 1, + [TOMTOM_A_CDC_RX5_B6_CTL] = 1, + [TOMTOM_A_CDC_RX6_B6_CTL] = 1, + [TOMTOM_A_CDC_RX7_B6_CTL] = 1, + [TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL] = 1, + [TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL] = 1, + [TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL] = 1, + [TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL] = 1, + [TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL] = 1, + [TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL] = 1, + [TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL] = 1, + [TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL] = 1, + [TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL] = 1, + [TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL] = 1, + [TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL] = 1, + [TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL] = 1, + [TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL] = 1, + [TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL] = 1, + [TOMTOM_A_CDC_VBAT_CFG] = 1, + [TOMTOM_A_CDC_VBAT_ADC_CAL1] = 1, + [TOMTOM_A_CDC_VBAT_ADC_CAL2] = 1, + [TOMTOM_A_CDC_VBAT_ADC_CAL3] = 1, + [TOMTOM_A_CDC_VBAT_PK_EST1] = 1, + [TOMTOM_A_CDC_VBAT_PK_EST2] = 1, + [TOMTOM_A_CDC_VBAT_PK_EST3] = 1, + [TOMTOM_A_CDC_VBAT_RF_PROC1] = 1, + [TOMTOM_A_CDC_VBAT_RF_PROC2] = 1, + [TOMTOM_A_CDC_VBAT_TAC1] = 1, + [TOMTOM_A_CDC_VBAT_TAC2] = 1, + [TOMTOM_A_CDC_VBAT_TAC3] = 1, + [TOMTOM_A_CDC_VBAT_TAC4] = 1, + [TOMTOM_A_CDC_VBAT_GAIN_UPD1] = 1, + [TOMTOM_A_CDC_VBAT_GAIN_UPD2] = 1, + [TOMTOM_A_CDC_VBAT_GAIN_UPD3] = 1, + [TOMTOM_A_CDC_VBAT_GAIN_UPD4] = 1, + [TOMTOM_A_CDC_VBAT_DEBUG1] = 1, + [TOMTOM_A_CDC_VBAT_GAIN_UPD_MON] = 0, + [TOMTOM_A_CDC_VBAT_GAIN_MON_VAL] = 1, + [TOMTOM_A_CDC_CLK_ANC_RESET_CTL] = 1, + [TOMTOM_A_CDC_CLK_RX_RESET_CTL] = 1, + [TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL] = 1, + [TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL] = 1, + [TOMTOM_A_CDC_CLK_RX_I2S_CTL] = 1, + [TOMTOM_A_CDC_CLK_TX_I2S_CTL] = 1, + [TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL] = 1, + [TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL] = 1, + [TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL] = 1, + [TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL] = 1, + [TOMTOM_A_CDC_CLK_OTHR_CTL] = 1, + [TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL] = 1, + [TOMTOM_A_CDC_CLK_RX_B1_CTL] = 1, + [TOMTOM_A_CDC_CLK_RX_B2_CTL] = 1, + [TOMTOM_A_CDC_CLK_MCLK_CTL] = 1, + [TOMTOM_A_CDC_CLK_PDM_CTL] = 1, + [TOMTOM_A_CDC_CLK_SD_CTL] = 1, + [TOMTOM_A_CDC_CLSH_B1_CTL] = 1, + [TOMTOM_A_CDC_CLSH_B2_CTL] = 1, + [TOMTOM_A_CDC_CLSH_B3_CTL] = 1, + [TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS] = 1, + [TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD] = 1, + [TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD] = 1, + [TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD] = 1, + [TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD] = 1, + [TOMTOM_A_CDC_CLSH_K_ADDR] = 1, + [TOMTOM_A_CDC_CLSH_K_DATA] = 1, + [TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L] = 1, + [TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U] = 1, + [TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L] = 1, + [TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U] = 1, + [TOMTOM_A_CDC_CLSH_V_PA_HD_EAR] = 1, + [TOMTOM_A_CDC_CLSH_V_PA_HD_HPH] = 1, + [TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR] = 1, + [TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH] = 1, + [TOMTOM_A_CDC_IIR1_GAIN_B1_CTL] = 1, + [TOMTOM_A_CDC_IIR2_GAIN_B1_CTL] = 1, + [TOMTOM_A_CDC_IIR1_GAIN_B2_CTL] = 1, + [TOMTOM_A_CDC_IIR2_GAIN_B2_CTL] = 1, + [TOMTOM_A_CDC_IIR1_GAIN_B3_CTL] = 1, + [TOMTOM_A_CDC_IIR2_GAIN_B3_CTL] = 1, + [TOMTOM_A_CDC_IIR1_GAIN_B4_CTL] = 1, + [TOMTOM_A_CDC_IIR2_GAIN_B4_CTL] = 1, + [TOMTOM_A_CDC_IIR1_GAIN_B5_CTL] = 1, + [TOMTOM_A_CDC_IIR2_GAIN_B5_CTL] = 1, + [TOMTOM_A_CDC_IIR1_GAIN_B6_CTL] = 1, + [TOMTOM_A_CDC_IIR2_GAIN_B6_CTL] = 1, + [TOMTOM_A_CDC_IIR1_GAIN_B7_CTL] = 1, + [TOMTOM_A_CDC_IIR2_GAIN_B7_CTL] = 1, + [TOMTOM_A_CDC_IIR1_GAIN_B8_CTL] = 1, + [TOMTOM_A_CDC_IIR2_GAIN_B8_CTL] = 1, + [TOMTOM_A_CDC_IIR1_CTL] = 1, + [TOMTOM_A_CDC_IIR2_CTL] = 1, + [TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL] = 1, + [TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL] = 1, + [TOMTOM_A_CDC_IIR1_COEF_B1_CTL] = 1, + [TOMTOM_A_CDC_IIR2_COEF_B1_CTL] = 1, + [TOMTOM_A_CDC_IIR1_COEF_B2_CTL] = 1, + [TOMTOM_A_CDC_IIR2_COEF_B2_CTL] = 1, + [TOMTOM_A_CDC_TOP_GAIN_UPDATE] = 1, + [TOMTOM_A_CDC_PA_RAMP_B1_CTL] = 1, + [TOMTOM_A_CDC_PA_RAMP_B2_CTL] = 1, + [TOMTOM_A_CDC_PA_RAMP_B3_CTL] = 1, + [TOMTOM_A_CDC_PA_RAMP_B4_CTL] = 1, + [TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL] = 1, + [TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL] = 1, + [TOMTOM_A_CDC_COMP0_B1_CTL] = 1, + [TOMTOM_A_CDC_COMP1_B1_CTL] = 1, + [TOMTOM_A_CDC_COMP2_B1_CTL] = 1, + [TOMTOM_A_CDC_COMP0_B2_CTL] = 1, + [TOMTOM_A_CDC_COMP1_B2_CTL] = 1, + [TOMTOM_A_CDC_COMP2_B2_CTL] = 1, + [TOMTOM_A_CDC_COMP0_B3_CTL] = 1, + [TOMTOM_A_CDC_COMP1_B3_CTL] = 1, + [TOMTOM_A_CDC_COMP2_B3_CTL] = 1, + [TOMTOM_A_CDC_COMP0_B4_CTL] = 1, + [TOMTOM_A_CDC_COMP1_B4_CTL] = 1, + [TOMTOM_A_CDC_COMP2_B4_CTL] = 1, + [TOMTOM_A_CDC_COMP0_B5_CTL] = 1, + [TOMTOM_A_CDC_COMP1_B5_CTL] = 1, + [TOMTOM_A_CDC_COMP2_B5_CTL] = 1, + [TOMTOM_A_CDC_COMP0_B6_CTL] = 1, + [TOMTOM_A_CDC_COMP1_B6_CTL] = 1, + [TOMTOM_A_CDC_COMP2_B6_CTL] = 1, + [TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS] = 1, + [TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS] = 1, + [TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS] = 1, + [TOMTOM_A_CDC_COMP0_FS_CFG] = 1, + [TOMTOM_A_CDC_COMP1_FS_CFG] = 1, + [TOMTOM_A_CDC_COMP2_FS_CFG] = 1, + [TOMTOM_A_CDC_CONN_RX1_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX1_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX1_B3_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX2_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX2_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX2_B3_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX3_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX3_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX4_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX4_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX5_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX5_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX6_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX6_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX7_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX7_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX7_B3_CTL] = 1, + [TOMTOM_A_CDC_CONN_ANC_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_ANC_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_B3_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_B4_CTL] = 1, + [TOMTOM_A_CDC_CONN_EQ1_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_EQ1_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_EQ1_B3_CTL] = 1, + [TOMTOM_A_CDC_CONN_EQ1_B4_CTL] = 1, + [TOMTOM_A_CDC_CONN_EQ2_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_EQ2_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_EQ2_B3_CTL] = 1, + [TOMTOM_A_CDC_CONN_EQ2_B4_CTL] = 1, + [TOMTOM_A_CDC_CONN_SRC1_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_SRC1_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_SRC2_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_SRC2_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_SB_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_SB_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_SB_B3_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_SB_B4_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_SB_B5_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_SB_B6_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_SB_B7_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_SB_B8_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_SB_B9_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_SB_B10_CTL] = 1, + [TOMTOM_A_CDC_CONN_TX_SB_B11_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX_SB_B1_CTL] = 1, + [TOMTOM_A_CDC_CONN_RX_SB_B2_CTL] = 1, + [TOMTOM_A_CDC_CONN_CLSH_CTL] = 1, + [TOMTOM_A_CDC_CONN_MISC] = 1, + [TOMTOM_A_CDC_CONN_RX8_B1_CTL] = 1, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL] = 1, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST] = 1, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD] = 1, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS] = 1, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK] = 1, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING] = 1, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL] = 1, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST] = 1, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD] = 1, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS] = 1, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK] = 1, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING] = 1, + [TOMTOM_A_CDC_MBHC_EN_CTL] = 1, + [TOMTOM_A_CDC_MBHC_FIR_B1_CFG] = 1, + [TOMTOM_A_CDC_MBHC_FIR_B2_CFG] = 1, + [TOMTOM_A_CDC_MBHC_TIMER_B1_CTL] = 1, + [TOMTOM_A_CDC_MBHC_TIMER_B2_CTL] = 1, + [TOMTOM_A_CDC_MBHC_TIMER_B3_CTL] = 1, + [TOMTOM_A_CDC_MBHC_TIMER_B4_CTL] = 1, + [TOMTOM_A_CDC_MBHC_TIMER_B5_CTL] = 1, + [TOMTOM_A_CDC_MBHC_TIMER_B6_CTL] = 1, + [TOMTOM_A_CDC_MBHC_B1_STATUS] = 1, + [TOMTOM_A_CDC_MBHC_B2_STATUS] = 1, + [TOMTOM_A_CDC_MBHC_B3_STATUS] = 1, + [TOMTOM_A_CDC_MBHC_B4_STATUS] = 1, + [TOMTOM_A_CDC_MBHC_B5_STATUS] = 1, + [TOMTOM_A_CDC_MBHC_B1_CTL] = 1, + [TOMTOM_A_CDC_MBHC_B2_CTL] = 1, + [TOMTOM_A_CDC_MBHC_VOLT_B1_CTL] = 1, + [TOMTOM_A_CDC_MBHC_VOLT_B2_CTL] = 1, + [TOMTOM_A_CDC_MBHC_VOLT_B3_CTL] = 1, + [TOMTOM_A_CDC_MBHC_VOLT_B4_CTL] = 1, + [TOMTOM_A_CDC_MBHC_VOLT_B5_CTL] = 1, + [TOMTOM_A_CDC_MBHC_VOLT_B6_CTL] = 1, + [TOMTOM_A_CDC_MBHC_VOLT_B7_CTL] = 1, + [TOMTOM_A_CDC_MBHC_VOLT_B8_CTL] = 1, + [TOMTOM_A_CDC_MBHC_VOLT_B9_CTL] = 1, + [TOMTOM_A_CDC_MBHC_VOLT_B10_CTL] = 1, + [TOMTOM_A_CDC_MBHC_VOLT_B11_CTL] = 1, + [TOMTOM_A_CDC_MBHC_VOLT_B12_CTL] = 1, + [TOMTOM_A_CDC_MBHC_CLK_CTL] = 1, + [TOMTOM_A_CDC_MBHC_INT_CTL] = 1, + [TOMTOM_A_CDC_MBHC_DEBUG_CTL] = 1, + [TOMTOM_A_CDC_MBHC_SPARE] = 1, + [TOMTOM_A_CDC_RX8_B1_CTL] = 1, + [TOMTOM_A_CDC_RX8_B2_CTL] = 1, + [TOMTOM_A_CDC_RX8_B3_CTL] = 1, + [TOMTOM_A_CDC_RX8_B4_CTL] = 1, + [TOMTOM_A_CDC_RX8_B5_CTL] = 1, + [TOMTOM_A_CDC_RX8_B6_CTL] = 1, + [TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL] = 1, + [TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL] = 1, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0] = 1, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1] = 1, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2] = 1, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3] = 1, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4] = 1, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5] = 1, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6] = 1, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7] = 1, + [TOMTOM_A_CDC_BOOST_MODE_CTL] = 1, + [TOMTOM_A_CDC_BOOST_THRESHOLD] = 1, + [TOMTOM_A_CDC_BOOST_TAP_SEL] = 1, + [TOMTOM_A_CDC_BOOST_HOLD_TIME] = 1, + [TOMTOM_A_CDC_BOOST_TRGR_EN] = 1, +}; + +const u8 tomtom_reset_reg_defaults[TOMTOM_CACHE_SIZE] = { + [TOMTOM_A_CHIP_CTL] = TOMTOM_A_CHIP_CTL__POR, + [TOMTOM_A_CHIP_STATUS] = TOMTOM_A_CHIP_STATUS__POR, + [TOMTOM_A_CHIP_ID_BYTE_0] = TOMTOM_A_CHIP_ID_BYTE_0__POR, + [TOMTOM_A_CHIP_ID_BYTE_1] = TOMTOM_A_CHIP_ID_BYTE_1__POR, + [TOMTOM_A_CHIP_ID_BYTE_2] = TOMTOM_A_CHIP_ID_BYTE_2__POR, + [TOMTOM_A_CHIP_ID_BYTE_3] = TOMTOM_A_CHIP_ID_BYTE_3__POR, + [TOMTOM_A_CHIP_I2C_SLAVE_ID] = TOMTOM_A_CHIP_I2C_SLAVE_ID__POR, + [TOMTOM_A_SLAVE_ID_1] = TOMTOM_A_SLAVE_ID_1__POR, + [TOMTOM_A_SLAVE_ID_2] = TOMTOM_A_SLAVE_ID_2__POR, + [TOMTOM_A_SLAVE_ID_3] = TOMTOM_A_SLAVE_ID_3__POR, + [TOMTOM_A_PIN_CTL_OE0] = TOMTOM_A_PIN_CTL_OE0__POR, + [TOMTOM_A_PIN_CTL_OE1] = TOMTOM_A_PIN_CTL_OE1__POR, + [TOMTOM_A_PIN_CTL_OE2] = TOMTOM_A_PIN_CTL_OE2__POR, + [TOMTOM_A_PIN_CTL_DATA0] = TOMTOM_A_PIN_CTL_DATA0__POR, + [TOMTOM_A_PIN_CTL_DATA1] = TOMTOM_A_PIN_CTL_DATA1__POR, + [TOMTOM_A_PIN_CTL_DATA2] = TOMTOM_A_PIN_CTL_DATA2__POR, + [TOMTOM_A_HDRIVE_GENERIC] = TOMTOM_A_HDRIVE_GENERIC__POR, + [TOMTOM_A_HDRIVE_OVERRIDE] = TOMTOM_A_HDRIVE_OVERRIDE__POR, + [TOMTOM_A_ANA_CSR_WAIT_STATE] = TOMTOM_A_ANA_CSR_WAIT_STATE__POR, + [TOMTOM_A_PROCESS_MONITOR_CTL0] = TOMTOM_A_PROCESS_MONITOR_CTL0__POR, + [TOMTOM_A_PROCESS_MONITOR_CTL1] = TOMTOM_A_PROCESS_MONITOR_CTL1__POR, + [TOMTOM_A_PROCESS_MONITOR_CTL2] = TOMTOM_A_PROCESS_MONITOR_CTL2__POR, + [TOMTOM_A_PROCESS_MONITOR_CTL3] = TOMTOM_A_PROCESS_MONITOR_CTL3__POR, + [TOMTOM_A_QFUSE_CTL] = TOMTOM_A_QFUSE_CTL__POR, + [TOMTOM_A_QFUSE_STATUS] = TOMTOM_A_QFUSE_STATUS__POR, + [TOMTOM_A_QFUSE_DATA_OUT0] = TOMTOM_A_QFUSE_DATA_OUT0__POR, + [TOMTOM_A_QFUSE_DATA_OUT1] = TOMTOM_A_QFUSE_DATA_OUT1__POR, + [TOMTOM_A_QFUSE_DATA_OUT2] = TOMTOM_A_QFUSE_DATA_OUT2__POR, + [TOMTOM_A_QFUSE_DATA_OUT3] = TOMTOM_A_QFUSE_DATA_OUT3__POR, + [TOMTOM_A_QFUSE_DATA_OUT4] = TOMTOM_A_QFUSE_DATA_OUT4__POR, + [TOMTOM_A_QFUSE_DATA_OUT5] = TOMTOM_A_QFUSE_DATA_OUT5__POR, + [TOMTOM_A_QFUSE_DATA_OUT6] = TOMTOM_A_QFUSE_DATA_OUT6__POR, + [TOMTOM_A_QFUSE_DATA_OUT7] = TOMTOM_A_QFUSE_DATA_OUT7__POR, + [TOMTOM_A_CDC_CTL] = TOMTOM_A_CDC_CTL__POR, + [TOMTOM_A_LEAKAGE_CTL] = TOMTOM_A_LEAKAGE_CTL__POR, + [TOMTOM_A_SVASS_MEM_PTR0] = TOMTOM_A_SVASS_MEM_PTR0__POR, + [TOMTOM_A_SVASS_MEM_PTR1] = TOMTOM_A_SVASS_MEM_PTR1__POR, + [TOMTOM_A_SVASS_MEM_PTR2] = TOMTOM_A_SVASS_MEM_PTR2__POR, + [TOMTOM_A_SVASS_MEM_CTL] = TOMTOM_A_SVASS_MEM_CTL__POR, + [TOMTOM_A_SVASS_MEM_BANK] = TOMTOM_A_SVASS_MEM_BANK__POR, + [TOMTOM_A_DMIC_B1_CTL] = TOMTOM_A_DMIC_B1_CTL__POR, + [TOMTOM_A_DMIC_B2_CTL] = TOMTOM_A_DMIC_B2_CTL__POR, + [TOMTOM_A_SVASS_CLKRST_CTL] = TOMTOM_A_SVASS_CLKRST_CTL__POR, + [TOMTOM_A_SVASS_CPAR_CFG] = TOMTOM_A_SVASS_CPAR_CFG__POR, + [TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD] = + TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD__POR, + [TOMTOM_A_SVASS_CPAR_WDOG_CFG] = TOMTOM_A_SVASS_CPAR_WDOG_CFG__POR, + [TOMTOM_A_SVASS_CFG] = TOMTOM_A_SVASS_CFG__POR, + [TOMTOM_A_SVASS_SPE_CFG] = TOMTOM_A_SVASS_SPE_CFG__POR, + [TOMTOM_A_SVASS_STATUS] = TOMTOM_A_SVASS_STATUS__POR, + [TOMTOM_A_SVASS_INT_MASK] = TOMTOM_A_SVASS_INT_MASK__POR, + [TOMTOM_A_SVASS_INT_STATUS] = TOMTOM_A_SVASS_INT_STATUS__POR, + [TOMTOM_A_SVASS_INT_CLR] = TOMTOM_A_SVASS_INT_CLR__POR, + [TOMTOM_A_SVASS_DEBUG] = TOMTOM_A_SVASS_DEBUG__POR, + [TOMTOM_A_SVASS_SPE_BKUP_INT] = TOMTOM_A_SVASS_SPE_BKUP_INT__POR, + [TOMTOM_A_SVASS_MEM_ACC] = TOMTOM_A_SVASS_MEM_ACC__POR, + [TOMTOM_A_MEM_LEAKAGE_CTL] = TOMTOM_A_MEM_LEAKAGE_CTL__POR, + [TOMTOM_A_SVASS_SPE_INBOX_TRG] = TOMTOM_A_SVASS_SPE_INBOX_TRG__POR, + [TOMTOM_A_SVASS_SPE_INBOX_0] = TOMTOM_A_SVASS_SPE_INBOX_0__POR, + [TOMTOM_A_SVASS_SPE_INBOX_1] = TOMTOM_A_SVASS_SPE_INBOX_1__POR, + [TOMTOM_A_SVASS_SPE_INBOX_2] = TOMTOM_A_SVASS_SPE_INBOX_2__POR, + [TOMTOM_A_SVASS_SPE_INBOX_3] = TOMTOM_A_SVASS_SPE_INBOX_3__POR, + [TOMTOM_A_SVASS_SPE_INBOX_4] = TOMTOM_A_SVASS_SPE_INBOX_4__POR, + [TOMTOM_A_SVASS_SPE_INBOX_5] = TOMTOM_A_SVASS_SPE_INBOX_5__POR, + [TOMTOM_A_SVASS_SPE_INBOX_6] = TOMTOM_A_SVASS_SPE_INBOX_6__POR, + [TOMTOM_A_SVASS_SPE_INBOX_7] = TOMTOM_A_SVASS_SPE_INBOX_7__POR, + [TOMTOM_A_SVASS_SPE_INBOX_8] = TOMTOM_A_SVASS_SPE_INBOX_8__POR, + [TOMTOM_A_SVASS_SPE_INBOX_9] = TOMTOM_A_SVASS_SPE_INBOX_9__POR, + [TOMTOM_A_SVASS_SPE_INBOX_10] = TOMTOM_A_SVASS_SPE_INBOX_10__POR, + [TOMTOM_A_SVASS_SPE_INBOX_11] = TOMTOM_A_SVASS_SPE_INBOX_11__POR, + [TOMTOM_A_SVASS_SPE_OUTBOX_0] = TOMTOM_A_SVASS_SPE_OUTBOX_0__POR, + [TOMTOM_A_SVASS_SPE_OUTBOX_1] = TOMTOM_A_SVASS_SPE_OUTBOX_1__POR, + [TOMTOM_A_SVASS_SPE_OUTBOX_2] = TOMTOM_A_SVASS_SPE_OUTBOX_2__POR, + [TOMTOM_A_SVASS_SPE_OUTBOX_3] = TOMTOM_A_SVASS_SPE_OUTBOX_3__POR, + [TOMTOM_A_SVASS_SPE_OUTBOX_4] = TOMTOM_A_SVASS_SPE_OUTBOX_4__POR, + [TOMTOM_A_SVASS_SPE_OUTBOX_5] = TOMTOM_A_SVASS_SPE_OUTBOX_5__POR, + [TOMTOM_A_SVASS_SPE_OUTBOX_6] = TOMTOM_A_SVASS_SPE_OUTBOX_6__POR, + [TOMTOM_A_SVASS_SPE_OUTBOX_7] = TOMTOM_A_SVASS_SPE_OUTBOX_7__POR, + [TOMTOM_A_SVASS_SPE_OUTBOX_8] = TOMTOM_A_SVASS_SPE_OUTBOX_8__POR, + [TOMTOM_A_SVASS_SPE_OUTBOX_9] = TOMTOM_A_SVASS_SPE_OUTBOX_9__POR, + [TOMTOM_A_SVASS_SPE_OUTBOX_10] = TOMTOM_A_SVASS_SPE_OUTBOX_10__POR, + [TOMTOM_A_SVASS_SPE_OUTBOX_11] = TOMTOM_A_SVASS_SPE_OUTBOX_11__POR, + [TOMTOM_A_INTR_MODE] = TOMTOM_A_INTR_MODE__POR, + [TOMTOM_A_INTR1_MASK0] = TOMTOM_A_INTR1_MASK0__POR, + [TOMTOM_A_INTR1_MASK1] = TOMTOM_A_INTR1_MASK1__POR, + [TOMTOM_A_INTR1_MASK2] = TOMTOM_A_INTR1_MASK2__POR, + [TOMTOM_A_INTR1_MASK3] = TOMTOM_A_INTR1_MASK3__POR, + [TOMTOM_A_INTR1_STATUS0] = TOMTOM_A_INTR1_STATUS0__POR, + [TOMTOM_A_INTR1_STATUS1] = TOMTOM_A_INTR1_STATUS1__POR, + [TOMTOM_A_INTR1_STATUS2] = TOMTOM_A_INTR1_STATUS2__POR, + [TOMTOM_A_INTR1_STATUS3] = TOMTOM_A_INTR1_STATUS3__POR, + [TOMTOM_A_INTR1_CLEAR0] = TOMTOM_A_INTR1_CLEAR0__POR, + [TOMTOM_A_INTR1_CLEAR1] = TOMTOM_A_INTR1_CLEAR1__POR, + [TOMTOM_A_INTR1_CLEAR2] = TOMTOM_A_INTR1_CLEAR2__POR, + [TOMTOM_A_INTR1_CLEAR3] = TOMTOM_A_INTR1_CLEAR3__POR, + [TOMTOM_A_INTR1_LEVEL0] = TOMTOM_A_INTR1_LEVEL0__POR, + [TOMTOM_A_INTR1_LEVEL1] = TOMTOM_A_INTR1_LEVEL1__POR, + [TOMTOM_A_INTR1_LEVEL2] = TOMTOM_A_INTR1_LEVEL2__POR, + [TOMTOM_A_INTR1_LEVEL3] = TOMTOM_A_INTR1_LEVEL3__POR, + [TOMTOM_A_INTR1_TEST0] = TOMTOM_A_INTR1_TEST0__POR, + [TOMTOM_A_INTR1_TEST1] = TOMTOM_A_INTR1_TEST1__POR, + [TOMTOM_A_INTR1_TEST2] = TOMTOM_A_INTR1_TEST2__POR, + [TOMTOM_A_INTR1_TEST3] = TOMTOM_A_INTR1_TEST3__POR, + [TOMTOM_A_INTR1_SET0] = TOMTOM_A_INTR1_SET0__POR, + [TOMTOM_A_INTR1_SET1] = TOMTOM_A_INTR1_SET1__POR, + [TOMTOM_A_INTR1_SET2] = TOMTOM_A_INTR1_SET2__POR, + [TOMTOM_A_INTR1_SET3] = TOMTOM_A_INTR1_SET3__POR, + [TOMTOM_A_INTR2_MASK0] = TOMTOM_A_INTR2_MASK0__POR, + [TOMTOM_A_INTR2_STATUS0] = TOMTOM_A_INTR2_STATUS0__POR, + [TOMTOM_A_INTR2_CLEAR0] = TOMTOM_A_INTR2_CLEAR0__POR, + [TOMTOM_A_INTR2_LEVEL0] = TOMTOM_A_INTR2_LEVEL0__POR, + [TOMTOM_A_INTR2_TEST0] = TOMTOM_A_INTR2_TEST0__POR, + [TOMTOM_A_INTR2_SET0] = TOMTOM_A_INTR2_SET0__POR, + [TOMTOM_A_CDC_TX_I2S_SCK_MODE] = TOMTOM_A_CDC_TX_I2S_SCK_MODE__POR, + [TOMTOM_A_CDC_TX_I2S_WS_MODE] = TOMTOM_A_CDC_TX_I2S_WS_MODE__POR, + [TOMTOM_A_CDC_DMIC_DATA0_MODE] = TOMTOM_A_CDC_DMIC_DATA0_MODE__POR, + [TOMTOM_A_CDC_DMIC_CLK0_MODE] = TOMTOM_A_CDC_DMIC_CLK0_MODE__POR, + [TOMTOM_A_CDC_DMIC_DATA1_MODE] = TOMTOM_A_CDC_DMIC_DATA1_MODE__POR, + [TOMTOM_A_CDC_DMIC_CLK1_MODE] = TOMTOM_A_CDC_DMIC_CLK1_MODE__POR, + [TOMTOM_A_CDC_RX_I2S_SCK_MODE] = TOMTOM_A_CDC_RX_I2S_SCK_MODE__POR, + [TOMTOM_A_CDC_RX_I2S_WS_MODE] = TOMTOM_A_CDC_RX_I2S_WS_MODE__POR, + [TOMTOM_A_CDC_DMIC_DATA2_MODE] = TOMTOM_A_CDC_DMIC_DATA2_MODE__POR, + [TOMTOM_A_CDC_DMIC_CLK2_MODE] = TOMTOM_A_CDC_DMIC_CLK2_MODE__POR, + [TOMTOM_A_CDC_INTR1_MODE] = TOMTOM_A_CDC_INTR1_MODE__POR, + [TOMTOM_A_CDC_SB_NRZ_SEL_MODE] = TOMTOM_A_CDC_SB_NRZ_SEL_MODE__POR, + [TOMTOM_A_CDC_INTR2_MODE] = TOMTOM_A_CDC_INTR2_MODE__POR, + [TOMTOM_A_CDC_RF_PA_ON_MODE] = TOMTOM_A_CDC_RF_PA_ON_MODE__POR, + [TOMTOM_A_CDC_BOOST_MODE] = TOMTOM_A_CDC_BOOST_MODE__POR, + [TOMTOM_A_CDC_JTCK_MODE] = TOMTOM_A_CDC_JTCK_MODE__POR, + [TOMTOM_A_CDC_JTDI_MODE] = TOMTOM_A_CDC_JTDI_MODE__POR, + [TOMTOM_A_CDC_JTMS_MODE] = TOMTOM_A_CDC_JTMS_MODE__POR, + [TOMTOM_A_CDC_JTDO_MODE] = TOMTOM_A_CDC_JTDO_MODE__POR, + [TOMTOM_A_CDC_JTRST_MODE] = TOMTOM_A_CDC_JTRST_MODE__POR, + [TOMTOM_A_CDC_BIST_MODE_MODE] = TOMTOM_A_CDC_BIST_MODE_MODE__POR, + [TOMTOM_A_CDC_MAD_MAIN_CTL_1] = TOMTOM_A_CDC_MAD_MAIN_CTL_1__POR, + [TOMTOM_A_CDC_MAD_MAIN_CTL_2] = TOMTOM_A_CDC_MAD_MAIN_CTL_2__POR, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_1] = TOMTOM_A_CDC_MAD_AUDIO_CTL_1__POR, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_2] = TOMTOM_A_CDC_MAD_AUDIO_CTL_2__POR, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_3] = TOMTOM_A_CDC_MAD_AUDIO_CTL_3__POR, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_4] = TOMTOM_A_CDC_MAD_AUDIO_CTL_4__POR, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_5] = TOMTOM_A_CDC_MAD_AUDIO_CTL_5__POR, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_6] = TOMTOM_A_CDC_MAD_AUDIO_CTL_6__POR, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_7] = TOMTOM_A_CDC_MAD_AUDIO_CTL_7__POR, + [TOMTOM_A_CDC_MAD_AUDIO_CTL_8] = TOMTOM_A_CDC_MAD_AUDIO_CTL_8__POR, + [TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR] = + TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR__POR, + [TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL] = + TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL__POR, + [TOMTOM_A_CDC_MAD_ULTR_CTL_1] = TOMTOM_A_CDC_MAD_ULTR_CTL_1__POR, + [TOMTOM_A_CDC_MAD_ULTR_CTL_2] = TOMTOM_A_CDC_MAD_ULTR_CTL_2__POR, + [TOMTOM_A_CDC_MAD_ULTR_CTL_3] = TOMTOM_A_CDC_MAD_ULTR_CTL_3__POR, + [TOMTOM_A_CDC_MAD_ULTR_CTL_4] = TOMTOM_A_CDC_MAD_ULTR_CTL_4__POR, + [TOMTOM_A_CDC_MAD_ULTR_CTL_5] = TOMTOM_A_CDC_MAD_ULTR_CTL_5__POR, + [TOMTOM_A_CDC_MAD_ULTR_CTL_6] = TOMTOM_A_CDC_MAD_ULTR_CTL_6__POR, + [TOMTOM_A_CDC_MAD_ULTR_CTL_7] = TOMTOM_A_CDC_MAD_ULTR_CTL_7__POR, + [TOMTOM_A_CDC_MAD_BEACON_CTL_1] = TOMTOM_A_CDC_MAD_BEACON_CTL_1__POR, + [TOMTOM_A_CDC_MAD_BEACON_CTL_2] = TOMTOM_A_CDC_MAD_BEACON_CTL_2__POR, + [TOMTOM_A_CDC_MAD_BEACON_CTL_3] = TOMTOM_A_CDC_MAD_BEACON_CTL_3__POR, + [TOMTOM_A_CDC_MAD_BEACON_CTL_4] = TOMTOM_A_CDC_MAD_BEACON_CTL_4__POR, + [TOMTOM_A_CDC_MAD_BEACON_CTL_5] = TOMTOM_A_CDC_MAD_BEACON_CTL_5__POR, + [TOMTOM_A_CDC_MAD_BEACON_CTL_6] = TOMTOM_A_CDC_MAD_BEACON_CTL_6__POR, + [TOMTOM_A_CDC_MAD_BEACON_CTL_7] = TOMTOM_A_CDC_MAD_BEACON_CTL_7__POR, + [TOMTOM_A_CDC_MAD_BEACON_CTL_8] = TOMTOM_A_CDC_MAD_BEACON_CTL_8__POR, + [TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR] = + TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR__POR, + [TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL] = + TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL__POR, + [TOMTOM_A_CDC_MAD_INP_SEL] = TOMTOM_A_CDC_MAD_INP_SEL__POR, + [TOMTOM_A_BIAS_REF_CTL] = TOMTOM_A_BIAS_REF_CTL__POR, + [TOMTOM_A_BIAS_CENTRAL_BG_CTL] = TOMTOM_A_BIAS_CENTRAL_BG_CTL__POR, + [TOMTOM_A_BIAS_PRECHRG_CTL] = TOMTOM_A_BIAS_PRECHRG_CTL__POR, + [TOMTOM_A_BIAS_CURR_CTL_1] = TOMTOM_A_BIAS_CURR_CTL_1__POR, + [TOMTOM_A_BIAS_CURR_CTL_2] = TOMTOM_A_BIAS_CURR_CTL_2__POR, + [TOMTOM_A_BIAS_OSC_BG_CTL] = TOMTOM_A_BIAS_OSC_BG_CTL__POR, + [TOMTOM_A_CLK_BUFF_EN1] = TOMTOM_A_CLK_BUFF_EN1__POR, + [TOMTOM_A_CLK_BUFF_EN2] = TOMTOM_A_CLK_BUFF_EN2__POR, + [TOMTOM_A_LDO_L_MODE_1] = TOMTOM_A_LDO_L_MODE_1__POR, + [TOMTOM_A_LDO_L_MODE_2] = TOMTOM_A_LDO_L_MODE_2__POR, + [TOMTOM_A_LDO_L_CTRL_1] = TOMTOM_A_LDO_L_CTRL_1__POR, + [TOMTOM_A_LDO_L_CTRL_2] = TOMTOM_A_LDO_L_CTRL_2__POR, + [TOMTOM_A_LDO_L_CTRL_3] = TOMTOM_A_LDO_L_CTRL_3__POR, + [TOMTOM_A_LDO_L_CTRL_4] = TOMTOM_A_LDO_L_CTRL_4__POR, + [TOMTOM_A_LDO_H_MODE_1] = TOMTOM_A_LDO_H_MODE_1__POR, + [TOMTOM_A_LDO_H_MODE_2] = TOMTOM_A_LDO_H_MODE_2__POR, + [TOMTOM_A_LDO_H_LOOP_CTL] = TOMTOM_A_LDO_H_LOOP_CTL__POR, + [TOMTOM_A_LDO_H_COMP_1] = TOMTOM_A_LDO_H_COMP_1__POR, + [TOMTOM_A_LDO_H_COMP_2] = TOMTOM_A_LDO_H_COMP_2__POR, + [TOMTOM_A_LDO_H_BIAS_1] = TOMTOM_A_LDO_H_BIAS_1__POR, + [TOMTOM_A_LDO_H_BIAS_2] = TOMTOM_A_LDO_H_BIAS_2__POR, + [TOMTOM_A_LDO_H_BIAS_3] = TOMTOM_A_LDO_H_BIAS_3__POR, + [TOMTOM_A_VBAT_CLK] = TOMTOM_A_VBAT_CLK__POR, + [TOMTOM_A_VBAT_LOOP] = TOMTOM_A_VBAT_LOOP__POR, + [TOMTOM_A_VBAT_REF] = TOMTOM_A_VBAT_REF__POR, + [TOMTOM_A_VBAT_ADC_TEST] = TOMTOM_A_VBAT_ADC_TEST__POR, + [TOMTOM_A_VBAT_FE] = TOMTOM_A_VBAT_FE__POR, + [TOMTOM_A_VBAT_BIAS_1] = TOMTOM_A_VBAT_BIAS_1__POR, + [TOMTOM_A_VBAT_BIAS_2] = TOMTOM_A_VBAT_BIAS_2__POR, + [TOMTOM_A_VBAT_ADC_DATA_MSB] = TOMTOM_A_VBAT_ADC_DATA_MSB__POR, + [TOMTOM_A_VBAT_ADC_DATA_LSB] = TOMTOM_A_VBAT_ADC_DATA_LSB__POR, + [TOMTOM_A_FLL_NREF] = TOMTOM_A_FLL_NREF__POR, + [TOMTOM_A_FLL_KDCO_TUNE] = TOMTOM_A_FLL_KDCO_TUNE__POR, + [TOMTOM_A_FLL_LOCK_THRESH] = TOMTOM_A_FLL_LOCK_THRESH__POR, + [TOMTOM_A_FLL_LOCK_DET_COUNT] = TOMTOM_A_FLL_LOCK_DET_COUNT__POR, + [TOMTOM_A_FLL_DAC_THRESHOLD] = TOMTOM_A_FLL_DAC_THRESHOLD__POR, + [TOMTOM_A_FLL_TEST_DCO_FREERUN] = TOMTOM_A_FLL_TEST_DCO_FREERUN__POR, + [TOMTOM_A_FLL_TEST_ENABLE] = TOMTOM_A_FLL_TEST_ENABLE__POR, + [TOMTOM_A_MICB_CFILT_1_CTL] = TOMTOM_A_MICB_CFILT_1_CTL__POR, + [TOMTOM_A_MICB_CFILT_1_VAL] = TOMTOM_A_MICB_CFILT_1_VAL__POR, + [TOMTOM_A_MICB_CFILT_1_PRECHRG] = TOMTOM_A_MICB_CFILT_1_PRECHRG__POR, + [TOMTOM_A_MICB_1_CTL] = TOMTOM_A_MICB_1_CTL__POR, + [TOMTOM_A_MICB_1_INT_RBIAS] = TOMTOM_A_MICB_1_INT_RBIAS__POR, + [TOMTOM_A_MICB_1_MBHC] = TOMTOM_A_MICB_1_MBHC__POR, + [TOMTOM_A_MICB_CFILT_2_CTL] = TOMTOM_A_MICB_CFILT_2_CTL__POR, + [TOMTOM_A_MICB_CFILT_2_VAL] = TOMTOM_A_MICB_CFILT_2_VAL__POR, + [TOMTOM_A_MICB_CFILT_2_PRECHRG] = TOMTOM_A_MICB_CFILT_2_PRECHRG__POR, + [TOMTOM_A_MICB_2_CTL] = TOMTOM_A_MICB_2_CTL__POR, + [TOMTOM_A_MICB_2_INT_RBIAS] = TOMTOM_A_MICB_2_INT_RBIAS__POR, + [TOMTOM_A_MICB_2_MBHC] = TOMTOM_A_MICB_2_MBHC__POR, + [TOMTOM_A_MICB_CFILT_3_CTL] = TOMTOM_A_MICB_CFILT_3_CTL__POR, + [TOMTOM_A_MICB_CFILT_3_VAL] = TOMTOM_A_MICB_CFILT_3_VAL__POR, + [TOMTOM_A_MICB_CFILT_3_PRECHRG] = TOMTOM_A_MICB_CFILT_3_PRECHRG__POR, + [TOMTOM_A_MICB_3_CTL] = TOMTOM_A_MICB_3_CTL__POR, + [TOMTOM_A_MICB_3_INT_RBIAS] = TOMTOM_A_MICB_3_INT_RBIAS__POR, + [TOMTOM_A_MICB_3_MBHC] = TOMTOM_A_MICB_3_MBHC__POR, + [TOMTOM_A_MICB_4_CTL] = TOMTOM_A_MICB_4_CTL__POR, + [TOMTOM_A_MICB_4_INT_RBIAS] = TOMTOM_A_MICB_4_INT_RBIAS__POR, + [TOMTOM_A_MICB_4_MBHC] = TOMTOM_A_MICB_4_MBHC__POR, + [TOMTOM_A_SPKR_DRV2_EN] = TOMTOM_A_SPKR_DRV2_EN__POR, + [TOMTOM_A_SPKR_DRV2_GAIN] = TOMTOM_A_SPKR_DRV2_GAIN__POR, + [TOMTOM_A_SPKR_DRV2_DAC_CTL] = TOMTOM_A_SPKR_DRV2_DAC_CTL__POR, + [TOMTOM_A_SPKR_DRV2_OCP_CTL] = TOMTOM_A_SPKR_DRV2_OCP_CTL__POR, + [TOMTOM_A_SPKR_DRV2_CLIP_DET] = TOMTOM_A_SPKR_DRV2_CLIP_DET__POR, + [TOMTOM_A_SPKR_DRV2_DBG_DAC] = TOMTOM_A_SPKR_DRV2_DBG_DAC__POR, + [TOMTOM_A_SPKR_DRV2_DBG_PA] = TOMTOM_A_SPKR_DRV2_DBG_PA__POR, + [TOMTOM_A_SPKR_DRV2_DBG_PWRSTG] = TOMTOM_A_SPKR_DRV2_DBG_PWRSTG__POR, + [TOMTOM_A_SPKR_DRV2_BIAS_LDO] = TOMTOM_A_SPKR_DRV2_BIAS_LDO__POR, + [TOMTOM_A_SPKR_DRV2_BIAS_INT] = TOMTOM_A_SPKR_DRV2_BIAS_INT__POR, + [TOMTOM_A_SPKR_DRV2_BIAS_PA] = TOMTOM_A_SPKR_DRV2_BIAS_PA__POR, + [TOMTOM_A_SPKR_DRV2_STATUS_OCP] = TOMTOM_A_SPKR_DRV2_STATUS_OCP__POR, + [TOMTOM_A_SPKR_DRV2_STATUS_PA] = TOMTOM_A_SPKR_DRV2_STATUS_PA__POR, + [TOMTOM_A_MBHC_INSERT_DETECT] = TOMTOM_A_MBHC_INSERT_DETECT__POR, + [TOMTOM_A_MBHC_INSERT_DET_STATUS] = + TOMTOM_A_MBHC_INSERT_DET_STATUS__POR, + [TOMTOM_A_TX_COM_BIAS] = TOMTOM_A_TX_COM_BIAS__POR, + [TOMTOM_A_MBHC_INSERT_DETECT2] = TOMTOM_A_MBHC_INSERT_DETECT2__POR, + [TOMTOM_A_MBHC_SCALING_MUX_1] = TOMTOM_A_MBHC_SCALING_MUX_1__POR, + [TOMTOM_A_MBHC_SCALING_MUX_2] = TOMTOM_A_MBHC_SCALING_MUX_2__POR, + [TOMTOM_A_MAD_ANA_CTRL] = TOMTOM_A_MAD_ANA_CTRL__POR, + [TOMTOM_A_TX_SUP_SWITCH_CTRL_1] = TOMTOM_A_TX_SUP_SWITCH_CTRL_1__POR, + [TOMTOM_A_TX_SUP_SWITCH_CTRL_2] = TOMTOM_A_TX_SUP_SWITCH_CTRL_2__POR, + [TOMTOM_A_TX_1_GAIN] = TOMTOM_A_TX_1_GAIN__POR, + [TOMTOM_A_TX_1_2_TEST_EN] = TOMTOM_A_TX_1_2_TEST_EN__POR, + [TOMTOM_A_TX_2_GAIN] = TOMTOM_A_TX_2_GAIN__POR, + [TOMTOM_A_TX_1_2_ADC_IB] = TOMTOM_A_TX_1_2_ADC_IB__POR, + [TOMTOM_A_TX_1_2_ATEST_REFCTRL] = TOMTOM_A_TX_1_2_ATEST_REFCTRL__POR, + [TOMTOM_A_TX_1_2_TEST_CTL] = TOMTOM_A_TX_1_2_TEST_CTL__POR, + [TOMTOM_A_TX_1_2_TEST_BLOCK_EN] = TOMTOM_A_TX_1_2_TEST_BLOCK_EN__POR, + [TOMTOM_A_TX_1_2_TXFE_CLKDIV] = TOMTOM_A_TX_1_2_TXFE_CLKDIV__POR, + [TOMTOM_A_TX_1_2_SAR_ERR_CH1] = TOMTOM_A_TX_1_2_SAR_ERR_CH1__POR, + [TOMTOM_A_TX_1_2_SAR_ERR_CH2] = TOMTOM_A_TX_1_2_SAR_ERR_CH2__POR, + [TOMTOM_A_TX_3_GAIN] = TOMTOM_A_TX_3_GAIN__POR, + [TOMTOM_A_TX_3_4_TEST_EN] = TOMTOM_A_TX_3_4_TEST_EN__POR, + [TOMTOM_A_TX_4_GAIN] = TOMTOM_A_TX_4_GAIN__POR, + [TOMTOM_A_TX_3_4_ADC_IB] = TOMTOM_A_TX_3_4_ADC_IB__POR, + [TOMTOM_A_TX_3_4_ATEST_REFCTRL] = TOMTOM_A_TX_3_4_ATEST_REFCTRL__POR, + [TOMTOM_A_TX_3_4_TEST_CTL] = TOMTOM_A_TX_3_4_TEST_CTL__POR, + [TOMTOM_A_TX_3_4_TEST_BLOCK_EN] = TOMTOM_A_TX_3_4_TEST_BLOCK_EN__POR, + [TOMTOM_A_TX_3_4_TXFE_CKDIV] = TOMTOM_A_TX_3_4_TXFE_CKDIV__POR, + [TOMTOM_A_TX_3_4_SAR_ERR_CH3] = TOMTOM_A_TX_3_4_SAR_ERR_CH3__POR, + [TOMTOM_A_TX_3_4_SAR_ERR_CH4] = TOMTOM_A_TX_3_4_SAR_ERR_CH4__POR, + [TOMTOM_A_TX_5_GAIN] = TOMTOM_A_TX_5_GAIN__POR, + [TOMTOM_A_TX_5_6_TEST_EN] = TOMTOM_A_TX_5_6_TEST_EN__POR, + [TOMTOM_A_TX_6_GAIN] = TOMTOM_A_TX_6_GAIN__POR, + [TOMTOM_A_TX_5_6_ADC_IB] = TOMTOM_A_TX_5_6_ADC_IB__POR, + [TOMTOM_A_TX_5_6_ATEST_REFCTRL] = TOMTOM_A_TX_5_6_ATEST_REFCTRL__POR, + [TOMTOM_A_TX_5_6_TEST_CTL] = TOMTOM_A_TX_5_6_TEST_CTL__POR, + [TOMTOM_A_TX_5_6_TEST_BLOCK_EN] = TOMTOM_A_TX_5_6_TEST_BLOCK_EN__POR, + [TOMTOM_A_TX_5_6_TXFE_CKDIV] = TOMTOM_A_TX_5_6_TXFE_CKDIV__POR, + [TOMTOM_A_TX_5_6_SAR_ERR_CH5] = TOMTOM_A_TX_5_6_SAR_ERR_CH5__POR, + [TOMTOM_A_TX_5_6_SAR_ERR_CH6] = TOMTOM_A_TX_5_6_SAR_ERR_CH6__POR, + [TOMTOM_A_TX_7_MBHC_EN] = TOMTOM_A_TX_7_MBHC_EN__POR, + [TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL] = + TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL__POR, + [TOMTOM_A_TX_7_MBHC_ADC] = TOMTOM_A_TX_7_MBHC_ADC__POR, + [TOMTOM_A_TX_7_MBHC_TEST_CTL] = TOMTOM_A_TX_7_MBHC_TEST_CTL__POR, + [TOMTOM_A_TX_7_MBHC_SAR_ERR] = TOMTOM_A_TX_7_MBHC_SAR_ERR__POR, + [TOMTOM_A_TX_7_TXFE_CLKDIV] = TOMTOM_A_TX_7_TXFE_CLKDIV__POR, + [TOMTOM_A_RCO_CTRL] = TOMTOM_A_RCO_CTRL__POR, + [TOMTOM_A_RCO_CALIBRATION_CTRL1] = TOMTOM_A_RCO_CALIBRATION_CTRL1__POR, + [TOMTOM_A_RCO_CALIBRATION_CTRL2] = TOMTOM_A_RCO_CALIBRATION_CTRL2__POR, + [TOMTOM_A_RCO_CALIBRATION_CTRL3] = TOMTOM_A_RCO_CALIBRATION_CTRL3__POR, + [TOMTOM_A_RCO_TEST_CTRL] = TOMTOM_A_RCO_TEST_CTRL__POR, + [TOMTOM_A_RCO_CALIBRATION_RESULT1] = + TOMTOM_A_RCO_CALIBRATION_RESULT1__POR, + [TOMTOM_A_RCO_CALIBRATION_RESULT2] = + TOMTOM_A_RCO_CALIBRATION_RESULT2__POR, + [TOMTOM_A_BUCK_MODE_1] = TOMTOM_A_BUCK_MODE_1__POR, + [TOMTOM_A_BUCK_MODE_2] = TOMTOM_A_BUCK_MODE_2__POR, + [TOMTOM_A_BUCK_MODE_3] = TOMTOM_A_BUCK_MODE_3__POR, + [TOMTOM_A_BUCK_MODE_4] = TOMTOM_A_BUCK_MODE_4__POR, + [TOMTOM_A_BUCK_MODE_5] = TOMTOM_A_BUCK_MODE_5__POR, + [TOMTOM_A_BUCK_CTRL_VCL_1] = TOMTOM_A_BUCK_CTRL_VCL_1__POR, + [TOMTOM_A_BUCK_CTRL_VCL_2] = TOMTOM_A_BUCK_CTRL_VCL_2__POR, + [TOMTOM_A_BUCK_CTRL_VCL_3] = TOMTOM_A_BUCK_CTRL_VCL_3__POR, + [TOMTOM_A_BUCK_CTRL_CCL_1] = TOMTOM_A_BUCK_CTRL_CCL_1__POR, + [TOMTOM_A_BUCK_CTRL_CCL_2] = TOMTOM_A_BUCK_CTRL_CCL_2__POR, + [TOMTOM_A_BUCK_CTRL_CCL_3] = TOMTOM_A_BUCK_CTRL_CCL_3__POR, + [TOMTOM_A_BUCK_CTRL_CCL_4] = TOMTOM_A_BUCK_CTRL_CCL_4__POR, + [TOMTOM_A_BUCK_CTRL_PWM_DRVR_1] = TOMTOM_A_BUCK_CTRL_PWM_DRVR_1__POR, + [TOMTOM_A_BUCK_CTRL_PWM_DRVR_2] = TOMTOM_A_BUCK_CTRL_PWM_DRVR_2__POR, + [TOMTOM_A_BUCK_CTRL_PWM_DRVR_3] = TOMTOM_A_BUCK_CTRL_PWM_DRVR_3__POR, + [TOMTOM_A_BUCK_TMUX_A_D] = TOMTOM_A_BUCK_TMUX_A_D__POR, + [TOMTOM_A_NCP_BUCKREF] = TOMTOM_A_NCP_BUCKREF__POR, + [TOMTOM_A_NCP_EN] = TOMTOM_A_NCP_EN__POR, + [TOMTOM_A_NCP_CLK] = TOMTOM_A_NCP_CLK__POR, + [TOMTOM_A_NCP_STATIC] = TOMTOM_A_NCP_STATIC__POR, + [TOMTOM_A_NCP_VTH_LOW] = TOMTOM_A_NCP_VTH_LOW__POR, + [TOMTOM_A_NCP_VTH_HIGH] = TOMTOM_A_NCP_VTH_HIGH__POR, + [TOMTOM_A_NCP_ATEST] = TOMTOM_A_NCP_ATEST__POR, + [TOMTOM_A_NCP_DTEST] = TOMTOM_A_NCP_DTEST__POR, + [TOMTOM_A_NCP_DLY1] = TOMTOM_A_NCP_DLY1__POR, + [TOMTOM_A_NCP_DLY2] = TOMTOM_A_NCP_DLY2__POR, + [TOMTOM_A_RX_AUX_SW_CTL] = TOMTOM_A_RX_AUX_SW_CTL__POR, + [TOMTOM_A_RX_PA_AUX_IN_CONN] = TOMTOM_A_RX_PA_AUX_IN_CONN__POR, + [TOMTOM_A_RX_COM_TIMER_DIV] = TOMTOM_A_RX_COM_TIMER_DIV__POR, + [TOMTOM_A_RX_COM_OCP_CTL] = TOMTOM_A_RX_COM_OCP_CTL__POR, + [TOMTOM_A_RX_COM_OCP_COUNT] = TOMTOM_A_RX_COM_OCP_COUNT__POR, + [TOMTOM_A_RX_COM_DAC_CTL] = TOMTOM_A_RX_COM_DAC_CTL__POR, + [TOMTOM_A_RX_COM_BIAS] = TOMTOM_A_RX_COM_BIAS__POR, + [TOMTOM_A_RX_HPH_AUTO_CHOP] = TOMTOM_A_RX_HPH_AUTO_CHOP__POR, + [TOMTOM_A_RX_HPH_CHOP_CTL] = TOMTOM_A_RX_HPH_CHOP_CTL__POR, + [TOMTOM_A_RX_HPH_BIAS_PA] = TOMTOM_A_RX_HPH_BIAS_PA__POR, + [TOMTOM_A_RX_HPH_BIAS_LDO] = TOMTOM_A_RX_HPH_BIAS_LDO__POR, + [TOMTOM_A_RX_HPH_BIAS_CNP] = TOMTOM_A_RX_HPH_BIAS_CNP__POR, + [TOMTOM_A_RX_HPH_BIAS_WG_OCP] = TOMTOM_A_RX_HPH_BIAS_WG_OCP__POR, + [TOMTOM_A_RX_HPH_OCP_CTL] = TOMTOM_A_RX_HPH_OCP_CTL__POR, + [TOMTOM_A_RX_HPH_CNP_EN] = TOMTOM_A_RX_HPH_CNP_EN__POR, + [TOMTOM_A_RX_HPH_CNP_WG_CTL] = TOMTOM_A_RX_HPH_CNP_WG_CTL__POR, + [TOMTOM_A_RX_HPH_CNP_WG_TIME] = TOMTOM_A_RX_HPH_CNP_WG_TIME__POR, + [TOMTOM_A_RX_HPH_L_GAIN] = TOMTOM_A_RX_HPH_L_GAIN__POR, + [TOMTOM_A_RX_HPH_L_TEST] = TOMTOM_A_RX_HPH_L_TEST__POR, + [TOMTOM_A_RX_HPH_L_PA_CTL] = TOMTOM_A_RX_HPH_L_PA_CTL__POR, + [TOMTOM_A_RX_HPH_L_DAC_CTL] = TOMTOM_A_RX_HPH_L_DAC_CTL__POR, + [TOMTOM_A_RX_HPH_L_ATEST] = TOMTOM_A_RX_HPH_L_ATEST__POR, + [TOMTOM_A_RX_HPH_L_STATUS] = TOMTOM_A_RX_HPH_L_STATUS__POR, + [TOMTOM_A_RX_HPH_R_GAIN] = TOMTOM_A_RX_HPH_R_GAIN__POR, + [TOMTOM_A_RX_HPH_R_TEST] = TOMTOM_A_RX_HPH_R_TEST__POR, + [TOMTOM_A_RX_HPH_R_PA_CTL] = TOMTOM_A_RX_HPH_R_PA_CTL__POR, + [TOMTOM_A_RX_HPH_R_DAC_CTL] = TOMTOM_A_RX_HPH_R_DAC_CTL__POR, + [TOMTOM_A_RX_HPH_R_ATEST] = TOMTOM_A_RX_HPH_R_ATEST__POR, + [TOMTOM_A_RX_HPH_R_STATUS] = TOMTOM_A_RX_HPH_R_STATUS__POR, + [TOMTOM_A_RX_EAR_BIAS_PA] = TOMTOM_A_RX_EAR_BIAS_PA__POR, + [TOMTOM_A_RX_EAR_BIAS_CMBUFF] = TOMTOM_A_RX_EAR_BIAS_CMBUFF__POR, + [TOMTOM_A_RX_EAR_EN] = TOMTOM_A_RX_EAR_EN__POR, + [TOMTOM_A_RX_EAR_GAIN] = TOMTOM_A_RX_EAR_GAIN__POR, + [TOMTOM_A_RX_EAR_CMBUFF] = TOMTOM_A_RX_EAR_CMBUFF__POR, + [TOMTOM_A_RX_EAR_ICTL] = TOMTOM_A_RX_EAR_ICTL__POR, + [TOMTOM_A_RX_EAR_CCOMP] = TOMTOM_A_RX_EAR_CCOMP__POR, + [TOMTOM_A_RX_EAR_VCM] = TOMTOM_A_RX_EAR_VCM__POR, + [TOMTOM_A_RX_EAR_CNP] = TOMTOM_A_RX_EAR_CNP__POR, + [TOMTOM_A_RX_EAR_DAC_CTL_ATEST] = TOMTOM_A_RX_EAR_DAC_CTL_ATEST__POR, + [TOMTOM_A_RX_EAR_STATUS] = TOMTOM_A_RX_EAR_STATUS__POR, + [TOMTOM_A_RX_LINE_BIAS_PA] = TOMTOM_A_RX_LINE_BIAS_PA__POR, + [TOMTOM_A_RX_BUCK_BIAS1] = TOMTOM_A_RX_BUCK_BIAS1__POR, + [TOMTOM_A_RX_BUCK_BIAS2] = TOMTOM_A_RX_BUCK_BIAS2__POR, + [TOMTOM_A_RX_LINE_COM] = TOMTOM_A_RX_LINE_COM__POR, + [TOMTOM_A_RX_LINE_CNP_EN] = TOMTOM_A_RX_LINE_CNP_EN__POR, + [TOMTOM_A_RX_LINE_CNP_WG_CTL] = TOMTOM_A_RX_LINE_CNP_WG_CTL__POR, + [TOMTOM_A_RX_LINE_CNP_WG_TIME] = TOMTOM_A_RX_LINE_CNP_WG_TIME__POR, + [TOMTOM_A_RX_LINE_1_GAIN] = TOMTOM_A_RX_LINE_1_GAIN__POR, + [TOMTOM_A_RX_LINE_1_TEST] = TOMTOM_A_RX_LINE_1_TEST__POR, + [TOMTOM_A_RX_LINE_1_DAC_CTL] = TOMTOM_A_RX_LINE_1_DAC_CTL__POR, + [TOMTOM_A_RX_LINE_1_STATUS] = TOMTOM_A_RX_LINE_1_STATUS__POR, + [TOMTOM_A_RX_LINE_2_GAIN] = TOMTOM_A_RX_LINE_2_GAIN__POR, + [TOMTOM_A_RX_LINE_2_TEST] = TOMTOM_A_RX_LINE_2_TEST__POR, + [TOMTOM_A_RX_LINE_2_DAC_CTL] = TOMTOM_A_RX_LINE_2_DAC_CTL__POR, + [TOMTOM_A_RX_LINE_2_STATUS] = TOMTOM_A_RX_LINE_2_STATUS__POR, + [TOMTOM_A_RX_LINE_3_GAIN] = TOMTOM_A_RX_LINE_3_GAIN__POR, + [TOMTOM_A_RX_LINE_3_TEST] = TOMTOM_A_RX_LINE_3_TEST__POR, + [TOMTOM_A_RX_LINE_3_DAC_CTL] = TOMTOM_A_RX_LINE_3_DAC_CTL__POR, + [TOMTOM_A_RX_LINE_3_STATUS] = TOMTOM_A_RX_LINE_3_STATUS__POR, + [TOMTOM_A_RX_LINE_4_GAIN] = TOMTOM_A_RX_LINE_4_GAIN__POR, + [TOMTOM_A_RX_LINE_4_TEST] = TOMTOM_A_RX_LINE_4_TEST__POR, + [TOMTOM_A_RX_LINE_4_DAC_CTL] = TOMTOM_A_RX_LINE_4_DAC_CTL__POR, + [TOMTOM_A_RX_LINE_4_STATUS] = TOMTOM_A_RX_LINE_4_STATUS__POR, + [TOMTOM_A_RX_LINE_CNP_DBG] = TOMTOM_A_RX_LINE_CNP_DBG__POR, + [TOMTOM_A_SPKR_DRV1_EN] = TOMTOM_A_SPKR_DRV1_EN__POR, + [TOMTOM_A_SPKR_DRV1_GAIN] = TOMTOM_A_SPKR_DRV1_GAIN__POR, + [TOMTOM_A_SPKR_DRV1_DAC_CTL] = TOMTOM_A_SPKR_DRV1_DAC_CTL__POR, + [TOMTOM_A_SPKR_DRV1_OCP_CTL] = TOMTOM_A_SPKR_DRV1_OCP_CTL__POR, + [TOMTOM_A_SPKR_DRV1_CLIP_DET] = TOMTOM_A_SPKR_DRV1_CLIP_DET__POR, + [TOMTOM_A_SPKR_DRV1_IEC] = TOMTOM_A_SPKR_DRV1_IEC__POR, + [TOMTOM_A_SPKR_DRV1_DBG_DAC] = TOMTOM_A_SPKR_DRV1_DBG_DAC__POR, + [TOMTOM_A_SPKR_DRV1_DBG_PA] = TOMTOM_A_SPKR_DRV1_DBG_PA__POR, + [TOMTOM_A_SPKR_DRV1_DBG_PWRSTG] = TOMTOM_A_SPKR_DRV1_DBG_PWRSTG__POR, + [TOMTOM_A_SPKR_DRV1_BIAS_LDO] = TOMTOM_A_SPKR_DRV1_BIAS_LDO__POR, + [TOMTOM_A_SPKR_DRV1_BIAS_INT] = TOMTOM_A_SPKR_DRV1_BIAS_INT__POR, + [TOMTOM_A_SPKR_DRV1_BIAS_PA] = TOMTOM_A_SPKR_DRV1_BIAS_PA__POR, + [TOMTOM_A_SPKR_DRV1_STATUS_OCP] = TOMTOM_A_SPKR_DRV1_STATUS_OCP__POR, + [TOMTOM_A_SPKR_DRV1_STATUS_PA] = TOMTOM_A_SPKR_DRV1_STATUS_PA__POR, + [TOMTOM_A_SPKR1_PROT_EN] = TOMTOM_A_SPKR1_PROT_EN__POR, + [TOMTOM_A_SPKR1_PROT_ADC_TEST_EN] = + TOMTOM_A_SPKR1_PROT_ADC_TEST_EN__POR, + [TOMTOM_A_SPKR1_PROT_ATEST] = TOMTOM_A_SPKR1_PROT_ATEST__POR, + [TOMTOM_A_SPKR1_PROT_LDO_CTRL] = TOMTOM_A_SPKR1_PROT_LDO_CTRL__POR, + [TOMTOM_A_SPKR1_PROT_ISENSE_CTRL] = + TOMTOM_A_SPKR1_PROT_ISENSE_CTRL__POR, + [TOMTOM_A_SPKR1_PROT_VSENSE_CTRL] = + TOMTOM_A_SPKR1_PROT_VSENSE_CTRL__POR, + [TOMTOM_A_SPKR2_PROT_EN] = TOMTOM_A_SPKR2_PROT_EN__POR, + [TOMTOM_A_SPKR2_PROT_ADC_TEST_EN] = + TOMTOM_A_SPKR2_PROT_ADC_TEST_EN__POR, + [TOMTOM_A_SPKR2_PROT_ATEST] = TOMTOM_A_SPKR2_PROT_ATEST__POR, + [TOMTOM_A_SPKR2_PROT_LDO_CTRL] = TOMTOM_A_SPKR2_PROT_LDO_CTRL__POR, + [TOMTOM_A_SPKR2_PROT_ISENSE_CTRL] = + TOMTOM_A_SPKR2_PROT_ISENSE_CTRL__POR, + [TOMTOM_A_SPKR2_PROT_VSENSE_CTRL] = + TOMTOM_A_SPKR2_PROT_VSENSE_CTRL__POR, + [TOMTOM_A_MBHC_HPH] = TOMTOM_A_MBHC_HPH__POR, + [TOMTOM_A_CDC_ANC1_B1_CTL] = TOMTOM_A_CDC_ANC1_B1_CTL__POR, + [TOMTOM_A_CDC_ANC2_B1_CTL] = TOMTOM_A_CDC_ANC2_B1_CTL__POR, + [TOMTOM_A_CDC_ANC1_SHIFT] = TOMTOM_A_CDC_ANC1_SHIFT__POR, + [TOMTOM_A_CDC_ANC2_SHIFT] = TOMTOM_A_CDC_ANC2_SHIFT__POR, + [TOMTOM_A_CDC_ANC1_IIR_B1_CTL] = TOMTOM_A_CDC_ANC1_IIR_B1_CTL__POR, + [TOMTOM_A_CDC_ANC2_IIR_B1_CTL] = TOMTOM_A_CDC_ANC2_IIR_B1_CTL__POR, + [TOMTOM_A_CDC_ANC1_IIR_B2_CTL] = TOMTOM_A_CDC_ANC1_IIR_B2_CTL__POR, + [TOMTOM_A_CDC_ANC2_IIR_B2_CTL] = TOMTOM_A_CDC_ANC2_IIR_B2_CTL__POR, + [TOMTOM_A_CDC_ANC1_IIR_B3_CTL] = TOMTOM_A_CDC_ANC1_IIR_B3_CTL__POR, + [TOMTOM_A_CDC_ANC2_IIR_B3_CTL] = TOMTOM_A_CDC_ANC2_IIR_B3_CTL__POR, + [TOMTOM_A_CDC_ANC1_LPF_B1_CTL] = TOMTOM_A_CDC_ANC1_LPF_B1_CTL__POR, + [TOMTOM_A_CDC_ANC2_LPF_B1_CTL] = TOMTOM_A_CDC_ANC2_LPF_B1_CTL__POR, + [TOMTOM_A_CDC_ANC1_LPF_B2_CTL] = TOMTOM_A_CDC_ANC1_LPF_B2_CTL__POR, + [TOMTOM_A_CDC_ANC2_LPF_B2_CTL] = TOMTOM_A_CDC_ANC2_LPF_B2_CTL__POR, + [TOMTOM_A_CDC_ANC1_SPARE] = TOMTOM_A_CDC_ANC1_SPARE__POR, + [TOMTOM_A_CDC_ANC2_SPARE] = TOMTOM_A_CDC_ANC2_SPARE__POR, + [TOMTOM_A_CDC_ANC1_SMLPF_CTL] = TOMTOM_A_CDC_ANC1_SMLPF_CTL__POR, + [TOMTOM_A_CDC_ANC2_SMLPF_CTL] = TOMTOM_A_CDC_ANC2_SMLPF_CTL__POR, + [TOMTOM_A_CDC_ANC1_DCFLT_CTL] = TOMTOM_A_CDC_ANC1_DCFLT_CTL__POR, + [TOMTOM_A_CDC_ANC2_DCFLT_CTL] = TOMTOM_A_CDC_ANC2_DCFLT_CTL__POR, + [TOMTOM_A_CDC_ANC1_GAIN_CTL] = TOMTOM_A_CDC_ANC1_GAIN_CTL__POR, + [TOMTOM_A_CDC_ANC2_GAIN_CTL] = TOMTOM_A_CDC_ANC2_GAIN_CTL__POR, + [TOMTOM_A_CDC_ANC1_B2_CTL] = TOMTOM_A_CDC_ANC1_B2_CTL__POR, + [TOMTOM_A_CDC_ANC2_B2_CTL] = TOMTOM_A_CDC_ANC2_B2_CTL__POR, + [TOMTOM_A_CDC_TX1_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX1_VOL_CTL_TIMER__POR, + [TOMTOM_A_CDC_TX2_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX2_VOL_CTL_TIMER__POR, + [TOMTOM_A_CDC_TX3_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX3_VOL_CTL_TIMER__POR, + [TOMTOM_A_CDC_TX4_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX4_VOL_CTL_TIMER__POR, + [TOMTOM_A_CDC_TX5_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX5_VOL_CTL_TIMER__POR, + [TOMTOM_A_CDC_TX6_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX6_VOL_CTL_TIMER__POR, + [TOMTOM_A_CDC_TX7_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX7_VOL_CTL_TIMER__POR, + [TOMTOM_A_CDC_TX8_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX8_VOL_CTL_TIMER__POR, + [TOMTOM_A_CDC_TX9_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX9_VOL_CTL_TIMER__POR, + [TOMTOM_A_CDC_TX10_VOL_CTL_TIMER] = + TOMTOM_A_CDC_TX10_VOL_CTL_TIMER__POR, + [TOMTOM_A_CDC_TX1_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX1_VOL_CTL_GAIN__POR, + [TOMTOM_A_CDC_TX2_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX2_VOL_CTL_GAIN__POR, + [TOMTOM_A_CDC_TX3_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX3_VOL_CTL_GAIN__POR, + [TOMTOM_A_CDC_TX4_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX4_VOL_CTL_GAIN__POR, + [TOMTOM_A_CDC_TX5_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX5_VOL_CTL_GAIN__POR, + [TOMTOM_A_CDC_TX6_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX6_VOL_CTL_GAIN__POR, + [TOMTOM_A_CDC_TX7_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX7_VOL_CTL_GAIN__POR, + [TOMTOM_A_CDC_TX8_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX8_VOL_CTL_GAIN__POR, + [TOMTOM_A_CDC_TX9_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX9_VOL_CTL_GAIN__POR, + [TOMTOM_A_CDC_TX10_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX10_VOL_CTL_GAIN__POR, + [TOMTOM_A_CDC_TX1_VOL_CTL_CFG] = TOMTOM_A_CDC_TX1_VOL_CTL_CFG__POR, + [TOMTOM_A_CDC_TX2_VOL_CTL_CFG] = TOMTOM_A_CDC_TX2_VOL_CTL_CFG__POR, + [TOMTOM_A_CDC_TX3_VOL_CTL_CFG] = TOMTOM_A_CDC_TX3_VOL_CTL_CFG__POR, + [TOMTOM_A_CDC_TX4_VOL_CTL_CFG] = TOMTOM_A_CDC_TX4_VOL_CTL_CFG__POR, + [TOMTOM_A_CDC_TX5_VOL_CTL_CFG] = TOMTOM_A_CDC_TX5_VOL_CTL_CFG__POR, + [TOMTOM_A_CDC_TX6_VOL_CTL_CFG] = TOMTOM_A_CDC_TX6_VOL_CTL_CFG__POR, + [TOMTOM_A_CDC_TX7_VOL_CTL_CFG] = TOMTOM_A_CDC_TX7_VOL_CTL_CFG__POR, + [TOMTOM_A_CDC_TX8_VOL_CTL_CFG] = TOMTOM_A_CDC_TX8_VOL_CTL_CFG__POR, + [TOMTOM_A_CDC_TX9_VOL_CTL_CFG] = TOMTOM_A_CDC_TX9_VOL_CTL_CFG__POR, + [TOMTOM_A_CDC_TX10_VOL_CTL_CFG] = TOMTOM_A_CDC_TX10_VOL_CTL_CFG__POR, + [TOMTOM_A_CDC_TX1_MUX_CTL] = TOMTOM_A_CDC_TX1_MUX_CTL__POR, + [TOMTOM_A_CDC_TX2_MUX_CTL] = TOMTOM_A_CDC_TX2_MUX_CTL__POR, + [TOMTOM_A_CDC_TX3_MUX_CTL] = TOMTOM_A_CDC_TX3_MUX_CTL__POR, + [TOMTOM_A_CDC_TX4_MUX_CTL] = TOMTOM_A_CDC_TX4_MUX_CTL__POR, + [TOMTOM_A_CDC_TX5_MUX_CTL] = TOMTOM_A_CDC_TX5_MUX_CTL__POR, + [TOMTOM_A_CDC_TX6_MUX_CTL] = TOMTOM_A_CDC_TX6_MUX_CTL__POR, + [TOMTOM_A_CDC_TX7_MUX_CTL] = TOMTOM_A_CDC_TX7_MUX_CTL__POR, + [TOMTOM_A_CDC_TX8_MUX_CTL] = TOMTOM_A_CDC_TX8_MUX_CTL__POR, + [TOMTOM_A_CDC_TX9_MUX_CTL] = TOMTOM_A_CDC_TX9_MUX_CTL__POR, + [TOMTOM_A_CDC_TX10_MUX_CTL] = TOMTOM_A_CDC_TX10_MUX_CTL__POR, + [TOMTOM_A_CDC_TX1_CLK_FS_CTL] = TOMTOM_A_CDC_TX1_CLK_FS_CTL__POR, + [TOMTOM_A_CDC_TX2_CLK_FS_CTL] = TOMTOM_A_CDC_TX2_CLK_FS_CTL__POR, + [TOMTOM_A_CDC_TX3_CLK_FS_CTL] = TOMTOM_A_CDC_TX3_CLK_FS_CTL__POR, + [TOMTOM_A_CDC_TX4_CLK_FS_CTL] = TOMTOM_A_CDC_TX4_CLK_FS_CTL__POR, + [TOMTOM_A_CDC_TX5_CLK_FS_CTL] = TOMTOM_A_CDC_TX5_CLK_FS_CTL__POR, + [TOMTOM_A_CDC_TX6_CLK_FS_CTL] = TOMTOM_A_CDC_TX6_CLK_FS_CTL__POR, + [TOMTOM_A_CDC_TX7_CLK_FS_CTL] = TOMTOM_A_CDC_TX7_CLK_FS_CTL__POR, + [TOMTOM_A_CDC_TX8_CLK_FS_CTL] = TOMTOM_A_CDC_TX8_CLK_FS_CTL__POR, + [TOMTOM_A_CDC_TX9_CLK_FS_CTL] = TOMTOM_A_CDC_TX9_CLK_FS_CTL__POR, + [TOMTOM_A_CDC_TX10_CLK_FS_CTL] = TOMTOM_A_CDC_TX10_CLK_FS_CTL__POR, + [TOMTOM_A_CDC_TX1_DMIC_CTL] = TOMTOM_A_CDC_TX1_DMIC_CTL__POR, + [TOMTOM_A_CDC_TX2_DMIC_CTL] = TOMTOM_A_CDC_TX2_DMIC_CTL__POR, + [TOMTOM_A_CDC_TX3_DMIC_CTL] = TOMTOM_A_CDC_TX3_DMIC_CTL__POR, + [TOMTOM_A_CDC_TX4_DMIC_CTL] = TOMTOM_A_CDC_TX4_DMIC_CTL__POR, + [TOMTOM_A_CDC_TX5_DMIC_CTL] = TOMTOM_A_CDC_TX5_DMIC_CTL__POR, + [TOMTOM_A_CDC_TX6_DMIC_CTL] = TOMTOM_A_CDC_TX6_DMIC_CTL__POR, + [TOMTOM_A_CDC_TX7_DMIC_CTL] = TOMTOM_A_CDC_TX7_DMIC_CTL__POR, + [TOMTOM_A_CDC_TX8_DMIC_CTL] = TOMTOM_A_CDC_TX8_DMIC_CTL__POR, + [TOMTOM_A_CDC_TX9_DMIC_CTL] = TOMTOM_A_CDC_TX9_DMIC_CTL__POR, + [TOMTOM_A_CDC_TX10_DMIC_CTL] = TOMTOM_A_CDC_TX10_DMIC_CTL__POR, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL0] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL0__POR, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL1] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL1__POR, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL2] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL2__POR, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL3] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL3__POR, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL4] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL4__POR, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL5] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL5__POR, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL6] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL6__POR, + [TOMTOM_A_CDC_SPKR_CLIPDET_VAL7] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL7__POR, + [TOMTOM_A_CDC_DEBUG_B1_CTL] = TOMTOM_A_CDC_DEBUG_B1_CTL__POR, + [TOMTOM_A_CDC_DEBUG_B2_CTL] = TOMTOM_A_CDC_DEBUG_B2_CTL__POR, + [TOMTOM_A_CDC_DEBUG_B3_CTL] = TOMTOM_A_CDC_DEBUG_B3_CTL__POR, + [TOMTOM_A_CDC_DEBUG_B4_CTL] = TOMTOM_A_CDC_DEBUG_B4_CTL__POR, + [TOMTOM_A_CDC_DEBUG_B5_CTL] = TOMTOM_A_CDC_DEBUG_B5_CTL__POR, + [TOMTOM_A_CDC_DEBUG_B6_CTL] = TOMTOM_A_CDC_DEBUG_B6_CTL__POR, + [TOMTOM_A_CDC_DEBUG_B7_CTL] = TOMTOM_A_CDC_DEBUG_B7_CTL__POR, + [TOMTOM_A_CDC_SRC1_PDA_CFG] = TOMTOM_A_CDC_SRC1_PDA_CFG__POR, + [TOMTOM_A_CDC_SRC2_PDA_CFG] = TOMTOM_A_CDC_SRC2_PDA_CFG__POR, + [TOMTOM_A_CDC_SRC1_FS_CTL] = TOMTOM_A_CDC_SRC1_FS_CTL__POR, + [TOMTOM_A_CDC_SRC2_FS_CTL] = TOMTOM_A_CDC_SRC2_FS_CTL__POR, + [TOMTOM_A_CDC_RX1_B1_CTL] = TOMTOM_A_CDC_RX1_B1_CTL__POR, + [TOMTOM_A_CDC_RX2_B1_CTL] = TOMTOM_A_CDC_RX2_B1_CTL__POR, + [TOMTOM_A_CDC_RX3_B1_CTL] = TOMTOM_A_CDC_RX3_B1_CTL__POR, + [TOMTOM_A_CDC_RX4_B1_CTL] = TOMTOM_A_CDC_RX4_B1_CTL__POR, + [TOMTOM_A_CDC_RX5_B1_CTL] = TOMTOM_A_CDC_RX5_B1_CTL__POR, + [TOMTOM_A_CDC_RX6_B1_CTL] = TOMTOM_A_CDC_RX6_B1_CTL__POR, + [TOMTOM_A_CDC_RX7_B1_CTL] = TOMTOM_A_CDC_RX7_B1_CTL__POR, + [TOMTOM_A_CDC_RX1_B2_CTL] = TOMTOM_A_CDC_RX1_B2_CTL__POR, + [TOMTOM_A_CDC_RX2_B2_CTL] = TOMTOM_A_CDC_RX2_B2_CTL__POR, + [TOMTOM_A_CDC_RX3_B2_CTL] = TOMTOM_A_CDC_RX3_B2_CTL__POR, + [TOMTOM_A_CDC_RX4_B2_CTL] = TOMTOM_A_CDC_RX4_B2_CTL__POR, + [TOMTOM_A_CDC_RX5_B2_CTL] = TOMTOM_A_CDC_RX5_B2_CTL__POR, + [TOMTOM_A_CDC_RX6_B2_CTL] = TOMTOM_A_CDC_RX6_B2_CTL__POR, + [TOMTOM_A_CDC_RX7_B2_CTL] = TOMTOM_A_CDC_RX7_B2_CTL__POR, + [TOMTOM_A_CDC_RX1_B3_CTL] = TOMTOM_A_CDC_RX1_B3_CTL__POR, + [TOMTOM_A_CDC_RX2_B3_CTL] = TOMTOM_A_CDC_RX2_B3_CTL__POR, + [TOMTOM_A_CDC_RX3_B3_CTL] = TOMTOM_A_CDC_RX3_B3_CTL__POR, + [TOMTOM_A_CDC_RX4_B3_CTL] = TOMTOM_A_CDC_RX4_B3_CTL__POR, + [TOMTOM_A_CDC_RX5_B3_CTL] = TOMTOM_A_CDC_RX5_B3_CTL__POR, + [TOMTOM_A_CDC_RX6_B3_CTL] = TOMTOM_A_CDC_RX6_B3_CTL__POR, + [TOMTOM_A_CDC_RX7_B3_CTL] = TOMTOM_A_CDC_RX7_B3_CTL__POR, + [TOMTOM_A_CDC_RX1_B4_CTL] = TOMTOM_A_CDC_RX1_B4_CTL__POR, + [TOMTOM_A_CDC_RX2_B4_CTL] = TOMTOM_A_CDC_RX2_B4_CTL__POR, + [TOMTOM_A_CDC_RX3_B4_CTL] = TOMTOM_A_CDC_RX3_B4_CTL__POR, + [TOMTOM_A_CDC_RX4_B4_CTL] = TOMTOM_A_CDC_RX4_B4_CTL__POR, + [TOMTOM_A_CDC_RX5_B4_CTL] = TOMTOM_A_CDC_RX5_B4_CTL__POR, + [TOMTOM_A_CDC_RX6_B4_CTL] = TOMTOM_A_CDC_RX6_B4_CTL__POR, + [TOMTOM_A_CDC_RX7_B4_CTL] = TOMTOM_A_CDC_RX7_B4_CTL__POR, + [TOMTOM_A_CDC_RX1_B5_CTL] = TOMTOM_A_CDC_RX1_B5_CTL__POR, + [TOMTOM_A_CDC_RX2_B5_CTL] = TOMTOM_A_CDC_RX2_B5_CTL__POR, + [TOMTOM_A_CDC_RX3_B5_CTL] = TOMTOM_A_CDC_RX3_B5_CTL__POR, + [TOMTOM_A_CDC_RX4_B5_CTL] = TOMTOM_A_CDC_RX4_B5_CTL__POR, + [TOMTOM_A_CDC_RX5_B5_CTL] = TOMTOM_A_CDC_RX5_B5_CTL__POR, + [TOMTOM_A_CDC_RX6_B5_CTL] = TOMTOM_A_CDC_RX6_B5_CTL__POR, + [TOMTOM_A_CDC_RX7_B5_CTL] = TOMTOM_A_CDC_RX7_B5_CTL__POR, + [TOMTOM_A_CDC_RX1_B6_CTL] = TOMTOM_A_CDC_RX1_B6_CTL__POR, + [TOMTOM_A_CDC_RX2_B6_CTL] = TOMTOM_A_CDC_RX2_B6_CTL__POR, + [TOMTOM_A_CDC_RX3_B6_CTL] = TOMTOM_A_CDC_RX3_B6_CTL__POR, + [TOMTOM_A_CDC_RX4_B6_CTL] = TOMTOM_A_CDC_RX4_B6_CTL__POR, + [TOMTOM_A_CDC_RX5_B6_CTL] = TOMTOM_A_CDC_RX5_B6_CTL__POR, + [TOMTOM_A_CDC_RX6_B6_CTL] = TOMTOM_A_CDC_RX6_B6_CTL__POR, + [TOMTOM_A_CDC_RX7_B6_CTL] = TOMTOM_A_CDC_RX7_B6_CTL__POR, + [TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL] = + TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL__POR, + [TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL] = + TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL__POR, + [TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL] = + TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL__POR, + [TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL] = + TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL__POR, + [TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL] = + TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL__POR, + [TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL] = + TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL__POR, + [TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL] = + TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL__POR, + [TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL] = + TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL__POR, + [TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL] = + TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL__POR, + [TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL] = + TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL__POR, + [TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL] = + TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL__POR, + [TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL] = + TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL__POR, + [TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL] = + TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL__POR, + [TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL] = + TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL__POR, + [TOMTOM_A_CDC_VBAT_CFG] = TOMTOM_A_CDC_VBAT_CFG__POR, + [TOMTOM_A_CDC_VBAT_ADC_CAL1] = TOMTOM_A_CDC_VBAT_ADC_CAL1__POR, + [TOMTOM_A_CDC_VBAT_ADC_CAL2] = TOMTOM_A_CDC_VBAT_ADC_CAL2__POR, + [TOMTOM_A_CDC_VBAT_ADC_CAL3] = TOMTOM_A_CDC_VBAT_ADC_CAL3__POR, + [TOMTOM_A_CDC_VBAT_PK_EST1] = TOMTOM_A_CDC_VBAT_PK_EST1__POR, + [TOMTOM_A_CDC_VBAT_PK_EST2] = TOMTOM_A_CDC_VBAT_PK_EST2__POR, + [TOMTOM_A_CDC_VBAT_PK_EST3] = TOMTOM_A_CDC_VBAT_PK_EST3__POR, + [TOMTOM_A_CDC_VBAT_RF_PROC1] = TOMTOM_A_CDC_VBAT_RF_PROC1__POR, + [TOMTOM_A_CDC_VBAT_RF_PROC2] = TOMTOM_A_CDC_VBAT_RF_PROC2__POR, + [TOMTOM_A_CDC_VBAT_TAC1] = TOMTOM_A_CDC_VBAT_TAC1__POR, + [TOMTOM_A_CDC_VBAT_TAC2] = TOMTOM_A_CDC_VBAT_TAC2__POR, + [TOMTOM_A_CDC_VBAT_TAC3] = TOMTOM_A_CDC_VBAT_TAC3__POR, + [TOMTOM_A_CDC_VBAT_TAC4] = TOMTOM_A_CDC_VBAT_TAC4__POR, + [TOMTOM_A_CDC_VBAT_GAIN_UPD1] = TOMTOM_A_CDC_VBAT_GAIN_UPD1__POR, + [TOMTOM_A_CDC_VBAT_GAIN_UPD2] = TOMTOM_A_CDC_VBAT_GAIN_UPD2__POR, + [TOMTOM_A_CDC_VBAT_GAIN_UPD3] = TOMTOM_A_CDC_VBAT_GAIN_UPD3__POR, + [TOMTOM_A_CDC_VBAT_GAIN_UPD4] = TOMTOM_A_CDC_VBAT_GAIN_UPD4__POR, + [TOMTOM_A_CDC_VBAT_DEBUG1] = TOMTOM_A_CDC_VBAT_DEBUG1__POR, + [TOMTOM_A_CDC_VBAT_GAIN_UPD_MON] = TOMTOM_A_CDC_VBAT_GAIN_UPD_MON__POR, + [TOMTOM_A_CDC_VBAT_GAIN_MON_VAL] = TOMTOM_A_CDC_VBAT_GAIN_MON_VAL__POR, + [TOMTOM_A_CDC_CLK_ANC_RESET_CTL] = TOMTOM_A_CDC_CLK_ANC_RESET_CTL__POR, + [TOMTOM_A_CDC_CLK_RX_RESET_CTL] = TOMTOM_A_CDC_CLK_RX_RESET_CTL__POR, + [TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL] = + TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL__POR, + [TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL] = + TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL__POR, + [TOMTOM_A_CDC_CLK_RX_I2S_CTL] = TOMTOM_A_CDC_CLK_RX_I2S_CTL__POR, + [TOMTOM_A_CDC_CLK_TX_I2S_CTL] = TOMTOM_A_CDC_CLK_TX_I2S_CTL__POR, + [TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL] = + TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL__POR, + [TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL] = + TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL__POR, + [TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL] = + TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR, + [TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL] = + TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL__POR, + [TOMTOM_A_CDC_CLK_OTHR_CTL] = TOMTOM_A_CDC_CLK_OTHR_CTL__POR, + [TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL] = + TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL__POR, + [TOMTOM_A_CDC_CLK_RX_B1_CTL] = TOMTOM_A_CDC_CLK_RX_B1_CTL__POR, + [TOMTOM_A_CDC_CLK_RX_B2_CTL] = TOMTOM_A_CDC_CLK_RX_B2_CTL__POR, + [TOMTOM_A_CDC_CLK_MCLK_CTL] = TOMTOM_A_CDC_CLK_MCLK_CTL__POR, + [TOMTOM_A_CDC_CLK_PDM_CTL] = TOMTOM_A_CDC_CLK_PDM_CTL__POR, + [TOMTOM_A_CDC_CLK_SD_CTL] = TOMTOM_A_CDC_CLK_SD_CTL__POR, + [TOMTOM_A_CDC_CLSH_B1_CTL] = TOMTOM_A_CDC_CLSH_B1_CTL__POR, + [TOMTOM_A_CDC_CLSH_B2_CTL] = TOMTOM_A_CDC_CLSH_B2_CTL__POR, + [TOMTOM_A_CDC_CLSH_B3_CTL] = TOMTOM_A_CDC_CLSH_B3_CTL__POR, + [TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS] = + TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS__POR, + [TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD] = + TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD__POR, + [TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD] = + TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD__POR, + [TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD] = + TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR, + [TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD] = + TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR, + [TOMTOM_A_CDC_CLSH_K_ADDR] = TOMTOM_A_CDC_CLSH_K_ADDR__POR, + [TOMTOM_A_CDC_CLSH_K_DATA] = TOMTOM_A_CDC_CLSH_K_DATA__POR, + [TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L] = + TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L__POR, + [TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U] = + TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U__POR, + [TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L] = + TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L__POR, + [TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U] = + TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U__POR, + [TOMTOM_A_CDC_CLSH_V_PA_HD_EAR] = TOMTOM_A_CDC_CLSH_V_PA_HD_EAR__POR, + [TOMTOM_A_CDC_CLSH_V_PA_HD_HPH] = TOMTOM_A_CDC_CLSH_V_PA_HD_HPH__POR, + [TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR] = TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR__POR, + [TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH] = TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH__POR, + [TOMTOM_A_CDC_IIR1_GAIN_B1_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B1_CTL__POR, + [TOMTOM_A_CDC_IIR2_GAIN_B1_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B1_CTL__POR, + [TOMTOM_A_CDC_IIR1_GAIN_B2_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B2_CTL__POR, + [TOMTOM_A_CDC_IIR2_GAIN_B2_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B2_CTL__POR, + [TOMTOM_A_CDC_IIR1_GAIN_B3_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B3_CTL__POR, + [TOMTOM_A_CDC_IIR2_GAIN_B3_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B3_CTL__POR, + [TOMTOM_A_CDC_IIR1_GAIN_B4_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B4_CTL__POR, + [TOMTOM_A_CDC_IIR2_GAIN_B4_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B4_CTL__POR, + [TOMTOM_A_CDC_IIR1_GAIN_B5_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B5_CTL__POR, + [TOMTOM_A_CDC_IIR2_GAIN_B5_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B5_CTL__POR, + [TOMTOM_A_CDC_IIR1_GAIN_B6_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B6_CTL__POR, + [TOMTOM_A_CDC_IIR2_GAIN_B6_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B6_CTL__POR, + [TOMTOM_A_CDC_IIR1_GAIN_B7_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B7_CTL__POR, + [TOMTOM_A_CDC_IIR2_GAIN_B7_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B7_CTL__POR, + [TOMTOM_A_CDC_IIR1_GAIN_B8_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B8_CTL__POR, + [TOMTOM_A_CDC_IIR2_GAIN_B8_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B8_CTL__POR, + [TOMTOM_A_CDC_IIR1_CTL] = TOMTOM_A_CDC_IIR1_CTL__POR, + [TOMTOM_A_CDC_IIR2_CTL] = TOMTOM_A_CDC_IIR2_CTL__POR, + [TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL] = + TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL__POR, + [TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL] = + TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL__POR, + [TOMTOM_A_CDC_IIR1_COEF_B1_CTL] = TOMTOM_A_CDC_IIR1_COEF_B1_CTL__POR, + [TOMTOM_A_CDC_IIR2_COEF_B1_CTL] = TOMTOM_A_CDC_IIR2_COEF_B1_CTL__POR, + [TOMTOM_A_CDC_IIR1_COEF_B2_CTL] = TOMTOM_A_CDC_IIR1_COEF_B2_CTL__POR, + [TOMTOM_A_CDC_IIR2_COEF_B2_CTL] = TOMTOM_A_CDC_IIR2_COEF_B2_CTL__POR, + [TOMTOM_A_CDC_TOP_GAIN_UPDATE] = TOMTOM_A_CDC_TOP_GAIN_UPDATE__POR, + [TOMTOM_A_CDC_PA_RAMP_B1_CTL] = TOMTOM_A_CDC_PA_RAMP_B1_CTL__POR, + [TOMTOM_A_CDC_PA_RAMP_B2_CTL] = TOMTOM_A_CDC_PA_RAMP_B2_CTL__POR, + [TOMTOM_A_CDC_PA_RAMP_B3_CTL] = TOMTOM_A_CDC_PA_RAMP_B3_CTL__POR, + [TOMTOM_A_CDC_PA_RAMP_B4_CTL] = TOMTOM_A_CDC_PA_RAMP_B4_CTL__POR, + [TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL] = + TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL__POR, + [TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL] = + TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL__POR, + [TOMTOM_A_CDC_COMP0_B1_CTL] = TOMTOM_A_CDC_COMP0_B1_CTL__POR, + [TOMTOM_A_CDC_COMP1_B1_CTL] = TOMTOM_A_CDC_COMP1_B1_CTL__POR, + [TOMTOM_A_CDC_COMP2_B1_CTL] = TOMTOM_A_CDC_COMP2_B1_CTL__POR, + [TOMTOM_A_CDC_COMP0_B2_CTL] = TOMTOM_A_CDC_COMP0_B2_CTL__POR, + [TOMTOM_A_CDC_COMP1_B2_CTL] = TOMTOM_A_CDC_COMP1_B2_CTL__POR, + [TOMTOM_A_CDC_COMP2_B2_CTL] = TOMTOM_A_CDC_COMP2_B2_CTL__POR, + [TOMTOM_A_CDC_COMP0_B3_CTL] = TOMTOM_A_CDC_COMP0_B3_CTL__POR, + [TOMTOM_A_CDC_COMP1_B3_CTL] = TOMTOM_A_CDC_COMP1_B3_CTL__POR, + [TOMTOM_A_CDC_COMP2_B3_CTL] = TOMTOM_A_CDC_COMP2_B3_CTL__POR, + [TOMTOM_A_CDC_COMP0_B4_CTL] = TOMTOM_A_CDC_COMP0_B4_CTL__POR, + [TOMTOM_A_CDC_COMP1_B4_CTL] = TOMTOM_A_CDC_COMP1_B4_CTL__POR, + [TOMTOM_A_CDC_COMP2_B4_CTL] = TOMTOM_A_CDC_COMP2_B4_CTL__POR, + [TOMTOM_A_CDC_COMP0_B5_CTL] = TOMTOM_A_CDC_COMP0_B5_CTL__POR, + [TOMTOM_A_CDC_COMP1_B5_CTL] = TOMTOM_A_CDC_COMP1_B5_CTL__POR, + [TOMTOM_A_CDC_COMP2_B5_CTL] = TOMTOM_A_CDC_COMP2_B5_CTL__POR, + [TOMTOM_A_CDC_COMP0_B6_CTL] = TOMTOM_A_CDC_COMP0_B6_CTL__POR, + [TOMTOM_A_CDC_COMP1_B6_CTL] = TOMTOM_A_CDC_COMP1_B6_CTL__POR, + [TOMTOM_A_CDC_COMP2_B6_CTL] = TOMTOM_A_CDC_COMP2_B6_CTL__POR, + [TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS] = + TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS__POR, + [TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS] = + TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS__POR, + [TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS] = + TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS__POR, + [TOMTOM_A_CDC_COMP0_FS_CFG] = TOMTOM_A_CDC_COMP0_FS_CFG__POR, + [TOMTOM_A_CDC_COMP1_FS_CFG] = TOMTOM_A_CDC_COMP1_FS_CFG__POR, + [TOMTOM_A_CDC_COMP2_FS_CFG] = TOMTOM_A_CDC_COMP2_FS_CFG__POR, + [TOMTOM_A_CDC_CONN_RX1_B1_CTL] = TOMTOM_A_CDC_CONN_RX1_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_RX1_B2_CTL] = TOMTOM_A_CDC_CONN_RX1_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_RX1_B3_CTL] = TOMTOM_A_CDC_CONN_RX1_B3_CTL__POR, + [TOMTOM_A_CDC_CONN_RX2_B1_CTL] = TOMTOM_A_CDC_CONN_RX2_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_RX2_B2_CTL] = TOMTOM_A_CDC_CONN_RX2_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_RX2_B3_CTL] = TOMTOM_A_CDC_CONN_RX2_B3_CTL__POR, + [TOMTOM_A_CDC_CONN_RX3_B1_CTL] = TOMTOM_A_CDC_CONN_RX3_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_RX3_B2_CTL] = TOMTOM_A_CDC_CONN_RX3_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_RX4_B1_CTL] = TOMTOM_A_CDC_CONN_RX4_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_RX4_B2_CTL] = TOMTOM_A_CDC_CONN_RX4_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_RX5_B1_CTL] = TOMTOM_A_CDC_CONN_RX5_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_RX5_B2_CTL] = TOMTOM_A_CDC_CONN_RX5_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_RX6_B1_CTL] = TOMTOM_A_CDC_CONN_RX6_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_RX6_B2_CTL] = TOMTOM_A_CDC_CONN_RX6_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_RX7_B1_CTL] = TOMTOM_A_CDC_CONN_RX7_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_RX7_B2_CTL] = TOMTOM_A_CDC_CONN_RX7_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_RX7_B3_CTL] = TOMTOM_A_CDC_CONN_RX7_B3_CTL__POR, + [TOMTOM_A_CDC_CONN_ANC_B1_CTL] = TOMTOM_A_CDC_CONN_ANC_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_ANC_B2_CTL] = TOMTOM_A_CDC_CONN_ANC_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_B1_CTL] = TOMTOM_A_CDC_CONN_TX_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_B2_CTL] = TOMTOM_A_CDC_CONN_TX_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_B3_CTL] = TOMTOM_A_CDC_CONN_TX_B3_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_B4_CTL] = TOMTOM_A_CDC_CONN_TX_B4_CTL__POR, + [TOMTOM_A_CDC_CONN_EQ1_B1_CTL] = TOMTOM_A_CDC_CONN_EQ1_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_EQ1_B2_CTL] = TOMTOM_A_CDC_CONN_EQ1_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_EQ1_B3_CTL] = TOMTOM_A_CDC_CONN_EQ1_B3_CTL__POR, + [TOMTOM_A_CDC_CONN_EQ1_B4_CTL] = TOMTOM_A_CDC_CONN_EQ1_B4_CTL__POR, + [TOMTOM_A_CDC_CONN_EQ2_B1_CTL] = TOMTOM_A_CDC_CONN_EQ2_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_EQ2_B2_CTL] = TOMTOM_A_CDC_CONN_EQ2_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_EQ2_B3_CTL] = TOMTOM_A_CDC_CONN_EQ2_B3_CTL__POR, + [TOMTOM_A_CDC_CONN_EQ2_B4_CTL] = TOMTOM_A_CDC_CONN_EQ2_B4_CTL__POR, + [TOMTOM_A_CDC_CONN_SRC1_B1_CTL] = TOMTOM_A_CDC_CONN_SRC1_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_SRC1_B2_CTL] = TOMTOM_A_CDC_CONN_SRC1_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_SRC2_B1_CTL] = TOMTOM_A_CDC_CONN_SRC2_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_SRC2_B2_CTL] = TOMTOM_A_CDC_CONN_SRC2_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_SB_B1_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_SB_B2_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_SB_B3_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B3_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_SB_B4_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B4_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_SB_B5_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B5_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_SB_B6_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B6_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_SB_B7_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B7_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_SB_B8_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B8_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_SB_B9_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B9_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_SB_B10_CTL] = + TOMTOM_A_CDC_CONN_TX_SB_B10_CTL__POR, + [TOMTOM_A_CDC_CONN_TX_SB_B11_CTL] = + TOMTOM_A_CDC_CONN_TX_SB_B11_CTL__POR, + [TOMTOM_A_CDC_CONN_RX_SB_B1_CTL] = TOMTOM_A_CDC_CONN_RX_SB_B1_CTL__POR, + [TOMTOM_A_CDC_CONN_RX_SB_B2_CTL] = TOMTOM_A_CDC_CONN_RX_SB_B2_CTL__POR, + [TOMTOM_A_CDC_CONN_CLSH_CTL] = TOMTOM_A_CDC_CONN_CLSH_CTL__POR, + [TOMTOM_A_CDC_CONN_MISC] = TOMTOM_A_CDC_CONN_MISC__POR, + [TOMTOM_A_CDC_CONN_RX8_B1_CTL] = TOMTOM_A_CDC_CONN_RX8_B1_CTL__POR, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL] = + TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL__POR, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST] = + TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST__POR, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD] = + TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD__POR, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS] = + TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS__POR, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK] = + TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK__POR, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING] = + TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING__POR, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL] = + TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL__POR, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST] = + TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST__POR, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD] = + TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD__POR, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS] = + TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS__POR, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK] = + TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK__POR, + [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING] = + TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING__POR, + [TOMTOM_A_CDC_MBHC_EN_CTL] = TOMTOM_A_CDC_MBHC_EN_CTL__POR, + [TOMTOM_A_CDC_MBHC_FIR_B1_CFG] = TOMTOM_A_CDC_MBHC_FIR_B1_CFG__POR, + [TOMTOM_A_CDC_MBHC_FIR_B2_CFG] = TOMTOM_A_CDC_MBHC_FIR_B2_CFG__POR, + [TOMTOM_A_CDC_MBHC_TIMER_B1_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B1_CTL__POR, + [TOMTOM_A_CDC_MBHC_TIMER_B2_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B2_CTL__POR, + [TOMTOM_A_CDC_MBHC_TIMER_B3_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B3_CTL__POR, + [TOMTOM_A_CDC_MBHC_TIMER_B4_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B4_CTL__POR, + [TOMTOM_A_CDC_MBHC_TIMER_B5_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B5_CTL__POR, + [TOMTOM_A_CDC_MBHC_TIMER_B6_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B6_CTL__POR, + [TOMTOM_A_CDC_MBHC_B1_STATUS] = TOMTOM_A_CDC_MBHC_B1_STATUS__POR, + [TOMTOM_A_CDC_MBHC_B2_STATUS] = TOMTOM_A_CDC_MBHC_B2_STATUS__POR, + [TOMTOM_A_CDC_MBHC_B3_STATUS] = TOMTOM_A_CDC_MBHC_B3_STATUS__POR, + [TOMTOM_A_CDC_MBHC_B4_STATUS] = TOMTOM_A_CDC_MBHC_B4_STATUS__POR, + [TOMTOM_A_CDC_MBHC_B5_STATUS] = TOMTOM_A_CDC_MBHC_B5_STATUS__POR, + [TOMTOM_A_CDC_MBHC_B1_CTL] = TOMTOM_A_CDC_MBHC_B1_CTL__POR, + [TOMTOM_A_CDC_MBHC_B2_CTL] = TOMTOM_A_CDC_MBHC_B2_CTL__POR, + [TOMTOM_A_CDC_MBHC_VOLT_B1_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B1_CTL__POR, + [TOMTOM_A_CDC_MBHC_VOLT_B2_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B2_CTL__POR, + [TOMTOM_A_CDC_MBHC_VOLT_B3_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B3_CTL__POR, + [TOMTOM_A_CDC_MBHC_VOLT_B4_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B4_CTL__POR, + [TOMTOM_A_CDC_MBHC_VOLT_B5_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B5_CTL__POR, + [TOMTOM_A_CDC_MBHC_VOLT_B6_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B6_CTL__POR, + [TOMTOM_A_CDC_MBHC_VOLT_B7_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B7_CTL__POR, + [TOMTOM_A_CDC_MBHC_VOLT_B8_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B8_CTL__POR, + [TOMTOM_A_CDC_MBHC_VOLT_B9_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B9_CTL__POR, + [TOMTOM_A_CDC_MBHC_VOLT_B10_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B10_CTL__POR, + [TOMTOM_A_CDC_MBHC_VOLT_B11_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B11_CTL__POR, + [TOMTOM_A_CDC_MBHC_VOLT_B12_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B12_CTL__POR, + [TOMTOM_A_CDC_MBHC_CLK_CTL] = TOMTOM_A_CDC_MBHC_CLK_CTL__POR, + [TOMTOM_A_CDC_MBHC_INT_CTL] = TOMTOM_A_CDC_MBHC_INT_CTL__POR, + [TOMTOM_A_CDC_MBHC_DEBUG_CTL] = TOMTOM_A_CDC_MBHC_DEBUG_CTL__POR, + [TOMTOM_A_CDC_MBHC_SPARE] = TOMTOM_A_CDC_MBHC_SPARE__POR, + [TOMTOM_A_CDC_RX8_B1_CTL] = TOMTOM_A_CDC_RX8_B1_CTL__POR, + [TOMTOM_A_CDC_RX8_B2_CTL] = TOMTOM_A_CDC_RX8_B2_CTL__POR, + [TOMTOM_A_CDC_RX8_B3_CTL] = TOMTOM_A_CDC_RX8_B3_CTL__POR, + [TOMTOM_A_CDC_RX8_B4_CTL] = TOMTOM_A_CDC_RX8_B4_CTL__POR, + [TOMTOM_A_CDC_RX8_B5_CTL] = TOMTOM_A_CDC_RX8_B5_CTL__POR, + [TOMTOM_A_CDC_RX8_B6_CTL] = TOMTOM_A_CDC_RX8_B6_CTL__POR, + [TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL] = + TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL__POR, + [TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL] = + TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL__POR, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0] = + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0__POR, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1] = + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1__POR, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2] = + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2__POR, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3] = + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3__POR, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4] = + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4__POR, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5] = + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5__POR, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6] = + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6__POR, + [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7] = + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7__POR, + [TOMTOM_A_CDC_BOOST_MODE_CTL] = TOMTOM_A_CDC_BOOST_MODE_CTL__POR, + [TOMTOM_A_CDC_BOOST_THRESHOLD] = TOMTOM_A_CDC_BOOST_THRESHOLD__POR, + [TOMTOM_A_CDC_BOOST_TAP_SEL] = TOMTOM_A_CDC_BOOST_TAP_SEL__POR, + [TOMTOM_A_CDC_BOOST_HOLD_TIME] = TOMTOM_A_CDC_BOOST_HOLD_TIME__POR, + [TOMTOM_A_CDC_BOOST_TRGR_EN] = TOMTOM_A_CDC_BOOST_TRGR_EN__POR, +}; diff --git a/sound/soc/codecs/wcd9330.c b/sound/soc/codecs/wcd9330.c new file mode 100644 index 0000000000000000000000000000000000000000..dfdfcfaf75d618f6374ae6801a1fe3a52f0d5c7a --- /dev/null +++ b/sound/soc/codecs/wcd9330.c @@ -0,0 +1,9107 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd9330.h" +#include "wcd9xxx-resmgr.h" +#include "wcd9xxx-common.h" +#include "wcdcal-hwdep.h" +#include "wcd_cpe_core.h" + +enum { + BUS_DOWN, + ADC1_TXFE, + ADC2_TXFE, + ADC3_TXFE, + ADC4_TXFE, + ADC5_TXFE, + ADC6_TXFE, + HPH_DELAY, +}; + +#define TOMTOM_MAD_SLIMBUS_TX_PORT 12 +#define TOMTOM_MAD_AUDIO_FIRMWARE_PATH "wcd9320/wcd9320_mad_audio.bin" +#define TOMTOM_VALIDATE_RX_SBPORT_RANGE(port) ((port >= 16) && (port <= 23)) +#define TOMTOM_VALIDATE_TX_SBPORT_RANGE(port) ((port >= 0) && (port <= 15)) +#define TOMTOM_CONVERT_RX_SBPORT_ID(port) (port - 16) /* RX1 port ID = 0 */ +#define TOMTOM_BIT_ADJ_SHIFT_PORT1_6 4 +#define TOMTOM_BIT_ADJ_SHIFT_PORT7_10 5 + +#define TOMTOM_HPH_PA_SETTLE_COMP_ON 10000 +#define TOMTOM_HPH_PA_SETTLE_COMP_OFF 13000 +#define TOMTOM_HPH_PA_RAMP_DELAY 30000 + +#define TOMTOM_SVASS_INT_STATUS_RCO_WDOG 0x20 +#define TOMTOM_SVASS_INT_STATUS_WDOG_BITE 0x02 + +/* Add any SVA IRQs that are to be treated as FATAL */ +#define TOMTOM_CPE_FATAL_IRQS \ + (TOMTOM_SVASS_INT_STATUS_RCO_WDOG | \ + TOMTOM_SVASS_INT_STATUS_WDOG_BITE) + +#define DAPM_MICBIAS2_EXTERNAL_STANDALONE "MIC BIAS2 External Standalone" + +/* RX_HPH_CNP_WG_TIME increases by 0.24ms */ +#define TOMTOM_WG_TIME_FACTOR_US 240 + +#define RX8_PATH 8 +#define HPH_PA_ENABLE true +#define HPH_PA_DISABLE false + +#define SLIM_BW_CLK_GEAR_9 6200000 +#define SLIM_BW_UNVOTE 0 + +static int cpe_debug_mode; +module_param(cpe_debug_mode, int, 0664); +MODULE_PARM_DESC(cpe_debug_mode, "boot cpe in debug mode"); + +static atomic_t kp_tomtom_priv; + +static int high_perf_mode; +module_param(high_perf_mode, int, 0664); +MODULE_PARM_DESC(high_perf_mode, "enable/disable class AB config for hph"); + +static struct afe_param_slimbus_slave_port_cfg tomtom_slimbus_slave_port_cfg = { + .minor_version = 1, + .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1, + .slave_dev_pgd_la = 0, + .slave_dev_intfdev_la = 0, + .bit_width = 16, + .data_format = 0, + .num_channels = 1 +}; + +static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = { + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_MAD_MAIN_CTL_1), + HW_MAD_AUDIO_ENABLE, 0x1, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_MAD_AUDIO_CTL_3), + HW_MAD_AUDIO_SLEEP_TIME, 0xF, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_MAD_AUDIO_CTL_4), + HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR_MODE), + MAD_AUDIO_INT_DEST_SELECT_REG, 0x4, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_MASK0), + MAD_AUDIO_INT_MASK_REG, 0x2, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_STATUS0), + MAD_AUDIO_INT_STATUS_REG, 0x2, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_CLEAR0), + MAD_AUDIO_INT_CLEAR_REG, 0x2, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_WATERMARK_N, 0x1E, 8, 0x1 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_ENABLE_N, 0x1, 8, 0x1 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_WATERMARK_N, 0x1E, 8, 0x1 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_ENABLE_N, 0x1, 8, 0x1 + }, + { 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_ANC1_IIR_B1_CTL), + AANC_FF_GAIN_ADAPTIVE, 0x4, 8, 0 + }, + { 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_ANC1_IIR_B1_CTL), + AANC_FFGAIN_ADAPTIVE_EN, 0x8, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_ANC1_GAIN_CTL), + AANC_GAIN_CONTROL, 0xFF, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_MASK0), + MAD_CLIP_INT_MASK_REG, 0x10, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_MASK0), + MAD2_CLIP_INT_MASK_REG, 0x20, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_STATUS0), + MAD_CLIP_INT_STATUS_REG, 0x10, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_STATUS0), + MAD2_CLIP_INT_STATUS_REG, 0x20, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_CLEAR0), + MAD_CLIP_INT_CLEAR_REG, 0x10, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_CLEAR0), + MAD2_CLIP_INT_CLEAR_REG, 0x20, 8, 0 + }, +}; + +static struct afe_param_cdc_reg_cfg clip_reg_cfg[] = { + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL), + SPKR_CLIP_PIPE_BANK_SEL, 0x3, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR_CLIPDET_VAL0), + SPKR_CLIPDET_VAL0, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR_CLIPDET_VAL1), + SPKR_CLIPDET_VAL1, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR_CLIPDET_VAL2), + SPKR_CLIPDET_VAL2, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR_CLIPDET_VAL3), + SPKR_CLIPDET_VAL3, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR_CLIPDET_VAL4), + SPKR_CLIPDET_VAL4, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR_CLIPDET_VAL5), + SPKR_CLIPDET_VAL5, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR_CLIPDET_VAL6), + SPKR_CLIPDET_VAL6, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR_CLIPDET_VAL7), + SPKR_CLIPDET_VAL7, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL), + SPKR2_CLIP_PIPE_BANK_SEL, 0x3, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0), + SPKR2_CLIPDET_VAL0, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1), + SPKR2_CLIPDET_VAL1, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2), + SPKR2_CLIPDET_VAL2, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3), + SPKR2_CLIPDET_VAL3, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4), + SPKR2_CLIPDET_VAL4, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5), + SPKR2_CLIPDET_VAL5, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6), + SPKR2_CLIPDET_VAL6, 0xff, 8, 0 + }, + { + 1, + (TOMTOM_REGISTER_START_OFFSET + + TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7), + SPKR2_CLIPDET_VAL7, 0xff, 8, 0 + }, +}; + +static struct afe_param_cdc_reg_cfg_data tomtom_audio_reg_cfg = { + .num_registers = ARRAY_SIZE(audio_reg_cfg), + .reg_data = audio_reg_cfg, +}; + +static struct afe_param_cdc_reg_cfg_data tomtom_clip_reg_cfg = { + .num_registers = ARRAY_SIZE(clip_reg_cfg), + .reg_data = clip_reg_cfg, +}; + +static struct afe_param_id_cdc_aanc_version tomtom_cdc_aanc_version = { + .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION, + .aanc_hw_version = AANC_HW_BLOCK_VERSION_2, +}; + +static struct afe_param_id_clip_bank_sel clip_bank_sel = { + .minor_version = AFE_API_VERSION_CLIP_BANK_SEL_CFG, + .num_banks = AFE_CLIP_MAX_BANKS, + .bank_map = {0, 1, 2, 3}, +}; + +#define WCD9330_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) + +#define NUM_DECIMATORS 10 +#define NUM_INTERPOLATORS 8 +#define BITS_PER_REG 8 +#define TOMTOM_TX_PORT_NUMBER 16 +#define TOMTOM_RX_PORT_START_NUMBER 16 + +#define TOMTOM_I2S_MASTER_MODE_MASK 0x08 + +#define TOMTOM_SLIM_CLOSE_TIMEOUT 1000 +#define TOMTOM_SLIM_IRQ_OVERFLOW (1 << 0) +#define TOMTOM_SLIM_IRQ_UNDERFLOW (1 << 1) +#define TOMTOM_SLIM_IRQ_PORT_CLOSED (1 << 2) +#define TOMTOM_MCLK_CLK_12P288MHZ 12288000 +#define TOMTOM_MCLK_CLK_9P6MHZ 9600000 + +#define TOMTOM_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FORMAT_S24_LE) + +#define TOMTOM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +#define TOMTOM_SLIM_PGD_PORT_INT_TX_EN0 (TOMTOM_SLIM_PGD_PORT_INT_EN0 + 2) +#define TOMTOM_ZDET_BOX_CAR_AVG_LOOP_COUNT 1 +#define TOMTOM_ZDET_MUL_FACTOR_1X 7218 +#define TOMTOM_ZDET_MUL_FACTOR_10X (TOMTOM_ZDET_MUL_FACTOR_1X * 10) +#define TOMTOM_ZDET_MUL_FACTOR_100X (TOMTOM_ZDET_MUL_FACTOR_1X * 100) +#define TOMTOM_ZDET_ERROR_APPROX_MUL_FACTOR 655 +#define TOMTOM_ZDET_ERROR_APPROX_SHIFT 16 +#define TOMTOM_ZDET_ZONE_3_DEFAULT_VAL 1000000 + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_PB, + AIF2_CAP, + AIF3_PB, + AIF3_CAP, + AIF4_VIFEED, + AIF4_MAD_TX, + NUM_CODEC_DAIS, +}; + +enum { + RX_MIX1_INP_SEL_ZERO = 0, + RX_MIX1_INP_SEL_SRC1, + RX_MIX1_INP_SEL_SRC2, + RX_MIX1_INP_SEL_IIR1, + RX_MIX1_INP_SEL_IIR2, + RX_MIX1_INP_SEL_RX1, + RX_MIX1_INP_SEL_RX2, + RX_MIX1_INP_SEL_RX3, + RX_MIX1_INP_SEL_RX4, + RX_MIX1_INP_SEL_RX5, + RX_MIX1_INP_SEL_RX6, + RX_MIX1_INP_SEL_RX7, + RX_MIX1_INP_SEL_AUXRX, +}; +enum { + RX8_MIX1_INP_SEL_ZERO = 0, + RX8_MIX1_INP_SEL_IIR1, + RX8_MIX1_INP_SEL_IIR2, + RX8_MIX1_INP_SEL_RX1, + RX8_MIX1_INP_SEL_RX2, + RX8_MIX1_INP_SEL_RX3, + RX8_MIX1_INP_SEL_RX4, + RX8_MIX1_INP_SEL_RX5, + RX8_MIX1_INP_SEL_RX6, + RX8_MIX1_INP_SEL_RX7, + RX8_MIX1_INP_SEL_RX8, +}; + +#define TOMTOM_COMP_DIGITAL_GAIN_OFFSET 3 + +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 struct snd_soc_dai_driver tomtom_dai[]; +static const DECLARE_TLV_DB_SCALE(aux_pga_gain, 0, 2, 0); + +/* Codec supports 2 IIR filters */ +enum { + IIR1 = 0, + IIR2, + IIR_MAX, +}; +/* Codec supports 5 bands */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +enum { + COMPANDER_0, + COMPANDER_1, + COMPANDER_2, + COMPANDER_MAX, +}; + +enum { + COMPANDER_FS_8KHZ = 0, + COMPANDER_FS_16KHZ, + COMPANDER_FS_32KHZ, + COMPANDER_FS_48KHZ, + COMPANDER_FS_96KHZ, + COMPANDER_FS_192KHZ, + COMPANDER_FS_MAX, +}; + +struct comp_sample_dependent_params { + u32 peak_det_timeout; + u32 rms_meter_div_fact; + u32 rms_meter_resamp_fact; +}; + +struct hpf_work { + struct tomtom_priv *tomtom; + u32 decimator; + u8 tx_hpf_cut_of_freq; + bool tx_hpf_bypass; + struct delayed_work dwork; +}; + +static struct hpf_work tx_hpf_work[NUM_DECIMATORS]; + +static const struct wcd9xxx_ch tomtom_rx_chs[TOMTOM_RX_MAX] = { + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER, 0), + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 1, 1), + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 2, 2), + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 3, 3), + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 4, 4), + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 5, 5), + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 6, 6), + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 7, 7), + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 8, 8), + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 9, 9), + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 10, 10), + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 11, 11), + WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 12, 12), +}; + +static const struct wcd9xxx_ch tomtom_tx_chs[TOMTOM_TX_MAX] = { + WCD9XXX_CH(0, 0), + WCD9XXX_CH(1, 1), + WCD9XXX_CH(2, 2), + WCD9XXX_CH(3, 3), + WCD9XXX_CH(4, 4), + WCD9XXX_CH(5, 5), + WCD9XXX_CH(6, 6), + WCD9XXX_CH(7, 7), + WCD9XXX_CH(8, 8), + WCD9XXX_CH(9, 9), + WCD9XXX_CH(10, 10), + WCD9XXX_CH(11, 11), + WCD9XXX_CH(12, 12), + WCD9XXX_CH(13, 13), + WCD9XXX_CH(14, 14), + WCD9XXX_CH(15, 15), +}; + +static const u32 vport_check_table[NUM_CODEC_DAIS] = { + 0, /* AIF1_PB */ + (1 << AIF2_CAP) | (1 << AIF3_CAP), /* AIF1_CAP */ + 0, /* AIF2_PB */ + (1 << AIF1_CAP) | (1 << AIF3_CAP), /* AIF2_CAP */ + 0, /* AIF3_PB */ + (1 << AIF1_CAP) | (1 << AIF2_CAP), /* AIF3_CAP */ +}; + +static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = { + 0, /* AIF1_PB */ + 0, /* AIF1_CAP */ + 0, /* AIF2_PB */ + 0, /* AIF2_CAP */ +}; + +/* + * Interrupt table for v3 corresponds to newer version + * codecs (wcd9330) + */ +static const struct intr_data wcd9330_intr_tbl[] = { + {WCD9XXX_IRQ_SLIMBUS, false}, + {WCD9XXX_IRQ_MBHC_INSERTION, true}, + {WCD9XXX_IRQ_MBHC_POTENTIAL, true}, + {WCD9XXX_IRQ_MBHC_RELEASE, true}, + {WCD9XXX_IRQ_MBHC_PRESS, true}, + {WCD9XXX_IRQ_MBHC_SHORT_TERM, true}, + {WCD9XXX_IRQ_MBHC_REMOVAL, true}, + {WCD9330_IRQ_MBHC_JACK_SWITCH, true}, + {WCD9XXX_IRQ_BG_PRECHARGE, false}, + {WCD9XXX_IRQ_PA1_STARTUP, false}, + {WCD9XXX_IRQ_PA2_STARTUP, false}, + {WCD9XXX_IRQ_PA3_STARTUP, false}, + {WCD9XXX_IRQ_PA4_STARTUP, false}, + {WCD9XXX_IRQ_PA5_STARTUP, false}, + {WCD9XXX_IRQ_MICBIAS1_PRECHARGE, false}, + {WCD9XXX_IRQ_MICBIAS2_PRECHARGE, false}, + {WCD9XXX_IRQ_MICBIAS3_PRECHARGE, false}, + {WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, false}, + {WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, false}, + {WCD9XXX_IRQ_EAR_PA_OCPL_FAULT, false}, + {WCD9XXX_IRQ_HPH_L_PA_STARTUP, false}, + {WCD9XXX_IRQ_HPH_R_PA_STARTUP, false}, + {WCD9320_IRQ_EAR_PA_STARTUP, false}, + {WCD9330_IRQ_SVASS_ERR_EXCEPTION, false}, + {WCD9330_IRQ_SVASS_ENGINE, true}, + {WCD9330_IRQ_MAD_AUDIO, false}, + {WCD9330_IRQ_MAD_BEACON, false}, + {WCD9330_IRQ_MAD_ULTRASOUND, false}, + {WCD9330_IRQ_SPEAKER1_CLIPPING, false}, + {WCD9330_IRQ_SPEAKER2_CLIPPING, false}, + {WCD9330_IRQ_VBAT_MONITOR_ATTACK, false}, + {WCD9330_IRQ_VBAT_MONITOR_RELEASE, false}, +}; + +struct tomtom_priv { + struct snd_soc_codec *codec; + u32 adc_count; + u32 rx_bias_count; + s32 dmic_1_2_clk_cnt; + s32 dmic_3_4_clk_cnt; + s32 dmic_5_6_clk_cnt; + s32 ldo_h_users; + s32 micb_2_users; + + u32 anc_slot; + bool anc_func; + + /* cal info for codec */ + struct fw_info *fw_data; + + /*track tomtom interface type*/ + u8 intf_type; + + /* num of slim ports required */ + struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS]; + + /*compander*/ + int comp_enabled[COMPANDER_MAX]; + u32 comp_fs[COMPANDER_MAX]; + + /* Maintain the status of AUX PGA */ + int aux_pga_cnt; + u8 aux_l_gain; + u8 aux_r_gain; + + bool spkr_pa_widget_on; + struct regulator *spkdrv_reg; + struct regulator *spkdrv2_reg; + + bool mbhc_started; + + struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg; + + /* resmgr module */ + struct wcd9xxx_resmgr resmgr; + /* mbhc module */ + struct wcd9xxx_mbhc mbhc; + + /* class h specific data */ + struct wcd9xxx_clsh_cdc_data clsh_d; + + int (*machine_codec_event_cb)(struct snd_soc_codec *codec, + enum wcd9xxx_codec_event); + int (*codec_ext_clk_en_cb)(struct snd_soc_codec *codec, + int enable, bool dapm); + int (*codec_get_ext_clk_cnt)(void); + /* + * list used to save/restore registers at start and + * end of impedance measurement + */ + struct list_head reg_save_restore; + + /* handle to cpe core */ + struct wcd_cpe_core *cpe_core; + + /* UHQA (class AB) mode */ + u8 uhqa_mode; + + /* Multiplication factor used for impedance detection */ + int zdet_gain_mul_fact; + + /* to track the status */ + unsigned long status_mask; + + int ext_clk_users; + struct clk *wcd_ext_clk; + + /* Port values for Rx and Tx codec_dai */ + unsigned int rx_port_value; + unsigned int tx_port_value; + + struct mutex codec_mutex; +}; + +static const u32 comp_shift[] = { + 4, /* Compander 0's clock source is on interpolator 7 */ + 0, + 2, +}; + +static const int comp_rx_path[] = { + COMPANDER_1, + COMPANDER_1, + COMPANDER_2, + COMPANDER_2, + COMPANDER_2, + COMPANDER_2, + COMPANDER_0, + COMPANDER_0, + COMPANDER_MAX, +}; + +static const struct comp_sample_dependent_params comp_samp_params[] = { + { + /* 8 Khz */ + .peak_det_timeout = 0x06, + .rms_meter_div_fact = 0x09, + .rms_meter_resamp_fact = 0x06, + }, + { + /* 16 Khz */ + .peak_det_timeout = 0x07, + .rms_meter_div_fact = 0x0A, + .rms_meter_resamp_fact = 0x0C, + }, + { + /* 32 Khz */ + .peak_det_timeout = 0x08, + .rms_meter_div_fact = 0x0B, + .rms_meter_resamp_fact = 0x1E, + }, + { + /* 48 Khz */ + .peak_det_timeout = 0x09, + .rms_meter_div_fact = 0x0B, + .rms_meter_resamp_fact = 0x28, + }, + { + /* 96 Khz */ + .peak_det_timeout = 0x0A, + .rms_meter_div_fact = 0x0C, + .rms_meter_resamp_fact = 0x50, + }, + { + /* 192 Khz */ + .peak_det_timeout = 0x0B, + .rms_meter_div_fact = 0xC, + .rms_meter_resamp_fact = 0xA0, + }, +}; + +static unsigned short rx_digital_gain_reg[] = { + TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL, + TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL, +}; + + +static unsigned short tx_digital_gain_reg[] = { + TOMTOM_A_CDC_TX1_VOL_CTL_GAIN, + TOMTOM_A_CDC_TX2_VOL_CTL_GAIN, + TOMTOM_A_CDC_TX3_VOL_CTL_GAIN, + TOMTOM_A_CDC_TX4_VOL_CTL_GAIN, + TOMTOM_A_CDC_TX5_VOL_CTL_GAIN, + TOMTOM_A_CDC_TX6_VOL_CTL_GAIN, + TOMTOM_A_CDC_TX7_VOL_CTL_GAIN, + TOMTOM_A_CDC_TX8_VOL_CTL_GAIN, + TOMTOM_A_CDC_TX9_VOL_CTL_GAIN, + TOMTOM_A_CDC_TX10_VOL_CTL_GAIN, +}; + +/* + * wcd9330_get_codec_info: Get codec specific information + * + * @wcd9xxx: pointer to wcd9xxx structure + * @wcd_type: pointer to wcd9xxx_codec_type structure + * + * Returns 0 for success or negative error code for failure + */ +int wcd9330_get_codec_info(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_codec_type *wcd_type) +{ + u16 id_minor, id_major; + struct regmap *wcd_regmap; + int rc, val, version = 0; + + if (!wcd9xxx || !wcd_type) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n", + __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + rc = regmap_bulk_read(wcd_regmap, TOMTOM_A_CHIP_ID_BYTE_0, + (u8 *)&id_minor, sizeof(u16)); + if (rc) + return -EINVAL; + + rc = regmap_bulk_read(wcd_regmap, TOMTOM_A_CHIP_ID_BYTE_2, + (u8 *)&id_major, sizeof(u16)); + if (rc) + return -EINVAL; + + dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n", + __func__, id_major, id_minor); + + if (id_minor == cpu_to_le16(0x1)) + version = 2; + else if (id_minor == cpu_to_le16(0x0)) + version = 1; + else + dev_err(wcd9xxx->dev, "%s: wcd9330 version unknown (major 0x%x, minor 0x%x)\n", + __func__, id_major, id_minor); + + /* Fill codec type info */ + wcd_type->id_major = id_major; + wcd_type->id_minor = id_minor; + wcd_type->num_irqs = WCD9330_NUM_IRQS; + wcd_type->version = version; + wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1; + wcd_type->i2c_chip_status = 0x01; + wcd_type->intr_tbl = wcd9330_intr_tbl; + wcd_type->intr_tbl_size = ARRAY_SIZE(wcd9330_intr_tbl); + + wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] = + TOMTOM_A_INTR1_STATUS0; + wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] = + TOMTOM_A_INTR1_CLEAR0; + wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] = + TOMTOM_A_INTR1_MASK0; + wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] = + TOMTOM_A_INTR1_LEVEL0; + wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] = + TOMTOM_A_INTR_MODE; + + return rc; +} +EXPORT_SYMBOL(wcd9330_get_codec_info); + +/* + * wcd9330_bringdown: Bringdown WCD Codec + * + * @wcd9xxx: Pointer to wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +int wcd9330_bringdown(struct wcd9xxx *wcd9xxx) +{ + if (!wcd9xxx || !wcd9xxx->regmap) + return -EINVAL; + + regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x7); + regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x6); + regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0xe); + regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x8); + + return 0; +} +EXPORT_SYMBOL(wcd9330_bringdown); + +/* + * wcd9330_bringup: Bring up WCD Codec + * + * @wcd9xxx: Pointer to wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +int wcd9330_bringup(struct wcd9xxx *wcd9xxx) +{ + if (!wcd9xxx || !wcd9xxx->regmap) + return -EINVAL; + + regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x4); + regmap_write(wcd9xxx->regmap, TOMTOM_A_CDC_CTL, 0x0); + /* wait for 5ms after codec reset for it to complete */ + usleep_range(5000, 5100); + regmap_write(wcd9xxx->regmap, TOMTOM_A_CDC_CTL, 0x1); + regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x3); + regmap_write(wcd9xxx->regmap, TOMTOM_A_CDC_CTL, 0x3); + + return 0; +} +EXPORT_SYMBOL(wcd9330_bringup); + +int tomtom_enable_qfuse_sensing(struct snd_soc_codec *codec) +{ + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + if (tomtom->wcd_ext_clk) + tomtom_codec_mclk_enable(codec, true, false); + + snd_soc_write(codec, TOMTOM_A_QFUSE_CTL, 0x03); + /* + * 5ms sleep required after enabling qfuse control + * before checking the status. + */ + usleep_range(5000, 5500); + if ((snd_soc_read(codec, TOMTOM_A_QFUSE_STATUS) & (0x03)) != 0x03) + WARN(1, "%s: Qfuse sense is not complete\n", __func__); + + if (tomtom->wcd_ext_clk) + tomtom_codec_mclk_enable(codec, false, false); + return 0; +} +EXPORT_SYMBOL(tomtom_enable_qfuse_sensing); + +static int tomtom_get_sample_rate(struct snd_soc_codec *codec, int path) +{ + if (path == RX8_PATH) + return snd_soc_read(codec, TOMTOM_A_CDC_RX8_B5_CTL); + else + return snd_soc_read(codec, + (TOMTOM_A_CDC_RX1_B5_CTL + 8 * (path - 1))); +} + +static int tomtom_compare_bit_format(struct snd_soc_codec *codec, + int bit_format) +{ + int i = 0; + int ret = 0; + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + + for (i = 0; i < NUM_CODEC_DAIS; i++) { + if (tomtom_p->dai[i].bit_width == bit_format) { + ret = 1; + break; + } + } + return ret; +} + +static int tomtom_update_uhqa_mode(struct snd_soc_codec *codec, int path) +{ + int ret = 0; + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + + /* UHQA path has fs=192KHz & bit=24 bit */ + if (((tomtom_get_sample_rate(codec, path) & 0xE0) == 0xA0) && + (tomtom_compare_bit_format(codec, 24))) { + tomtom_p->uhqa_mode = 1; + } else { + tomtom_p->uhqa_mode = 0; + } + dev_dbg(codec->dev, "%s: uhqa_mode=%d", __func__, tomtom_p->uhqa_mode); + return ret; +} + +static int tomtom_get_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tomtom->anc_slot; + return 0; +} + +static int tomtom_put_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + tomtom->anc_slot = ucontrol->value.integer.value[0]; + return 0; +} + +static int tomtom_get_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = (tomtom->anc_func == true ? 1 : 0); + return 0; +} + +static int tomtom_put_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = + snd_soc_codec_get_dapm(codec); + + mutex_lock(&tomtom->codec_mutex); + tomtom->anc_func = (!ucontrol->value.integer.value[0] ? false : true); + + dev_dbg(codec->dev, "%s: anc_func %x", __func__, tomtom->anc_func); + + if (tomtom->anc_func == true) { + snd_soc_dapm_enable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_enable_pin(dapm, "ANC HEADPHONE"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "HPHR"); + snd_soc_dapm_disable_pin(dapm, "HPHL"); + snd_soc_dapm_disable_pin(dapm, "HEADPHONE"); + snd_soc_dapm_disable_pin(dapm, "EAR PA"); + snd_soc_dapm_disable_pin(dapm, "EAR"); + } else { + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HEADPHONE"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "HPHR"); + snd_soc_dapm_enable_pin(dapm, "HPHL"); + snd_soc_dapm_enable_pin(dapm, "HEADPHONE"); + snd_soc_dapm_enable_pin(dapm, "EAR PA"); + snd_soc_dapm_enable_pin(dapm, "EAR"); + } + mutex_unlock(&tomtom->codec_mutex); + snd_soc_dapm_sync(dapm); + return 0; +} + +static int tomtom_get_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, (TOMTOM_A_CDC_IIR1_CTL + 16 * iir_idx)) & + (1 << band_idx)) != 0; + + pr_debug("%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int tomtom_put_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, (TOMTOM_A_CDC_IIR1_CTL + 16 * iir_idx), + (1 << band_idx), (value << band_idx)); + + pr_debug("%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + ((snd_soc_read(codec, (TOMTOM_A_CDC_IIR1_CTL + 16 * iir_idx)) & + (1 << band_idx)) != 0)); + return 0; +} +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)); + + snd_soc_write(codec, + (TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) << 8); + + snd_soc_write(codec, + (TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) << 16); + + snd_soc_write(codec, + (TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, + (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) & 0x3F) << 24); + + return value; +} + +static int tomtom_get_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx), + (value >> 24) & 0x3F); +} + +static int tomtom_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + /* Mask top bit it is reserved */ + /* Updates addr automatically for each B2 write */ + snd_soc_write(codec, + (TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[0]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[1]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[2]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[3]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[4]); + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static int tomtom_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tomtom->comp_enabled[comp]; + return 0; +} + +static int tomtom_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + pr_debug("%s: Compander %d enable current %d, new %d\n", + __func__, comp, tomtom->comp_enabled[comp], value); + tomtom->comp_enabled[comp] = value; + + if (comp == COMPANDER_1 && + tomtom->comp_enabled[comp] == 1) { + /* Wavegen to 5 msec */ + snd_soc_write(codec, TOMTOM_A_RX_HPH_CNP_WG_CTL, 0xDB); + snd_soc_write(codec, TOMTOM_A_RX_HPH_CNP_WG_TIME, 0x2A); + snd_soc_write(codec, TOMTOM_A_RX_HPH_BIAS_WG_OCP, 0x2A); + + /* Enable Chopper */ + snd_soc_update_bits(codec, + TOMTOM_A_RX_HPH_CHOP_CTL, 0x80, 0x80); + + snd_soc_write(codec, TOMTOM_A_NCP_DTEST, 0x20); + pr_debug("%s: Enabled Chopper and set wavegen to 5 msec\n", + __func__); + } else if (comp == COMPANDER_1 && + tomtom->comp_enabled[comp] == 0) { + /* Wavegen to 20 msec */ + snd_soc_write(codec, TOMTOM_A_RX_HPH_CNP_WG_CTL, 0xDB); + snd_soc_write(codec, TOMTOM_A_RX_HPH_CNP_WG_TIME, 0x58); + snd_soc_write(codec, TOMTOM_A_RX_HPH_BIAS_WG_OCP, 0x1A); + + /* Disable CHOPPER block */ + snd_soc_update_bits(codec, + TOMTOM_A_RX_HPH_CHOP_CTL, 0x80, 0x00); + + snd_soc_write(codec, TOMTOM_A_NCP_DTEST, 0x10); + pr_debug("%s: Disabled Chopper and set wavegen to 20 msec\n", + __func__); + } + return 0; +} + +static int tomtom_config_gain_compander(struct snd_soc_codec *codec, + int comp, bool enable) +{ + int ret = 0; + + switch (comp) { + case COMPANDER_0: + snd_soc_update_bits(codec, TOMTOM_A_SPKR_DRV1_GAIN, + 1 << 2, !enable << 2); + snd_soc_update_bits(codec, TOMTOM_A_SPKR_DRV2_GAIN, + 1 << 2, !enable << 2); + break; + case COMPANDER_1: + snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_L_GAIN, + 1 << 5, !enable << 5); + snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_R_GAIN, + 1 << 5, !enable << 5); + break; + case COMPANDER_2: + snd_soc_update_bits(codec, TOMTOM_A_RX_LINE_1_GAIN, + 1 << 5, !enable << 5); + snd_soc_update_bits(codec, TOMTOM_A_RX_LINE_3_GAIN, + 1 << 5, !enable << 5); + snd_soc_update_bits(codec, TOMTOM_A_RX_LINE_2_GAIN, + 1 << 5, !enable << 5); + snd_soc_update_bits(codec, TOMTOM_A_RX_LINE_4_GAIN, + 1 << 5, !enable << 5); + break; + default: + WARN_ON(1); + ret = -EINVAL; + } + + return ret; +} + +static void tomtom_discharge_comp(struct snd_soc_codec *codec, int comp) +{ + /* Level meter DIV Factor to 5*/ + snd_soc_update_bits(codec, TOMTOM_A_CDC_COMP0_B2_CTL + (comp * 8), 0xF0, + 0x05 << 4); + /* RMS meter Sampling to 0x01 */ + snd_soc_write(codec, TOMTOM_A_CDC_COMP0_B3_CTL + (comp * 8), 0x01); + + /* Worst case timeout for compander CnP sleep timeout */ + usleep_range(3000, 3100); +} + +static enum wcd9xxx_buck_volt tomtom_codec_get_buck_mv( + struct snd_soc_codec *codec) +{ + int buck_volt = WCD9XXX_CDC_BUCK_UNSUPPORTED; + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = tomtom->resmgr.pdata; + int i; + + for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) { + if (!strcmp(pdata->regulator[i].name, + WCD9XXX_SUPPLY_BUCK_NAME)) { + if ((pdata->regulator[i].min_uV == + WCD9XXX_CDC_BUCK_MV_1P8) || + (pdata->regulator[i].min_uV == + WCD9XXX_CDC_BUCK_MV_2P15)) + buck_volt = pdata->regulator[i].min_uV; + break; + } + } + return buck_volt; +} + +static int tomtom_config_compander(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int mask, enable_mask; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + const int comp = w->shift; + const u32 rate = tomtom->comp_fs[comp]; + const struct comp_sample_dependent_params *comp_params = + &comp_samp_params[rate]; + enum wcd9xxx_buck_volt buck_mv; + + pr_debug("%s: %s event %d compander %d, enabled %d", __func__, + w->name, event, comp, tomtom->comp_enabled[comp]); + + if (!tomtom->comp_enabled[comp]) + return 0; + + /* Compander 0 has two channels */ + mask = enable_mask = 0x03; + buck_mv = tomtom_codec_get_buck_mv(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Set compander Sample rate */ + snd_soc_update_bits(codec, + TOMTOM_A_CDC_COMP0_FS_CFG + (comp * 8), + 0x07, rate); + /* Set the static gain offset for HPH Path */ + if (comp == COMPANDER_1) { + if (buck_mv == WCD9XXX_CDC_BUCK_MV_2P15) { + snd_soc_update_bits(codec, + TOMTOM_A_CDC_COMP0_B4_CTL + (comp * 8), + 0x80, 0x00); + } else { + snd_soc_update_bits(codec, + TOMTOM_A_CDC_COMP0_B4_CTL + (comp * 8), + 0x80, 0x80); + } + } + /* Enable RX interpolation path compander clocks */ + snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_B2_CTL, + mask << comp_shift[comp], + mask << comp_shift[comp]); + /* Toggle compander reset bits */ + snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL, + mask << comp_shift[comp], + mask << comp_shift[comp]); + snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL, + mask << comp_shift[comp], 0); + + /* Set gain source to compander */ + tomtom_config_gain_compander(codec, comp, true); + + /* Compander enable */ + snd_soc_update_bits(codec, TOMTOM_A_CDC_COMP0_B1_CTL + + (comp * 8), enable_mask, enable_mask); + + tomtom_discharge_comp(codec, comp); + + /* Set sample rate dependent parameter */ + snd_soc_write(codec, TOMTOM_A_CDC_COMP0_B3_CTL + (comp * 8), + comp_params->rms_meter_resamp_fact); + snd_soc_update_bits(codec, + TOMTOM_A_CDC_COMP0_B2_CTL + (comp * 8), + 0xF0, comp_params->rms_meter_div_fact << 4); + snd_soc_update_bits(codec, + TOMTOM_A_CDC_COMP0_B2_CTL + (comp * 8), + 0x0F, comp_params->peak_det_timeout); + break; + case SND_SOC_DAPM_PRE_PMD: + /* Disable compander */ + snd_soc_update_bits(codec, + TOMTOM_A_CDC_COMP0_B1_CTL + (comp * 8), + enable_mask, 0x00); + + /* Toggle compander reset bits */ + snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL, + mask << comp_shift[comp], + mask << comp_shift[comp]); + snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL, + mask << comp_shift[comp], 0); + + /* Turn off the clock for compander in pair */ + snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_B2_CTL, + mask << comp_shift[comp], 0); + + /* Set gain source to register */ + tomtom_config_gain_compander(codec, comp, false); + break; + } + return 0; +} + + + +static const char *const tomtom_anc_func_text[] = {"OFF", "ON"}; +static const struct soc_enum tomtom_anc_func_enum = + SOC_ENUM_SINGLE_EXT(2, tomtom_anc_func_text); + +static const char *const tabla_ear_pa_gain_text[] = {"POS_6_DB", "POS_2_DB"}; +static const struct soc_enum tabla_ear_pa_gain_enum[] = { + SOC_ENUM_SINGLE_EXT(2, tabla_ear_pa_gain_text), +}; + +/*cut of frequency for high pass filter*/ +static const char * const cf_text[] = { + "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz" +}; + +static const char * const rx_cf_text[] = { + "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz", + "MIN_3DB_0P48Hz" +}; + +static const struct soc_enum cf_dec1_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX1_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec2_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX2_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec3_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX3_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec4_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX4_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec5_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX5_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec6_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX6_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec7_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX7_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec8_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX8_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec9_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX9_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec10_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX10_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_rxmix1_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX1_B4_CTL, 0, 4, rx_cf_text); + +static const struct soc_enum cf_rxmix2_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX2_B4_CTL, 0, 4, rx_cf_text); + +static const struct soc_enum cf_rxmix3_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX3_B4_CTL, 0, 4, rx_cf_text); + +static const struct soc_enum cf_rxmix4_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX4_B4_CTL, 0, 4, rx_cf_text); + +static const struct soc_enum cf_rxmix5_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX5_B4_CTL, 0, 4, rx_cf_text) +; +static const struct soc_enum cf_rxmix6_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX6_B4_CTL, 0, 4, rx_cf_text); + +static const struct soc_enum cf_rxmix7_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX7_B4_CTL, 0, 4, rx_cf_text); + +static const struct soc_enum cf_rxmix8_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX8_B4_CTL, 0, 4, rx_cf_text); + +static const char * const class_h_dsm_text[] = { + "ZERO", "DSM_HPHL_RX1", "DSM_SPKR_RX7" +}; + +static const struct soc_enum class_h_dsm_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_CLSH_CTL, 4, 3, class_h_dsm_text); + +static const struct snd_kcontrol_new class_h_dsm_mux = + SOC_DAPM_ENUM("CLASS_H_DSM MUX Mux", class_h_dsm_enum); + +static const char * const rx1_interp_text[] = { + "ZERO", "RX1 MIX2" +}; + +static const struct soc_enum rx1_interp_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CLK_RX_B1_CTL, 0, 2, rx1_interp_text); + +static const struct snd_kcontrol_new rx1_interp_mux = + SOC_DAPM_ENUM("RX1 INTERP MUX Mux", rx1_interp_enum); + +static const char * const rx2_interp_text[] = { + "ZERO", "RX2 MIX2" +}; + +static const struct soc_enum rx2_interp_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CLK_RX_B1_CTL, 1, 2, rx2_interp_text); + +static const struct snd_kcontrol_new rx2_interp_mux = + SOC_DAPM_ENUM("RX2 INTERP MUX Mux", rx2_interp_enum); + +static const char *const tomtom_conn_mad_text[] = { + "ADC_MB", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6", "NOTUSED1", + "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", "DMIC6", "NOTUSED2", + "NOTUSED3"}; + +static const struct soc_enum tomtom_conn_mad_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tomtom_conn_mad_text), + tomtom_conn_mad_text); + + +static int tomtom_mad_input_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 tomtom_mad_input; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + tomtom_mad_input = snd_soc_read(codec, TOMTOM_A_CDC_MAD_INP_SEL); + + tomtom_mad_input = tomtom_mad_input & 0x0F; + + ucontrol->value.integer.value[0] = tomtom_mad_input; + + pr_debug("%s: tomtom_mad_input = %s\n", __func__, + tomtom_conn_mad_text[tomtom_mad_input]); + + return 0; +} + +static int tomtom_mad_input_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 tomtom_mad_input; + u16 micb_int_reg, micb_4_int_reg; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + char mad_amic_input_widget[6]; + u32 adc; + const char *mad_input_widget; + const char *source_widget = NULL; + u32 mic_bias_found = 0; + u32 i; + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + int ret = 0; + char *mad_input; + + tomtom_mad_input = ucontrol->value.integer.value[0]; + micb_4_int_reg = tomtom->resmgr.reg_addr->micb_4_int_rbias; + + pr_debug("%s: tomtom_mad_input = %s\n", __func__, + tomtom_conn_mad_text[tomtom_mad_input]); + + if (!strcmp(tomtom_conn_mad_text[tomtom_mad_input], "NOTUSED1") || + !strcmp(tomtom_conn_mad_text[tomtom_mad_input], "NOTUSED2") || + !strcmp(tomtom_conn_mad_text[tomtom_mad_input], "NOTUSED3") || + !strcmp(tomtom_conn_mad_text[tomtom_mad_input], "ADC_MB")) { + pr_info("%s: tomtom mad input is set to unsupported input = %s\n", + __func__, tomtom_conn_mad_text[tomtom_mad_input]); + return -EINVAL; + } + + if (strnstr(tomtom_conn_mad_text[tomtom_mad_input], + "ADC", sizeof("ADC"))) { + mad_input = strpbrk(tomtom_conn_mad_text[tomtom_mad_input], + "123456"); + if (!mad_input) { + dev_err(codec->dev, "%s: Invalid MAD input %s\n", + __func__, tomtom_conn_mad_text[tomtom_mad_input]); + return -EINVAL; + } + ret = kstrtouint(mad_input, 10, &adc); + if ((ret < 0) || (adc > 6)) { + pr_err("%s: Invalid ADC = %s\n", __func__, + tomtom_conn_mad_text[tomtom_mad_input]); + ret = -EINVAL; + } + + snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc); + + mad_input_widget = mad_amic_input_widget; + pr_debug("%s: tomtom amic input widget = %s\n", __func__, + mad_amic_input_widget); + } else { + /* DMIC type input widget*/ + mad_input_widget = tomtom_conn_mad_text[tomtom_mad_input]; + } + + pr_debug("%s: tomtom input widget = %s\n", __func__, mad_input_widget); + + for (i = 0; i < card->num_dapm_routes; i++) { + + if (!strcmp(card->dapm_routes[i].sink, mad_input_widget)) { + + source_widget = card->dapm_routes[i].source; + if (!source_widget) { + dev_err(codec->dev, + "%s: invalid source widget\n", + __func__); + return -EINVAL; + } + + if (strnstr(source_widget, + "MIC BIAS1", sizeof("MIC BIAS1"))) { + mic_bias_found = 1; + micb_int_reg = TOMTOM_A_MICB_1_INT_RBIAS; + break; + } else if (strnstr(source_widget, + "MIC BIAS2", sizeof("MIC BIAS2"))) { + mic_bias_found = 2; + micb_int_reg = TOMTOM_A_MICB_2_INT_RBIAS; + break; + } else if (strnstr(source_widget, + "MIC BIAS3", sizeof("MIC BIAS3"))) { + mic_bias_found = 3; + micb_int_reg = TOMTOM_A_MICB_3_INT_RBIAS; + break; + } else if (strnstr(source_widget, + "MIC BIAS4", sizeof("MIC BIAS4"))) { + mic_bias_found = 4; + micb_int_reg = micb_4_int_reg; + break; + } + } + } + + if (mic_bias_found) { + pr_debug("%s: source mic bias = %s. sink = %s\n", __func__, + card->dapm_routes[i].source, + card->dapm_routes[i].sink); + + snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_INP_SEL, + 0x0F, tomtom_mad_input); + snd_soc_update_bits(codec, TOMTOM_A_MAD_ANA_CTRL, + 0x07, mic_bias_found); + + /* Setup internal micbias */ + + if (strnstr(source_widget, "Internal1", strlen(source_widget))) + snd_soc_update_bits(codec, + micb_int_reg, + 0xE0, 0xE0); + else if (strnstr(source_widget, "Internal2", + strlen(source_widget))) + snd_soc_update_bits(codec, + micb_int_reg, + 0x1C, 0x1C); + else if (strnstr(source_widget, "Internal3", + strlen(source_widget))) + snd_soc_update_bits(codec, + micb_int_reg, + 0x3, 0x3); + else + /* + * If not internal, make sure to write the + * register to default value + */ + snd_soc_write(codec, micb_int_reg, 0x24); + return 0; + } + + pr_err("%s: mic bias source not found for input = %s\n", + __func__, mad_input_widget); + return -EINVAL; +} + +static int tomtom_tx_hpf_bypass_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 tx_index; + + tx_index = (u32)kcontrol->private_value; + + if (tx_index > NUM_DECIMATORS) { + pr_err("%s: Invalid TX decimator %d\n", __func__, + tx_index); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = + tx_hpf_work[tx_index-1].tx_hpf_bypass; + + return 0; +} + +static int tomtom_tx_hpf_bypass_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + bool tx_hpf_bypass_cfg; + u32 tx_index; + + tx_hpf_bypass_cfg = (bool)ucontrol->value.integer.value[0]; + + pr_debug("%s: tx_hpf_bypass = %d\n", __func__, + tx_hpf_bypass_cfg); + + tx_index = (u32)kcontrol->private_value; + + if (tx_index > NUM_DECIMATORS) { + pr_err("%s: Invalid TX decimator %d\n", __func__, + tx_index); + return -EINVAL; + } + if (tx_hpf_work[tx_index-1].tx_hpf_bypass != tx_hpf_bypass_cfg) + tx_hpf_work[tx_index-1].tx_hpf_bypass = tx_hpf_bypass_cfg; + + pr_debug("%s: Set TX%d HPF bypass configuration %d", + __func__, tx_index, + tx_hpf_work[tx_index-1].tx_hpf_bypass); + + return 0; +} + +static const struct snd_kcontrol_new tomtom_snd_controls[] = { + + SOC_SINGLE_SX_TLV("RX1 Digital Volume", TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Digital Volume", TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Digital Volume", TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX4 Digital Volume", TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX5 Digital Volume", TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX6 Digital Volume", TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX7 Digital Volume", TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Digital Volume", TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("DEC1 Volume", TOMTOM_A_CDC_TX1_VOL_CTL_GAIN, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC2 Volume", TOMTOM_A_CDC_TX2_VOL_CTL_GAIN, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC3 Volume", TOMTOM_A_CDC_TX3_VOL_CTL_GAIN, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC4 Volume", TOMTOM_A_CDC_TX4_VOL_CTL_GAIN, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC5 Volume", TOMTOM_A_CDC_TX5_VOL_CTL_GAIN, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC6 Volume", TOMTOM_A_CDC_TX6_VOL_CTL_GAIN, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC7 Volume", TOMTOM_A_CDC_TX7_VOL_CTL_GAIN, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC8 Volume", TOMTOM_A_CDC_TX8_VOL_CTL_GAIN, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC9 Volume", TOMTOM_A_CDC_TX9_VOL_CTL_GAIN, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC10 Volume", TOMTOM_A_CDC_TX10_VOL_CTL_GAIN, 0, + -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", TOMTOM_A_CDC_IIR1_GAIN_B1_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", TOMTOM_A_CDC_IIR1_GAIN_B2_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", TOMTOM_A_CDC_IIR1_GAIN_B3_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP4 Volume", TOMTOM_A_CDC_IIR1_GAIN_B4_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR2 INP1 Volume", TOMTOM_A_CDC_IIR2_GAIN_B1_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR2 INP2 Volume", TOMTOM_A_CDC_IIR2_GAIN_B2_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR2 INP3 Volume", TOMTOM_A_CDC_IIR2_GAIN_B3_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR2 INP4 Volume", TOMTOM_A_CDC_IIR2_GAIN_B4_CTL, 0, + -84, 40, digital_gain), + + SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tomtom_get_anc_slot, + tomtom_put_anc_slot), + SOC_ENUM_EXT("ANC Function", tomtom_anc_func_enum, tomtom_get_anc_func, + tomtom_put_anc_func), + + 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("TX9 HPF cut off", cf_dec9_enum), + SOC_ENUM("TX10 HPF cut off", cf_dec10_enum), + + SOC_SINGLE_BOOL_EXT("TX1 HPF Switch", 1, + tomtom_tx_hpf_bypass_get, + tomtom_tx_hpf_bypass_put), + SOC_SINGLE_BOOL_EXT("TX2 HPF Switch", 2, + tomtom_tx_hpf_bypass_get, + tomtom_tx_hpf_bypass_put), + SOC_SINGLE_BOOL_EXT("TX3 HPF Switch", 3, + tomtom_tx_hpf_bypass_get, + tomtom_tx_hpf_bypass_put), + SOC_SINGLE_BOOL_EXT("TX4 HPF Switch", 4, + tomtom_tx_hpf_bypass_get, + tomtom_tx_hpf_bypass_put), + SOC_SINGLE_BOOL_EXT("TX5 HPF Switch", 5, + tomtom_tx_hpf_bypass_get, + tomtom_tx_hpf_bypass_put), + SOC_SINGLE_BOOL_EXT("TX6 HPF Switch", 6, + tomtom_tx_hpf_bypass_get, + tomtom_tx_hpf_bypass_put), + SOC_SINGLE_BOOL_EXT("TX7 HPF Switch", 7, + tomtom_tx_hpf_bypass_get, + tomtom_tx_hpf_bypass_put), + SOC_SINGLE_BOOL_EXT("TX8 HPF Switch", 8, + tomtom_tx_hpf_bypass_get, + tomtom_tx_hpf_bypass_put), + SOC_SINGLE_BOOL_EXT("TX9 HPF Switch", 9, + tomtom_tx_hpf_bypass_get, + tomtom_tx_hpf_bypass_put), + SOC_SINGLE_BOOL_EXT("TX10 HPF Switch", 10, + tomtom_tx_hpf_bypass_get, + tomtom_tx_hpf_bypass_put), + + SOC_SINGLE("RX1 HPF Switch", TOMTOM_A_CDC_RX1_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX2 HPF Switch", TOMTOM_A_CDC_RX2_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX3 HPF Switch", TOMTOM_A_CDC_RX3_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX4 HPF Switch", TOMTOM_A_CDC_RX4_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX5 HPF Switch", TOMTOM_A_CDC_RX5_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX6 HPF Switch", TOMTOM_A_CDC_RX6_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX7 HPF Switch", TOMTOM_A_CDC_RX7_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX8 HPF Switch", TOMTOM_A_CDC_RX8_B5_CTL, 2, 1, 0), + + SOC_ENUM("RX1 HPF cut off", cf_rxmix1_enum), + SOC_ENUM("RX2 HPF cut off", cf_rxmix2_enum), + SOC_ENUM("RX3 HPF cut off", cf_rxmix3_enum), + SOC_ENUM("RX4 HPF cut off", cf_rxmix4_enum), + SOC_ENUM("RX5 HPF cut off", cf_rxmix5_enum), + SOC_ENUM("RX6 HPF cut off", cf_rxmix6_enum), + SOC_ENUM("RX7 HPF cut off", cf_rxmix7_enum), + SOC_ENUM("RX8 HPF cut off", cf_rxmix8_enum), + + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band1", IIR2, BAND1, 1, 0, + tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band2", IIR2, BAND2, 1, 0, + tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band3", IIR2, BAND3, 1, 0, + tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band4", IIR2, BAND4, 1, 0, + tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band5", IIR2, BAND5, 1, 0, + tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer), + + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band1", IIR2, BAND1, 255, 0, 5, + tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band2", IIR2, BAND2, 255, 0, 5, + tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band3", IIR2, BAND3, 255, 0, 5, + tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band4", IIR2, BAND4, 255, 0, 5, + tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5, + tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer), + + SOC_SINGLE_EXT("COMP0 Switch", SND_SOC_NOPM, COMPANDER_0, 1, 0, + tomtom_get_compander, tomtom_set_compander), + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + tomtom_get_compander, tomtom_set_compander), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + tomtom_get_compander, tomtom_set_compander), + + SOC_ENUM_EXT("MAD Input", tomtom_conn_mad_enum, + tomtom_mad_input_get, tomtom_mad_input_put), + +}; + +static int tomtom_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ear_pa_gain = snd_soc_read(codec, TOMTOM_A_RX_EAR_GAIN); + + ear_pa_gain = ear_pa_gain >> 5; + + ucontrol->value.integer.value[0] = ear_pa_gain; + + pr_debug("%s: ear_pa_gain = 0x%x\n", __func__, ear_pa_gain); + + return 0; +} + +static int tomtom_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + pr_debug("%s: ucontrol->value.integer.value[0] = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + ear_pa_gain = ucontrol->value.integer.value[0] << 5; + + snd_soc_update_bits(codec, TOMTOM_A_RX_EAR_GAIN, 0xE0, ear_pa_gain); + return 0; +} + +static const char * const tomtom_1_x_ear_pa_gain_text[] = { + "POS_6_DB", "POS_4P5_DB", "POS_3_DB", "POS_1P5_DB", + "POS_0_DB", "NEG_2P5_DB", "UNDEFINED", "NEG_12_DB" +}; + +static const struct soc_enum tomtom_1_x_ear_pa_gain_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tomtom_1_x_ear_pa_gain_text), + tomtom_1_x_ear_pa_gain_text); + +static const struct snd_kcontrol_new tomtom_1_x_analog_gain_controls[] = { + + SOC_ENUM_EXT("EAR PA Gain", tomtom_1_x_ear_pa_gain_enum, + tomtom_pa_gain_get, tomtom_pa_gain_put), + + SOC_SINGLE_TLV("HPHL Volume", TOMTOM_A_RX_HPH_L_GAIN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("HPHR Volume", TOMTOM_A_RX_HPH_R_GAIN, 0, 20, 1, + line_gain), + + SOC_SINGLE_TLV("LINEOUT1 Volume", TOMTOM_A_RX_LINE_1_GAIN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("LINEOUT2 Volume", TOMTOM_A_RX_LINE_2_GAIN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("LINEOUT3 Volume", TOMTOM_A_RX_LINE_3_GAIN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("LINEOUT4 Volume", TOMTOM_A_RX_LINE_4_GAIN, 0, 20, 1, + line_gain), + + SOC_SINGLE_TLV("SPK DRV Volume", TOMTOM_A_SPKR_DRV1_GAIN, 3, 8, 1, + line_gain), + SOC_SINGLE_TLV("SPK DRV2 Volume", TOMTOM_A_SPKR_DRV2_GAIN, 3, 8, 1, + line_gain), + + SOC_SINGLE_TLV("ADC1 Volume", TOMTOM_A_TX_1_GAIN, 2, 19, 0, + analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", TOMTOM_A_TX_2_GAIN, 2, 19, 0, + analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", TOMTOM_A_TX_3_GAIN, 2, 19, 0, + analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", TOMTOM_A_TX_4_GAIN, 2, 19, 0, + analog_gain), + SOC_SINGLE_TLV("ADC5 Volume", TOMTOM_A_TX_5_GAIN, 2, 19, 0, + analog_gain), + SOC_SINGLE_TLV("ADC6 Volume", TOMTOM_A_TX_6_GAIN, 2, 19, 0, + analog_gain), +}; + +static int tomtom_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl, zr; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec); + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + + hphr = mc->shift; + wcd9xxx_mbhc_get_impedance(&priv->mbhc, &zl, &zr); + pr_debug("%s: zl %u, zr %u\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + tomtom_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + tomtom_hph_impedance_get, NULL), +}; + +static int tomtom_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_mbhc *mbhc; + + if (!priv) { + pr_debug("%s: wcd9330 private data is NULL\n", __func__); + return 0; + } + + mbhc = &priv->mbhc; + if (!mbhc) { + pr_debug("%s: mbhc not initialized\n", __func__); + return 0; + } + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + pr_debug("%s: hph_type = %u\n", __func__, mbhc->hph_type); + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + tomtom_get_hph_type, NULL), +}; + +static const char * const rx_mix1_text[] = { + "ZERO", "SRC1", "SRC2", "IIR1", "IIR2", "RX1", "RX2", "RX3", "RX4", + "RX5", "RX6", "RX7" +}; + +static const char * const rx8_mix1_text[] = { + "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3", "RX4", + "RX5", "RX6", "RX7", "RX8" +}; + +static const char * const rx_mix2_text[] = { + "ZERO", "SRC1", "SRC2", "IIR1", "IIR2" +}; + +static const char * const rx_rdac5_text[] = { + "DEM4", "DEM3_INV" +}; + +static const char * const rx_rdac7_text[] = { + "DEM6", "DEM5_INV" +}; + +static const char * const mad_sel_text[] = { + "SPE", "MSM" +}; + +static const char * const sb_tx1_mux_text[] = { + "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7", + "DEC1", "RMIX8" +}; + +static const char * const sb_tx2_mux_text[] = { + "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7", + "DEC2", "RMIX8" +}; + +static const char * const sb_tx3_mux_text[] = { + "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7", + "DEC3", "RMIX8" +}; + +static const char * const sb_tx4_mux_text[] = { + "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7", + "DEC4", "RMIX8" +}; + +static const char * const sb_tx5_mux_text[] = { + "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7", + "DEC5", "RMIX8" +}; + +static const char * const sb_tx6_mux_text[] = { + "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7", + "DEC6", "RMIX8" +}; + +static const char * const sb_tx7_to_tx10_mux_text[] = { + "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7", + "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8", + "DEC9", "DEC10" +}; + +static const char * const dec1_mux_text[] = { + "ZERO", "DMIC1", "ADC6", +}; + +static const char * const dec2_mux_text[] = { + "ZERO", "DMIC2", "ADC5", +}; + +static const char * const dec3_mux_text[] = { + "ZERO", "DMIC3", "ADC4", +}; + +static const char * const dec4_mux_text[] = { + "ZERO", "DMIC4", "ADC3", +}; + +static const char * const dec5_mux_text[] = { + "ZERO", "DMIC5", "ADC2", +}; + +static const char * const dec6_mux_text[] = { + "ZERO", "DMIC6", "ADC1", +}; + +static const char * const dec7_mux_text[] = { + "ZERO", "DMIC1", "DMIC6", "ADC1", "ADC6", "ANC1_FB", "ANC2_FB", +}; + +static const char * const dec8_mux_text[] = { + "ZERO", "DMIC2", "DMIC5", "ADC2", "ADC5", "ANC1_FB", "ANC2_FB", +}; + +static const char * const dec9_mux_text[] = { + "ZERO", "DMIC4", "DMIC5", "ADC2", "ADC3", "ADCMB", "ANC1_FB", "ANC2_FB", +}; + +static const char * const dec10_mux_text[] = { + "ZERO", "DMIC3", "DMIC6", "ADC1", "ADC4", "ADCMB", "ANC1_FB", "ANC2_FB", +}; + +static const char * const anc_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6", "ADC_MB", + "RSVD_1", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", "DMIC6" +}; + +static const char * const anc1_fb_mux_text[] = { + "ZERO", "EAR_HPH_L", "EAR_LINE_1", +}; + +static const char * const iir_inp1_text[] = { + "ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8", + "DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const iir_inp2_text[] = { + "ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8", + "DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const iir_inp3_text[] = { + "ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8", + "DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const iir_inp4_text[] = { + "ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8", + "DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const struct soc_enum rx_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B1_CTL, 0, 12, rx_mix1_text); + +static const struct soc_enum rx_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B1_CTL, 4, 12, rx_mix1_text); + +static const struct soc_enum rx_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B2_CTL, 0, 12, rx_mix1_text); + +static const struct soc_enum rx2_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX2_B1_CTL, 0, 12, rx_mix1_text); + +static const struct soc_enum rx2_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX2_B1_CTL, 4, 12, rx_mix1_text); + +static const struct soc_enum rx3_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX3_B1_CTL, 0, 12, rx_mix1_text); + +static const struct soc_enum rx3_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX3_B1_CTL, 4, 12, rx_mix1_text); + +static const struct soc_enum rx4_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX4_B1_CTL, 0, 12, rx_mix1_text); + +static const struct soc_enum rx4_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX4_B1_CTL, 4, 12, rx_mix1_text); + +static const struct soc_enum rx5_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX5_B1_CTL, 0, 12, rx_mix1_text); + +static const struct soc_enum rx5_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX5_B1_CTL, 4, 12, rx_mix1_text); + +static const struct soc_enum rx6_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX6_B1_CTL, 0, 12, rx_mix1_text); + +static const struct soc_enum rx6_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX6_B1_CTL, 4, 12, rx_mix1_text); + +static const struct soc_enum rx7_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX7_B1_CTL, 0, 12, rx_mix1_text); + +static const struct soc_enum rx7_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX7_B1_CTL, 4, 12, rx_mix1_text); + +static const struct soc_enum rx8_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX8_B1_CTL, 0, 11, rx8_mix1_text); + +static const struct soc_enum rx8_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX8_B1_CTL, 4, 11, rx8_mix1_text); + +static const struct soc_enum rx1_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B3_CTL, 0, 5, rx_mix2_text); + +static const struct soc_enum rx1_mix2_inp2_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B3_CTL, 3, 5, rx_mix2_text); + +static const struct soc_enum rx2_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX2_B3_CTL, 0, 5, rx_mix2_text); + +static const struct soc_enum rx2_mix2_inp2_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX2_B3_CTL, 3, 5, rx_mix2_text); + +static const struct soc_enum rx7_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX7_B3_CTL, 0, 5, rx_mix2_text); + +static const struct soc_enum rx7_mix2_inp2_chain_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX7_B3_CTL, 3, 5, rx_mix2_text); + +static const struct soc_enum rx_rdac5_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_MISC, 2, 2, rx_rdac5_text); + +static const struct soc_enum rx_rdac7_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_MISC, 1, 2, rx_rdac7_text); + +static const struct soc_enum mad_sel_enum = + SOC_ENUM_SINGLE(TOMTOM_A_SVASS_CFG, 0, 2, mad_sel_text); + +static const struct soc_enum sb_tx1_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B1_CTL, 0, 10, sb_tx1_mux_text); + +static const struct soc_enum sb_tx2_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B2_CTL, 0, 10, sb_tx2_mux_text); + +static const struct soc_enum sb_tx3_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B3_CTL, 0, 10, sb_tx3_mux_text); + +static const struct soc_enum sb_tx4_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B4_CTL, 0, 10, sb_tx4_mux_text); + +static const struct soc_enum sb_tx5_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B5_CTL, 0, 10, sb_tx5_mux_text); + +static const struct soc_enum sb_tx6_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B6_CTL, 0, 10, sb_tx6_mux_text); + +static const struct soc_enum sb_tx7_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B7_CTL, 0, 18, + sb_tx7_to_tx10_mux_text); + +static const struct soc_enum sb_tx8_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B8_CTL, 0, 18, + sb_tx7_to_tx10_mux_text); + +static const struct soc_enum sb_tx9_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B9_CTL, 0, 18, + sb_tx7_to_tx10_mux_text); + +static const struct soc_enum sb_tx10_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B10_CTL, 0, 18, + sb_tx7_to_tx10_mux_text); + +static const struct soc_enum dec1_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B1_CTL, 0, 3, dec1_mux_text); + +static const struct soc_enum dec2_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B1_CTL, 2, 3, dec2_mux_text); + +static const struct soc_enum dec3_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B1_CTL, 4, 3, dec3_mux_text); + +static const struct soc_enum dec4_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B1_CTL, 6, 3, dec4_mux_text); + +static const struct soc_enum dec5_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B2_CTL, 0, 3, dec5_mux_text); + +static const struct soc_enum dec6_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B2_CTL, 2, 3, dec6_mux_text); + +static const struct soc_enum dec7_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B2_CTL, 4, 7, dec7_mux_text); + +static const struct soc_enum dec8_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B3_CTL, 0, 7, dec8_mux_text); + +static const struct soc_enum dec9_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B3_CTL, 3, 8, dec9_mux_text); + +static const struct soc_enum dec10_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B4_CTL, 0, 8, dec10_mux_text); + +static const struct soc_enum anc1_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_ANC_B1_CTL, 0, 15, anc_mux_text); + +static const struct soc_enum anc2_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_ANC_B1_CTL, 4, 15, anc_mux_text); + +static const struct soc_enum anc1_fb_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_ANC_B2_CTL, 0, 3, anc1_fb_mux_text); + +static const struct soc_enum iir1_inp1_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ1_B1_CTL, 0, 18, iir_inp1_text); + +static const struct soc_enum iir2_inp1_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ2_B1_CTL, 0, 18, iir_inp1_text); + +static const struct soc_enum iir1_inp2_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ1_B2_CTL, 0, 18, iir_inp2_text); + +static const struct soc_enum iir2_inp2_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ2_B2_CTL, 0, 18, iir_inp2_text); + +static const struct soc_enum iir1_inp3_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ1_B3_CTL, 0, 18, iir_inp3_text); + +static const struct soc_enum iir2_inp3_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ2_B3_CTL, 0, 18, iir_inp3_text); + +static const struct soc_enum iir1_inp4_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ1_B4_CTL, 0, 18, iir_inp4_text); + +static const struct soc_enum iir2_inp4_mux_enum = + SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ2_B4_CTL, 0, 18, iir_inp4_text); + +static const struct snd_kcontrol_new rx_mix1_inp1_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_mix1_inp2_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_mix1_inp3_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP3 Mux", rx_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp1_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp2_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP2 Mux", rx2_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx3_mix1_inp1_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx3_mix1_inp2_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx4_mix1_inp1_mux = + SOC_DAPM_ENUM("RX4 MIX1 INP1 Mux", rx4_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx4_mix1_inp2_mux = + SOC_DAPM_ENUM("RX4 MIX1 INP2 Mux", rx4_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx5_mix1_inp1_mux = + SOC_DAPM_ENUM("RX5 MIX1 INP1 Mux", rx5_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx5_mix1_inp2_mux = + SOC_DAPM_ENUM("RX5 MIX1 INP2 Mux", rx5_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx6_mix1_inp1_mux = + SOC_DAPM_ENUM("RX6 MIX1 INP1 Mux", rx6_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx6_mix1_inp2_mux = + SOC_DAPM_ENUM("RX6 MIX1 INP2 Mux", rx6_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx7_mix1_inp1_mux = + SOC_DAPM_ENUM("RX7 MIX1 INP1 Mux", rx7_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx7_mix1_inp2_mux = + SOC_DAPM_ENUM("RX7 MIX1 INP2 Mux", rx7_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx8_mix1_inp1_mux = + SOC_DAPM_ENUM("RX8 MIX1 INP1 Mux", rx8_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx8_mix1_inp2_mux = + SOC_DAPM_ENUM("RX8 MIX1 INP2 Mux", rx8_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx1_mix2_inp1_mux = + SOC_DAPM_ENUM("RX1 MIX2 INP1 Mux", rx1_mix2_inp1_chain_enum); + +static const struct snd_kcontrol_new rx1_mix2_inp2_mux = + SOC_DAPM_ENUM("RX1 MIX2 INP2 Mux", rx1_mix2_inp2_chain_enum); + +static const struct snd_kcontrol_new rx2_mix2_inp1_mux = + SOC_DAPM_ENUM("RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum); + +static const struct snd_kcontrol_new rx2_mix2_inp2_mux = + SOC_DAPM_ENUM("RX2 MIX2 INP2 Mux", rx2_mix2_inp2_chain_enum); + +static const struct snd_kcontrol_new rx7_mix2_inp1_mux = + SOC_DAPM_ENUM("RX7 MIX2 INP1 Mux", rx7_mix2_inp1_chain_enum); + +static const struct snd_kcontrol_new rx7_mix2_inp2_mux = + SOC_DAPM_ENUM("RX7 MIX2 INP2 Mux", rx7_mix2_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_dac5_mux = + SOC_DAPM_ENUM("RDAC5 MUX Mux", rx_rdac5_enum); + +static const struct snd_kcontrol_new rx_dac7_mux = + SOC_DAPM_ENUM("RDAC7 MUX Mux", rx_rdac7_enum); + +static const struct snd_kcontrol_new mad_sel_mux = + SOC_DAPM_ENUM("MAD_SEL MUX Mux", mad_sel_enum); + +static const struct snd_kcontrol_new sb_tx1_mux = + SOC_DAPM_ENUM("SLIM TX1 MUX Mux", sb_tx1_mux_enum); + +static const struct snd_kcontrol_new sb_tx2_mux = + SOC_DAPM_ENUM("SLIM TX2 MUX Mux", sb_tx2_mux_enum); + +static const struct snd_kcontrol_new sb_tx3_mux = + SOC_DAPM_ENUM("SLIM TX3 MUX Mux", sb_tx3_mux_enum); + +static const struct snd_kcontrol_new sb_tx4_mux = + SOC_DAPM_ENUM("SLIM TX4 MUX Mux", sb_tx4_mux_enum); + +static const struct snd_kcontrol_new sb_tx5_mux = + SOC_DAPM_ENUM("SLIM TX5 MUX Mux", sb_tx5_mux_enum); + +static const struct snd_kcontrol_new sb_tx6_mux = + SOC_DAPM_ENUM("SLIM TX6 MUX Mux", sb_tx6_mux_enum); + +static const struct snd_kcontrol_new sb_tx7_mux = + SOC_DAPM_ENUM("SLIM TX7 MUX Mux", sb_tx7_mux_enum); + +static const struct snd_kcontrol_new sb_tx8_mux = + SOC_DAPM_ENUM("SLIM TX8 MUX Mux", sb_tx8_mux_enum); + +static const struct snd_kcontrol_new sb_tx9_mux = + SOC_DAPM_ENUM("SLIM TX9 MUX Mux", sb_tx9_mux_enum); + +static const struct snd_kcontrol_new sb_tx10_mux = + SOC_DAPM_ENUM("SLIM TX10 MUX Mux", sb_tx10_mux_enum); + + +static int wcd9330_put_dec_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *w = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int dec_mux, decimator; + char *dec_name = NULL; + char *widget_name = NULL; + char *temp; + u16 tx_mux_ctl_reg; + u8 adc_dmic_sel = 0x0; + int ret = 0; + char *dec; + + if (ucontrol->value.enumerated.item[0] >= e->items) + return -EINVAL; + + dec_mux = ucontrol->value.enumerated.item[0]; + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + temp = widget_name; + + dec_name = strsep(&widget_name, " "); + widget_name = temp; + if (!dec_name) { + pr_err("%s: Invalid decimator = %s\n", __func__, w->name); + ret = -EINVAL; + goto out; + } + dec = strpbrk(dec_name, "123456789"); + if (!dec) { + dev_err(w->dapm->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + pr_err("%s: Invalid decimator = %s\n", __func__, dec_name); + ret = -EINVAL; + goto out; + } + + dev_dbg(w->dapm->dev, "%s(): widget = %s decimator = %u dec_mux = %u\n" + , __func__, w->name, decimator, dec_mux); + + + switch (decimator) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + if (dec_mux == 1) + adc_dmic_sel = 0x1; + else + adc_dmic_sel = 0x0; + break; + case 7: + case 8: + case 9: + case 10: + if ((dec_mux == 1) || (dec_mux == 2)) + adc_dmic_sel = 0x1; + else + adc_dmic_sel = 0x0; + break; + default: + pr_err("%s: Invalid Decimator = %u\n", __func__, decimator); + ret = -EINVAL; + goto out; + } + + tx_mux_ctl_reg = TOMTOM_A_CDC_TX1_MUX_CTL + 8 * (decimator - 1); + + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x1, adc_dmic_sel); + + ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + +out: + kfree(widget_name); + return ret; +} + +#define WCD9330_DEC_ENUM(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_dapm_get_enum_double, \ + .put = wcd9330_put_dec_enum, \ + .private_value = (unsigned long)&xenum } + +static const struct snd_kcontrol_new dec1_mux = + WCD9330_DEC_ENUM("DEC1 MUX Mux", dec1_mux_enum); + +static const struct snd_kcontrol_new dec2_mux = + WCD9330_DEC_ENUM("DEC2 MUX Mux", dec2_mux_enum); + +static const struct snd_kcontrol_new dec3_mux = + WCD9330_DEC_ENUM("DEC3 MUX Mux", dec3_mux_enum); + +static const struct snd_kcontrol_new dec4_mux = + WCD9330_DEC_ENUM("DEC4 MUX Mux", dec4_mux_enum); + +static const struct snd_kcontrol_new dec5_mux = + WCD9330_DEC_ENUM("DEC5 MUX Mux", dec5_mux_enum); + +static const struct snd_kcontrol_new dec6_mux = + WCD9330_DEC_ENUM("DEC6 MUX Mux", dec6_mux_enum); + +static const struct snd_kcontrol_new dec7_mux = + WCD9330_DEC_ENUM("DEC7 MUX Mux", dec7_mux_enum); + +static const struct snd_kcontrol_new dec8_mux = + WCD9330_DEC_ENUM("DEC8 MUX Mux", dec8_mux_enum); + +static const struct snd_kcontrol_new dec9_mux = + WCD9330_DEC_ENUM("DEC9 MUX Mux", dec9_mux_enum); + +static const struct snd_kcontrol_new dec10_mux = + WCD9330_DEC_ENUM("DEC10 MUX Mux", dec10_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 iir2_inp1_mux = + SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_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 iir2_inp2_mux = + SOC_DAPM_ENUM("IIR2 INP2 Mux", iir2_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 iir2_inp3_mux = + SOC_DAPM_ENUM("IIR2 INP3 Mux", iir2_inp3_mux_enum); + +static const struct snd_kcontrol_new iir1_inp4_mux = + SOC_DAPM_ENUM("IIR1 INP4 Mux", iir1_inp4_mux_enum); + +static const struct snd_kcontrol_new iir2_inp4_mux = + SOC_DAPM_ENUM("IIR2 INP4 Mux", iir2_inp4_mux_enum); + +static const struct snd_kcontrol_new anc1_mux = + SOC_DAPM_ENUM("ANC1 MUX Mux", anc1_mux_enum); + +static const struct snd_kcontrol_new anc2_mux = + SOC_DAPM_ENUM("ANC2 MUX Mux", anc2_mux_enum); + +static const struct snd_kcontrol_new anc1_fb_mux = + SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum); + +static const struct snd_kcontrol_new dac1_switch[] = { + SOC_DAPM_SINGLE("Switch", TOMTOM_A_RX_EAR_EN, 5, 1, 0) +}; +static const struct snd_kcontrol_new hphl_switch[] = { + SOC_DAPM_SINGLE("Switch", TOMTOM_A_RX_HPH_L_DAC_CTL, 6, 1, 0) +}; + +static const struct snd_kcontrol_new hphl_pa_mix[] = { + SOC_DAPM_SINGLE("AUX_PGA_L Switch", TOMTOM_A_RX_PA_AUX_IN_CONN, + 7, 1, 0), +}; + +static const struct snd_kcontrol_new hphr_pa_mix[] = { + SOC_DAPM_SINGLE("AUX_PGA_R Switch", TOMTOM_A_RX_PA_AUX_IN_CONN, + 6, 1, 0), +}; + +static const struct snd_kcontrol_new ear_pa_mix[] = { + SOC_DAPM_SINGLE("AUX_PGA_L Switch", TOMTOM_A_RX_PA_AUX_IN_CONN, + 5, 1, 0), +}; +static const struct snd_kcontrol_new lineout1_pa_mix[] = { + SOC_DAPM_SINGLE("AUX_PGA_L Switch", TOMTOM_A_RX_PA_AUX_IN_CONN, + 4, 1, 0), +}; + +static const struct snd_kcontrol_new lineout2_pa_mix[] = { + SOC_DAPM_SINGLE("AUX_PGA_R Switch", TOMTOM_A_RX_PA_AUX_IN_CONN, + 3, 1, 0), +}; + +static const struct snd_kcontrol_new lineout3_pa_mix[] = { + SOC_DAPM_SINGLE("AUX_PGA_L Switch", TOMTOM_A_RX_PA_AUX_IN_CONN, + 2, 1, 0), +}; + +static const struct snd_kcontrol_new lineout4_pa_mix[] = { + SOC_DAPM_SINGLE("AUX_PGA_R Switch", TOMTOM_A_RX_PA_AUX_IN_CONN, + 1, 1, 0), +}; + +static const struct snd_kcontrol_new lineout3_ground_switch = + SOC_DAPM_SINGLE("Switch", TOMTOM_A_RX_LINE_3_DAC_CTL, 6, 1, 0); + +static const struct snd_kcontrol_new lineout4_ground_switch = + SOC_DAPM_SINGLE("Switch", TOMTOM_A_RX_LINE_4_DAC_CTL, 6, 1, 0); + +static const struct snd_kcontrol_new aif4_mad_switch = + SOC_DAPM_SINGLE("Switch", TOMTOM_A_SVASS_CLKRST_CTL, 0, 1, 0); + +static const struct snd_kcontrol_new aif4_vi_switch = + SOC_DAPM_SINGLE("Switch", TOMTOM_A_SPKR1_PROT_EN, 3, 1, 0); + +/* virtual port entries */ +static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tomtom_p->tx_port_value; + return 0; +} + +static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + u32 vtable = vport_check_table[dai_id]; + + + pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__, + widget->name, ucontrol->id.name, tomtom_p->tx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tomtom_p->codec_mutex); + + if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + if (dai_id != AIF1_CAP) { + dev_err(codec->dev, "%s: invalid AIF for I2C mode\n", + __func__); + mutex_unlock(&tomtom_p->codec_mutex); + return -EINVAL; + } + } + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set + */ + if (enable && !(tomtom_p->tx_port_value & 1 << port_id)) { + + if (tomtom_p->intf_type == + WCD9XXX_INTERFACE_TYPE_SLIMBUS) + vtable = vport_check_table[dai_id]; + if (tomtom_p->intf_type == + WCD9XXX_INTERFACE_TYPE_I2C) + vtable = vport_i2s_check_table[dai_id]; + + if (wcd9xxx_tx_vport_validation( + vtable, + port_id, + tomtom_p->dai, NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n", + __func__, port_id + 1); + mutex_unlock(&tomtom_p->codec_mutex); + return 0; + } + tomtom_p->tx_port_value |= 1 << port_id; + list_add_tail(&core->tx_chs[port_id].list, + &tomtom_p->dai[dai_id].wcd9xxx_ch_list + ); + } else if (!enable && (tomtom_p->tx_port_value & + 1 << port_id)) { + tomtom_p->tx_port_value &= ~(1 << port_id); + list_del_init(&core->tx_chs[port_id].list); + } else { + if (enable) + dev_dbg(codec->dev, "%s: TX%u port is used by\n" + "this virtual port\n", + __func__, port_id + 1); + else + dev_dbg(codec->dev, "%s: TX%u port is not used by\n" + "this virtual port\n", + __func__, port_id + 1); + /* avoid update power function */ + mutex_unlock(&tomtom_p->codec_mutex); + return 0; + } + break; + default: + pr_err("Unknown AIF %d\n", dai_id); + mutex_unlock(&tomtom_p->codec_mutex); + return -EINVAL; + } + pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__, + widget->name, widget->sname, tomtom_p->tx_port_value, + widget->shift); + + mutex_unlock(&tomtom_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int slim_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = tomtom_p->rx_port_value; + return 0; +} + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB" +}; + +static int slim_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + u32 port_id = widget->shift; + + pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__, + widget->name, ucontrol->id.name, tomtom_p->rx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + tomtom_p->rx_port_value = ucontrol->value.enumerated.item[0]; + + mutex_lock(&tomtom_p->codec_mutex); + + if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + if (tomtom_p->rx_port_value > 2) { + dev_err(codec->dev, "%s: invalid AIF for I2C mode\n", + __func__); + goto err; + } + } + /* value need to match the Virtual port and AIF number + */ + switch (tomtom_p->rx_port_value) { + case 0: + list_del_init(&core->rx_chs[port_id].list); + break; + case 1: + if (wcd9xxx_rx_vport_validation(port_id + + TOMTOM_RX_PORT_START_NUMBER, + &tomtom_p->dai[AIF1_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id + 1); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tomtom_p->dai[AIF1_PB].wcd9xxx_ch_list); + break; + case 2: + if (wcd9xxx_rx_vport_validation(port_id + + TOMTOM_RX_PORT_START_NUMBER, + &tomtom_p->dai[AIF2_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id + 1); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tomtom_p->dai[AIF2_PB].wcd9xxx_ch_list); + break; + case 3: + if (wcd9xxx_rx_vport_validation(port_id + + TOMTOM_RX_PORT_START_NUMBER, + &tomtom_p->dai[AIF3_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id + 1); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tomtom_p->dai[AIF3_PB].wcd9xxx_ch_list); + break; + default: + pr_err("Unknown AIF %d\n", tomtom_p->rx_port_value); + goto err; + } +rtn: + mutex_unlock(&tomtom_p->codec_mutex); + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + tomtom_p->rx_port_value, e, update); + + return 0; +err: + mutex_unlock(&tomtom_p->codec_mutex); + return -EINVAL; +} + +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 snd_kcontrol_new slim_rx_mux[TOMTOM_RX_MAX] = { + 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), + SOC_DAPM_ENUM_EXT("SLIM RX8 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), +}; + +static const struct snd_kcontrol_new aif1_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TOMTOM_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TOMTOM_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TOMTOM_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TOMTOM_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TOMTOM_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TOMTOM_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TOMTOM_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TOMTOM_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TOMTOM_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TOMTOM_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TOMTOM_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TOMTOM_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TOMTOM_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TOMTOM_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TOMTOM_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TOMTOM_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TOMTOM_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TOMTOM_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TOMTOM_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TOMTOM_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TOMTOM_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TOMTOM_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TOMTOM_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TOMTOM_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TOMTOM_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TOMTOM_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TOMTOM_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TOMTOM_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TOMTOM_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TOMTOM_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static void tomtom_codec_enable_adc_block(struct snd_soc_codec *codec, + int enable) +{ + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s %d\n", __func__, enable); + + if (enable) { + tomtom->adc_count++; + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL, + 0x2, 0x2); + } else { + tomtom->adc_count--; + if (!tomtom->adc_count) + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL, + 0x2, 0x0); + } +} + +static int tomtom_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec); + u16 adc_reg; + u16 tx_fe_clkdiv_reg; + u8 tx_fe_clkdiv_mask; + u8 init_bit_shift; + u8 bit_pos; + + pr_debug("%s %d\n", __func__, event); + + switch (w->reg) { + case TOMTOM_A_TX_1_GAIN: + adc_reg = TOMTOM_A_TX_1_2_TEST_CTL; + tx_fe_clkdiv_reg = TOMTOM_A_TX_1_2_TXFE_CLKDIV; + tx_fe_clkdiv_mask = 0x0F; + init_bit_shift = 7; + bit_pos = ADC1_TXFE; + break; + case TOMTOM_A_TX_2_GAIN: + adc_reg = TOMTOM_A_TX_1_2_TEST_CTL; + tx_fe_clkdiv_reg = TOMTOM_A_TX_1_2_TXFE_CLKDIV; + tx_fe_clkdiv_mask = 0xF0; + init_bit_shift = 6; + bit_pos = ADC2_TXFE; + break; + case TOMTOM_A_TX_3_GAIN: + adc_reg = TOMTOM_A_TX_3_4_TEST_CTL; + init_bit_shift = 7; + tx_fe_clkdiv_reg = TOMTOM_A_TX_3_4_TXFE_CKDIV; + tx_fe_clkdiv_mask = 0x0F; + bit_pos = ADC3_TXFE; + break; + case TOMTOM_A_TX_4_GAIN: + adc_reg = TOMTOM_A_TX_3_4_TEST_CTL; + init_bit_shift = 6; + tx_fe_clkdiv_reg = TOMTOM_A_TX_3_4_TXFE_CKDIV; + tx_fe_clkdiv_mask = 0xF0; + bit_pos = ADC4_TXFE; + break; + case TOMTOM_A_TX_5_GAIN: + adc_reg = TOMTOM_A_TX_5_6_TEST_CTL; + init_bit_shift = 7; + tx_fe_clkdiv_reg = TOMTOM_A_TX_5_6_TXFE_CKDIV; + tx_fe_clkdiv_mask = 0x0F; + bit_pos = ADC5_TXFE; + break; + case TOMTOM_A_TX_6_GAIN: + adc_reg = TOMTOM_A_TX_5_6_TEST_CTL; + init_bit_shift = 6; + tx_fe_clkdiv_reg = TOMTOM_A_TX_5_6_TXFE_CKDIV; + tx_fe_clkdiv_mask = 0xF0; + bit_pos = ADC6_TXFE; + break; + default: + pr_err("%s: Error, invalid adc register\n", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, tx_fe_clkdiv_reg, tx_fe_clkdiv_mask, + 0x0); + set_bit(bit_pos, &priv->status_mask); + tomtom_codec_enable_adc_block(codec, 1); + snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, + 1 << init_bit_shift); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + tomtom_codec_enable_adc_block(codec, 0); + break; + } + return 0; +} + +static int tomtom_codec_ext_clk_en(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + if (!tomtom->codec_ext_clk_en_cb) { + dev_err(codec->dev, + "%s: Invalid ext_clk_callback\n", + __func__); + return -EINVAL; + } + + return tomtom->codec_ext_clk_en_cb(codec, enable, dapm); +} + +static int __tomtom_mclk_enable(struct tomtom_priv *tomtom, int mclk_enable) +{ + int ret = 0; + + WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr); + if (mclk_enable) { + tomtom->ext_clk_users++; + if (tomtom->ext_clk_users > 1) + goto bg_clk_unlock; + ret = clk_prepare_enable(tomtom->wcd_ext_clk); + if (ret) { + pr_err("%s: ext clk enable failed\n", + __func__); + tomtom->ext_clk_users--; + goto bg_clk_unlock; + } + wcd9xxx_resmgr_get_bandgap(&tomtom->resmgr, + WCD9XXX_BANDGAP_AUDIO_MODE); + wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr, WCD9XXX_CLK_MCLK); + } else { + tomtom->ext_clk_users--; + if (tomtom->ext_clk_users == 0) { + /* Put clock and BG */ + wcd9xxx_resmgr_put_clk_block(&tomtom->resmgr, + WCD9XXX_CLK_MCLK); + wcd9xxx_resmgr_put_bandgap(&tomtom->resmgr, + WCD9XXX_BANDGAP_AUDIO_MODE); + clk_disable_unprepare(tomtom->wcd_ext_clk); + } + } +bg_clk_unlock: + WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr); + + return ret; +} + +int tomtom_codec_mclk_enable(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + if (tomtom->wcd_ext_clk) { + dev_dbg(codec->dev, "%s: mclk_enable = %u, dapm = %d\n", + __func__, enable, dapm); + return __tomtom_mclk_enable(tomtom, enable); + } else if (tomtom->codec_ext_clk_en_cb) + return tomtom_codec_ext_clk_en(codec, enable, dapm); + else { + dev_err(codec->dev, + "%s: Cannot turn on MCLK\n", + __func__); + return -EINVAL; + } +} +EXPORT_SYMBOL(tomtom_codec_mclk_enable); + +static int tomtom_codec_get_ext_clk_users(struct tomtom_priv *tomtom) +{ + if (tomtom->wcd_ext_clk) + return tomtom->ext_clk_users; + else if (tomtom->codec_get_ext_clk_cnt) + return tomtom->codec_get_ext_clk_cnt(); + else + return 0; +} + +/* tomtom_codec_internal_rco_ctrl( ) + * Make sure that BG_CLK_LOCK is not acquired. Exit if acquired to avoid + * potential deadlock as ext_clk_en_cb() also tries to acquire the same + * lock to enable MCLK for RCO calibration + */ +static int tomtom_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (enable) { + if (wcd9xxx_resmgr_get_clk_type(&tomtom->resmgr) == + WCD9XXX_CLK_RCO) { + WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr); + wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr, + WCD9XXX_CLK_RCO); + WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr); + } else { + tomtom_codec_mclk_enable(codec, true, false); + WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr); + tomtom->resmgr.ext_clk_users = + tomtom_codec_get_ext_clk_users(tomtom); + wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr, + WCD9XXX_CLK_RCO); + WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr); + tomtom_codec_mclk_enable(codec, false, false); + } + + } else { + WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr); + wcd9xxx_resmgr_put_clk_block(&tomtom->resmgr, + WCD9XXX_CLK_RCO); + WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr); + } + + return ret; +} + +static int tomtom_codec_enable_aux_pga(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr); + wcd9xxx_resmgr_get_bandgap(&tomtom->resmgr, + WCD9XXX_BANDGAP_AUDIO_MODE); + WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr); + /* AUX PGA requires RCO or MCLK */ + tomtom_codec_internal_rco_ctrl(codec, true); + WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr); + wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 1); + WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr); + break; + + case SND_SOC_DAPM_POST_PMD: + WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr); + wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 0); + WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr); + tomtom_codec_internal_rco_ctrl(codec, false); + WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr); + wcd9xxx_resmgr_put_bandgap(&tomtom->resmgr, + WCD9XXX_BANDGAP_AUDIO_MODE); + WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr); + break; + } + return 0; +} + +static int tomtom_codec_enable_lineout(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + u16 lineout_gain_reg; + + pr_debug("%s %d %s\n", __func__, event, w->name); + + switch (w->shift) { + case 0: + lineout_gain_reg = TOMTOM_A_RX_LINE_1_GAIN; + break; + case 1: + lineout_gain_reg = TOMTOM_A_RX_LINE_2_GAIN; + break; + case 2: + lineout_gain_reg = TOMTOM_A_RX_LINE_3_GAIN; + break; + case 3: + lineout_gain_reg = TOMTOM_A_RX_LINE_4_GAIN; + break; + default: + pr_err("%s: Error, incorrect lineout register value\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, lineout_gain_reg, 0x40, 0x40); + break; + case SND_SOC_DAPM_POST_PMU: + wcd9xxx_clsh_fsm(codec, &tomtom->clsh_d, + WCD9XXX_CLSH_STATE_LO, + WCD9XXX_CLSH_REQ_ENABLE, + WCD9XXX_CLSH_EVENT_POST_PA); + pr_debug("%s: sleeping 5 ms after %s PA turn on\n", + __func__, w->name); + /* Wait for CnP time after PA enable */ + usleep_range(5000, 5100); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, lineout_gain_reg, 0x40, 0x00); + pr_debug("%s: sleeping 5 ms after %s PA turn off\n", + __func__, w->name); + /* Wait for CnP time after PA disable */ + usleep_range(5000, 5100); + break; + } + return 0; +} + +static int tomtom_codec_enable_spk_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + u16 spk_drv_reg; + + pr_debug("%s: %d %s\n", __func__, event, w->name); + if (strnstr(w->name, "SPK2 PA", sizeof("SPK2 PA"))) + spk_drv_reg = TOMTOM_A_SPKR_DRV2_EN; + else + spk_drv_reg = TOMTOM_A_SPKR_DRV1_EN; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tomtom->spkr_pa_widget_on = true; + snd_soc_update_bits(codec, spk_drv_reg, 0x80, 0x80); + break; + case SND_SOC_DAPM_POST_PMD: + tomtom->spkr_pa_widget_on = false; + snd_soc_update_bits(codec, spk_drv_reg, 0x80, 0x00); + break; + } + return 0; +} + +static u8 tomtom_get_dmic_clk_val(struct snd_soc_codec *codec, + u32 mclk_rate, u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val; + + dev_dbg(codec->dev, + "%s: mclk_rate = %d, dmic_sample_rate = %d\n", + __func__, mclk_rate, dmic_clk_rate); + + /* Default value to return in case of error */ + if (mclk_rate == TOMTOM_MCLK_CLK_9P6MHZ) + dmic_ctl_val = WCD9330_DMIC_CLK_DIV_2; + else + dmic_ctl_val = WCD9330_DMIC_CLK_DIV_3; + + if (dmic_clk_rate == 0) { + dev_err(codec->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 = WCD9330_DMIC_CLK_DIV_2; + break; + case 3: + dmic_ctl_val = WCD9330_DMIC_CLK_DIV_3; + break; + case 4: + dmic_ctl_val = WCD9330_DMIC_CLK_DIV_4; + break; + case 6: + dmic_ctl_val = WCD9330_DMIC_CLK_DIV_6; + break; + case 16: + dmic_ctl_val = WCD9330_DMIC_CLK_DIV_16; + break; + default: + dev_err(codec->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 tomtom_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = tomtom->resmgr.pdata; + u8 dmic_clk_en; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 dmic_rate_val, dmic_rate_shift; + unsigned int dmic; + int ret; + char *wname; + + wname = strpbrk(w->name, "123456"); + if (!wname) { + dev_err(codec->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + pr_err("%s: Invalid DMIC line on the codec\n", __func__); + return -EINVAL; + } + + switch (dmic) { + case 1: + case 2: + dmic_clk_en = 0x01; + dmic_clk_cnt = &(tomtom->dmic_1_2_clk_cnt); + dmic_clk_reg = TOMTOM_A_DMIC_B1_CTL; + dmic_rate_shift = 5; + pr_debug("%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + break; + + case 3: + case 4: + dmic_clk_en = 0x02; + dmic_clk_cnt = &(tomtom->dmic_3_4_clk_cnt); + dmic_clk_reg = TOMTOM_A_DMIC_B2_CTL; + dmic_rate_shift = 1; + pr_debug("%s() event %d DMIC%d dmic_3_4_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + break; + + case 5: + case 6: + dmic_clk_en = 0x04; + dmic_clk_cnt = &(tomtom->dmic_5_6_clk_cnt); + dmic_clk_reg = TOMTOM_A_DMIC_B2_CTL; + dmic_rate_shift = 4; + pr_debug("%s() event %d DMIC%d dmic_5_6_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + break; + + default: + pr_err("%s: Invalid DMIC Selection\n", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + + dmic_rate_val = + tomtom_get_dmic_clk_val(codec, + pdata->mclk_rate, + pdata->dmic_sample_rate); + + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + snd_soc_update_bits(codec, TOMTOM_A_DMIC_B1_CTL, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + + dmic_rate_val = + tomtom_get_dmic_clk_val(codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_update_bits(codec, TOMTOM_A_DMIC_B1_CTL, + dmic_clk_en, 0); + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + } + break; + } + return 0; +} + +static int tomtom_codec_config_mad(struct snd_soc_codec *codec) +{ + int ret = 0; + const struct firmware *fw; + struct firmware_cal *hwdep_cal = NULL; + struct mad_audio_cal *mad_cal; + const void *data; + const char *filename = TOMTOM_MAD_AUDIO_FIRMWARE_PATH; + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + size_t cal_size; + int idx; + + pr_debug("%s: enter\n", __func__); + + if (!tomtom->fw_data) { + dev_err(codec->dev, "%s: invalid cal data\n", + __func__); + return -ENODEV; + } + + hwdep_cal = wcdcal_get_fw_cal(tomtom->fw_data, WCD9XXX_MAD_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + ret = request_firmware(&fw, filename, codec->dev); + if (ret != 0) { + pr_err("Failed to acquire MAD firwmare data %s: %d\n", + filename, ret); + return -ENODEV; + } + if (!fw) { + dev_err(codec->dev, "failed to get mad fw"); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + if (cal_size < sizeof(struct mad_audio_cal)) { + pr_err("%s: incorrect hwdep cal size %zu\n", + __func__, cal_size); + ret = -ENOMEM; + goto err; + } + + mad_cal = (struct mad_audio_cal *)(data); + if (!mad_cal) { + dev_err(codec->dev, "%s: Invalid calibration data\n", + __func__); + ret = -EINVAL; + goto err; + } + + snd_soc_write(codec, TOMTOM_A_CDC_MAD_MAIN_CTL_2, + mad_cal->microphone_info.cycle_time); + snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_MAIN_CTL_1, 0xFF << 3, + ((uint16_t)mad_cal->microphone_info.settle_time) + << 3); + + /* Audio */ + snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_8, + mad_cal->audio_info.rms_omit_samples); + snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_1, + 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4); + snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_2, 0x03 << 2, + mad_cal->audio_info.detection_mechanism << 2); + snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_7, + mad_cal->audio_info.rms_diff_threshold & 0x3F); + snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_5, + mad_cal->audio_info.rms_threshold_lsb); + snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_6, + mad_cal->audio_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL, + mad_cal->audio_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->audio_info.iir_coefficients[idx]); + } + + /* Beacon */ + snd_soc_write(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_8, + mad_cal->beacon_info.rms_omit_samples); + snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_1, + 0x07 << 4, mad_cal->beacon_info.rms_comp_time); + snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_2, 0x03 << 2, + mad_cal->beacon_info.detection_mechanism << 2); + snd_soc_write(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_7, + mad_cal->beacon_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_5, + mad_cal->beacon_info.rms_threshold_lsb); + snd_soc_write(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_6, + mad_cal->beacon_info.rms_threshold_msb); + + /* Ultrasound */ + snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_1, + 0x07 << 4, mad_cal->beacon_info.rms_comp_time); + snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_ULTR_CTL_2, 0x03 << 2, + mad_cal->ultrasound_info.detection_mechanism); + snd_soc_write(codec, TOMTOM_A_CDC_MAD_ULTR_CTL_7, + mad_cal->ultrasound_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, TOMTOM_A_CDC_MAD_ULTR_CTL_5, + mad_cal->ultrasound_info.rms_threshold_lsb); + snd_soc_write(codec, TOMTOM_A_CDC_MAD_ULTR_CTL_6, + mad_cal->ultrasound_info.rms_threshold_msb); + + /* Set MAD intr time to 20 msec */ + snd_soc_update_bits(codec, 0x4E, 0x01F, 0x13); + + pr_debug("%s: leave ret %d\n", __func__, ret); +err: + if (!hwdep_cal) + release_firmware(fw); + return ret; +} + +static int tomtom_codec_enable_mad(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + int ret = 0; + u8 mad_micb, mad_cfilt; + u16 mad_cfilt_reg; + + mad_micb = snd_soc_read(codec, TOMTOM_A_MAD_ANA_CTRL) & 0x07; + switch (mad_micb) { + case 1: + mad_cfilt = tomtom->resmgr.pdata->micbias.bias1_cfilt_sel; + break; + case 2: + mad_cfilt = tomtom->resmgr.pdata->micbias.bias2_cfilt_sel; + break; + case 3: + mad_cfilt = tomtom->resmgr.pdata->micbias.bias3_cfilt_sel; + break; + case 4: + mad_cfilt = tomtom->resmgr.pdata->micbias.bias4_cfilt_sel; + break; + default: + dev_err(codec->dev, + "%s: Invalid micbias selection 0x%x\n", + __func__, mad_micb); + return -EINVAL; + } + + switch (mad_cfilt) { + case WCD9XXX_CFILT1_SEL: + mad_cfilt_reg = TOMTOM_A_MICB_CFILT_1_VAL; + break; + case WCD9XXX_CFILT2_SEL: + mad_cfilt_reg = TOMTOM_A_MICB_CFILT_2_VAL; + break; + case WCD9XXX_CFILT3_SEL: + mad_cfilt_reg = TOMTOM_A_MICB_CFILT_3_VAL; + break; + default: + dev_err(codec->dev, + "%s: invalid cfilt 0x%x for micb 0x%x\n", + __func__, mad_cfilt, mad_micb); + return -EINVAL; + } + + dev_dbg(codec->dev, + "%s event = %d, mad_cfilt_reg = 0x%x\n", + __func__, event, mad_cfilt_reg); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Undo reset for MAD */ + snd_soc_update_bits(codec, TOMTOM_A_SVASS_CLKRST_CTL, + 0x02, 0x00); + + ret = tomtom_codec_config_mad(codec); + if (ret) { + pr_err("%s: Failed to config MAD\n", __func__); + break; + } + + /* setup MAD micbias to VDDIO */ + snd_soc_update_bits(codec, mad_cfilt_reg, + 0x02, 0x02); + break; + case SND_SOC_DAPM_POST_PMD: + /* Reset the MAD block */ + snd_soc_update_bits(codec, TOMTOM_A_SVASS_CLKRST_CTL, + 0x02, 0x02); + + /* Undo setup of MAD micbias to VDDIO */ + snd_soc_update_bits(codec, mad_cfilt_reg, + 0x02, 0x00); + } + return ret; +} + +static int tomtom_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + u16 micb_int_reg = 0, micb_ctl_reg = 0; + u8 cfilt_sel_val = 0; + char *internal1_text = "Internal1"; + char *internal2_text = "Internal2"; + char *internal3_text = "Internal3"; + enum wcd9xxx_notify_event e_post_off, e_pre_on, e_post_on; + + pr_debug("%s: w->name %s event %d\n", __func__, w->name, event); + if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) { + micb_ctl_reg = TOMTOM_A_MICB_1_CTL; + micb_int_reg = TOMTOM_A_MICB_1_INT_RBIAS; + cfilt_sel_val = tomtom->resmgr.pdata->micbias.bias1_cfilt_sel; + e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_1_ON; + e_post_on = WCD9XXX_EVENT_POST_MICBIAS_1_ON; + e_post_off = WCD9XXX_EVENT_POST_MICBIAS_1_OFF; + } else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) { + micb_ctl_reg = TOMTOM_A_MICB_2_CTL; + micb_int_reg = TOMTOM_A_MICB_2_INT_RBIAS; + cfilt_sel_val = tomtom->resmgr.pdata->micbias.bias2_cfilt_sel; + e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_2_ON; + e_post_on = WCD9XXX_EVENT_POST_MICBIAS_2_ON; + e_post_off = WCD9XXX_EVENT_POST_MICBIAS_2_OFF; + } else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) { + micb_ctl_reg = TOMTOM_A_MICB_3_CTL; + micb_int_reg = TOMTOM_A_MICB_3_INT_RBIAS; + cfilt_sel_val = tomtom->resmgr.pdata->micbias.bias3_cfilt_sel; + e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_3_ON; + e_post_on = WCD9XXX_EVENT_POST_MICBIAS_3_ON; + e_post_off = WCD9XXX_EVENT_POST_MICBIAS_3_OFF; + } else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4"))) { + micb_ctl_reg = TOMTOM_A_MICB_4_CTL; + micb_int_reg = tomtom->resmgr.reg_addr->micb_4_int_rbias; + cfilt_sel_val = tomtom->resmgr.pdata->micbias.bias4_cfilt_sel; + e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_4_ON; + e_post_on = WCD9XXX_EVENT_POST_MICBIAS_4_ON; + e_post_off = WCD9XXX_EVENT_POST_MICBIAS_4_OFF; + } else { + pr_err("%s: Error, invalid micbias %s\n", __func__, w->name); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Let MBHC module know so micbias switch to be off */ + wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_pre_on); + + /* Get cfilt */ + wcd9xxx_resmgr_cfilt_get(&tomtom->resmgr, cfilt_sel_val); + + if (strnstr(w->name, internal1_text, 30)) + snd_soc_update_bits(codec, micb_int_reg, 0xE0, 0xE0); + else if (strnstr(w->name, internal2_text, 30)) + snd_soc_update_bits(codec, micb_int_reg, 0x1C, 0x1C); + else if (strnstr(w->name, internal3_text, 30)) + snd_soc_update_bits(codec, micb_int_reg, 0x3, 0x3); + else + /* + * If not internal, make sure to write the + * register to default value + */ + snd_soc_write(codec, micb_int_reg, 0x24); + if (tomtom->mbhc_started && micb_ctl_reg == + TOMTOM_A_MICB_2_CTL) { + if (++tomtom->micb_2_users == 1) { + if (tomtom->resmgr.pdata-> + micbias.bias2_is_headset_only) + wcd9xxx_resmgr_add_cond_update_bits( + &tomtom->resmgr, + WCD9XXX_COND_HPH_MIC, + micb_ctl_reg, w->shift, + false); + else + snd_soc_update_bits(codec, micb_ctl_reg, + 1 << w->shift, + 1 << w->shift); + } + pr_debug("%s: micb_2_users %d\n", __func__, + tomtom->micb_2_users); + } else { + snd_soc_update_bits(codec, micb_ctl_reg, 1 << w->shift, + 1 << w->shift); + } + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(5000, 5100); + /* Let MBHC module know so micbias is on */ + wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_post_on); + break; + case SND_SOC_DAPM_POST_PMD: + if (tomtom->mbhc_started && micb_ctl_reg == + TOMTOM_A_MICB_2_CTL) { + if (--tomtom->micb_2_users == 0) { + if (tomtom->resmgr.pdata-> + micbias.bias2_is_headset_only) + wcd9xxx_resmgr_rm_cond_update_bits( + &tomtom->resmgr, + WCD9XXX_COND_HPH_MIC, + micb_ctl_reg, 7, false); + else + snd_soc_update_bits(codec, micb_ctl_reg, + 1 << w->shift, 0); + } + pr_debug("%s: micb_2_users %d\n", __func__, + tomtom->micb_2_users); + WARN(tomtom->micb_2_users < 0, + "Unexpected micbias users %d\n", + tomtom->micb_2_users); + } else { + snd_soc_update_bits(codec, micb_ctl_reg, 1 << w->shift, + 0); + } + + /* Let MBHC module know so micbias switch to be off */ + wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_post_off); + + if (strnstr(w->name, internal1_text, 30)) + snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x00); + else if (strnstr(w->name, internal2_text, 30)) + snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x00); + else if (strnstr(w->name, internal3_text, 30)) + snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0); + + /* Put cfilt */ + wcd9xxx_resmgr_cfilt_put(&tomtom->resmgr, cfilt_sel_val); + break; + } + + return 0; +} + +/* called under codec_resource_lock acquisition */ +static int tomtom_enable_mbhc_micbias(struct snd_soc_codec *codec, bool enable, + enum wcd9xxx_micbias_num micb_num) +{ + int rc; + + if (micb_num != MBHC_MICBIAS2) { + dev_err(codec->dev, "%s: Unsupported micbias, micb_num=%d\n", + __func__, micb_num); + return -EINVAL; + } + + if (enable) + rc = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + DAPM_MICBIAS2_EXTERNAL_STANDALONE); + else + rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec), + DAPM_MICBIAS2_EXTERNAL_STANDALONE); + if (!rc) + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + pr_debug("%s: leave ret %d\n", __func__, rc); + return rc; +} + +static void txfe_clkdiv_update(struct snd_soc_codec *codec) +{ + struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (test_bit(ADC1_TXFE, &priv->status_mask)) { + snd_soc_update_bits(codec, TOMTOM_A_TX_1_2_TXFE_CLKDIV, + 0x0F, 0x05); + clear_bit(ADC1_TXFE, &priv->status_mask); + } + if (test_bit(ADC2_TXFE, &priv->status_mask)) { + snd_soc_update_bits(codec, TOMTOM_A_TX_1_2_TXFE_CLKDIV, + 0xF0, 0x50); + clear_bit(ADC2_TXFE, &priv->status_mask); + } + if (test_bit(ADC3_TXFE, &priv->status_mask)) { + snd_soc_update_bits(codec, TOMTOM_A_TX_3_4_TXFE_CKDIV, + 0x0F, 0x05); + clear_bit(ADC3_TXFE, &priv->status_mask); + } + if (test_bit(ADC4_TXFE, &priv->status_mask)) { + snd_soc_update_bits(codec, TOMTOM_A_TX_3_4_TXFE_CKDIV, + 0xF0, 0x50); + clear_bit(ADC4_TXFE, &priv->status_mask); + } + if (test_bit(ADC5_TXFE, &priv->status_mask)) { + snd_soc_update_bits(codec, TOMTOM_A_TX_5_6_TXFE_CKDIV, + 0x0F, 0x05); + clear_bit(ADC5_TXFE, &priv->status_mask); + } + if (test_bit(ADC6_TXFE, &priv->status_mask)) { + snd_soc_update_bits(codec, TOMTOM_A_TX_5_6_TXFE_CKDIV, + 0xF0, 0x50); + clear_bit(ADC6_TXFE, &priv->status_mask); + } +} + +static void tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct tomtom_priv *tomtom; + struct snd_soc_codec *codec; + u16 tx_mux_ctl_reg; + u8 hpf_cut_of_freq; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + tomtom = hpf_work->tomtom; + codec = hpf_work->tomtom->codec; + hpf_cut_of_freq = hpf_work->tx_hpf_cut_of_freq; + + tx_mux_ctl_reg = TOMTOM_A_CDC_TX1_MUX_CTL + + (hpf_work->decimator - 1) * 8; + + pr_debug("%s(): decimator %u hpf_cut_of_freq 0x%x\n", __func__, + hpf_work->decimator, (unsigned int)hpf_cut_of_freq); + + /* + * Restore TXFE ClkDiv registers to default. + * If any of these registers are modified during analog + * front-end enablement, they will be restored back to the + * default + */ + txfe_clkdiv_update(codec); + + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, hpf_cut_of_freq << 4); +} + +#define TX_MUX_CTL_CUT_OFF_FREQ_MASK 0x30 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +static int tomtom_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int decimator; + char *dec_name = NULL; + char *widget_name = NULL; + char *temp; + int ret = 0; + u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg; + u8 dec_hpf_cut_of_freq; + int offset; + char *dec; + + pr_debug("%s %d\n", __func__, event); + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + temp = widget_name; + + dec_name = strsep(&widget_name, " "); + widget_name = temp; + if (!dec_name) { + pr_err("%s: Invalid decimator = %s\n", __func__, w->name); + ret = -EINVAL; + goto out; + } + + dec = strpbrk(dec_name, "123456789"); + if (!dec) { + dev_err(codec->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + pr_err("%s: Invalid decimator = %s\n", __func__, dec_name); + ret = -EINVAL; + goto out; + } + + pr_debug("%s(): widget = %s dec_name = %s decimator = %u\n", __func__, + w->name, dec_name, decimator); + + if (w->reg == TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL) { + dec_reset_reg = TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL; + offset = 0; + } else if (w->reg == TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL) { + dec_reset_reg = TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL; + offset = 8; + } else { + pr_err("%s: Error, incorrect dec\n", __func__); + return -EINVAL; + } + + tx_vol_ctl_reg = TOMTOM_A_CDC_TX1_VOL_CTL_CFG + 8 * (decimator - 1); + tx_mux_ctl_reg = TOMTOM_A_CDC_TX1_MUX_CTL + 8 * (decimator - 1); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + + /* Enableable TX digital mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); + + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, + 1 << w->shift); + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0); + + pr_debug("%s: decimator = %u, bypass = %d\n", __func__, + decimator, tx_hpf_work[decimator - 1].tx_hpf_bypass); + if (tx_hpf_work[decimator - 1].tx_hpf_bypass != true) { + dec_hpf_cut_of_freq = snd_soc_read(codec, + tx_mux_ctl_reg); + + dec_hpf_cut_of_freq = (dec_hpf_cut_of_freq & 0x30) >> 4; + + tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq = + dec_hpf_cut_of_freq; + + if (dec_hpf_cut_of_freq != CF_MIN_3DB_150HZ) { + + /* set cut of freq to CF_MIN_3DB_150HZ (0x1); */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, + CF_MIN_3DB_150HZ << 4); + } + + /* enable HPF */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x00); + } else + /* bypass HPF */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); + + break; + + case SND_SOC_DAPM_POST_PMU: + + /* Disable TX digital mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00); + + if ((tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq != + CF_MIN_3DB_150HZ) && + (tx_hpf_work[decimator - 1].tx_hpf_bypass != true)) { + + schedule_delayed_work(&tx_hpf_work[decimator - 1].dwork, + msecs_to_jiffies(300)); + } + /* apply the digital gain after the decimator is enabled*/ + if ((w->shift + offset) < ARRAY_SIZE(tx_digital_gain_reg)) + snd_soc_write(codec, + tx_digital_gain_reg[w->shift + offset], + snd_soc_read(codec, + tx_digital_gain_reg[w->shift + offset]) + ); + + break; + + case SND_SOC_DAPM_PRE_PMD: + + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); + cancel_delayed_work_sync(&tx_hpf_work[decimator - 1].dwork); + break; + + case SND_SOC_DAPM_POST_PMD: + + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, + (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq) << 4); + + break; + } +out: + kfree(widget_name); + return ret; +} + +static int tomtom_codec_enable_vdd_spkr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: %d %s\n", __func__, event, w->name); + + WARN_ONCE(!priv->spkdrv_reg, "SPKDRV supply %s isn't defined\n", + WCD9XXX_VDD_SPKDRV_NAME); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (priv->spkdrv_reg) { + ret = regulator_enable(priv->spkdrv_reg); + if (ret) + pr_err("%s: Failed to enable spkdrv_reg %s\n", + __func__, WCD9XXX_VDD_SPKDRV_NAME); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (priv->spkdrv_reg) { + ret = regulator_disable(priv->spkdrv_reg); + if (ret) + pr_err("%s: Failed to disable spkdrv_reg %s\n", + __func__, WCD9XXX_VDD_SPKDRV_NAME); + } + break; + } + + return ret; +} + +static int tomtom_codec_enable_vdd_spkr2(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: %d %s\n", __func__, event, w->name); + + /* + * If on-demand voltage regulators of spkr1 and spkr2 has been derived + * from same power rail then same on-demand voltage regulator can be + * used by both spkr1 and spkr2, if a separate device tree entry has + * not been defined for on-demand voltage regulator for spkr2. + */ + if (!priv->spkdrv2_reg) { + if (priv->spkdrv_reg) { + priv->spkdrv2_reg = priv->spkdrv_reg; + } else { + WARN_ONCE(!priv->spkdrv2_reg, + "SPKDRV2 supply %s isn't defined\n", + WCD9XXX_VDD_SPKDRV2_NAME); + return 0; + } + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (priv->spkdrv2_reg) { + ret = regulator_enable(priv->spkdrv2_reg); + if (ret) + pr_err("%s: Failed to enable spkdrv2_reg %s ret:%d\n", + __func__, WCD9XXX_VDD_SPKDRV2_NAME, ret); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (priv->spkdrv2_reg) { + ret = regulator_disable(priv->spkdrv2_reg); + if (ret) + pr_err("%s: Failed to disable spkdrv2_reg %s ret:%d\n", + __func__, WCD9XXX_VDD_SPKDRV2_NAME, ret); + } + break; + } + + return ret; +} + +static int tomtom_codec_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s %d %s\n", __func__, event, w->name); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_RESET_CTL, + 1 << w->shift, 1 << w->shift); + snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_RESET_CTL, + 1 << w->shift, 0x0); + break; + case SND_SOC_DAPM_POST_PMU: + /* apply the digital gain after the interpolator is enabled*/ + if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg)) + snd_soc_write(codec, + rx_digital_gain_reg[w->shift], + snd_soc_read(codec, + rx_digital_gain_reg[w->shift]) + ); + /* Check for Rx1 and Rx2 paths for uhqa mode update */ + if (w->shift == 0 || w->shift == 1) + tomtom_update_uhqa_mode(codec, (1 << w->shift)); + + break; + } + return 0; +} + +/* called under codec_resource_lock acquisition */ +static int __tomtom_codec_enable_ldo_h(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: enter\n", __func__); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* + * ldo_h_users is protected by tomtom->codec_mutex, don't need + * additional mutex + */ + if (++priv->ldo_h_users == 1) { + WCD9XXX_BG_CLK_LOCK(&priv->resmgr); + wcd9xxx_resmgr_get_bandgap(&priv->resmgr, + WCD9XXX_BANDGAP_AUDIO_MODE); + WCD9XXX_BG_CLK_UNLOCK(&priv->resmgr); + tomtom_codec_internal_rco_ctrl(codec, true); + snd_soc_update_bits(codec, TOMTOM_A_LDO_H_MODE_1, + 1 << 7, 1 << 7); + tomtom_codec_internal_rco_ctrl(codec, false); + pr_debug("%s: ldo_h_users %d\n", __func__, + priv->ldo_h_users); + /* LDO enable requires 1ms to settle down */ + usleep_range(1000, 1100); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (--priv->ldo_h_users == 0) { + tomtom_codec_internal_rco_ctrl(codec, true); + snd_soc_update_bits(codec, TOMTOM_A_LDO_H_MODE_1, + 1 << 7, 0); + tomtom_codec_internal_rco_ctrl(codec, false); + WCD9XXX_BG_CLK_LOCK(&priv->resmgr); + wcd9xxx_resmgr_put_bandgap(&priv->resmgr, + WCD9XXX_BANDGAP_AUDIO_MODE); + WCD9XXX_BG_CLK_UNLOCK(&priv->resmgr); + pr_debug("%s: ldo_h_users %d\n", __func__, + priv->ldo_h_users); + } + WARN(priv->ldo_h_users < 0, "Unexpected ldo_h users %d\n", + priv->ldo_h_users); + break; + } + pr_debug("%s: leave\n", __func__); + return 0; +} + +static int tomtom_codec_enable_ldo_h(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int rc; + + rc = __tomtom_codec_enable_ldo_h(w, kcontrol, event); + return rc; +} + +static int tomtom_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 1); + break; + case SND_SOC_DAPM_POST_PMD: + wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 0); + break; + } + return 0; +} + +static int tomtom_codec_enable_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + const char *filename; + const struct firmware *fw; + int i; + int ret = 0; + int num_anc_slots; + struct wcd9xxx_anc_header *anc_head; + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + struct firmware_cal *hwdep_cal = NULL; + u32 anc_writes_size = 0; + u32 anc_cal_size = 0; + int anc_size_remaining; + u32 *anc_ptr; + u16 reg; + u8 mask, val, old_val; + size_t cal_size; + const void *data; + + if (tomtom->anc_func == 0) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + filename = "wcd9320/wcd9320_anc.bin"; + + hwdep_cal = wcdcal_get_fw_cal(tomtom->fw_data, WCD9XXX_ANC_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + ret = request_firmware(&fw, filename, codec->dev); + if (ret != 0) { + dev_err(codec->dev, "Failed to acquire ANC data: %d\n", + ret); + return -ENODEV; + } + if (!fw) { + dev_err(codec->dev, "failed to get anc fw"); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + if (cal_size < sizeof(struct wcd9xxx_anc_header)) { + dev_err(codec->dev, "Not enough data\n"); + ret = -ENOMEM; + goto err; + } + /* First number is the number of register writes */ + anc_head = (struct wcd9xxx_anc_header *)(data); + anc_ptr = (u32 *)(data + + sizeof(struct wcd9xxx_anc_header)); + anc_size_remaining = cal_size - + sizeof(struct wcd9xxx_anc_header); + num_anc_slots = anc_head->num_anc_slots; + + if (tomtom->anc_slot >= num_anc_slots) { + dev_err(codec->dev, "Invalid ANC slot selected\n"); + ret = -EINVAL; + goto err; + } + for (i = 0; i < num_anc_slots; i++) { + if (anc_size_remaining < TOMTOM_PACKED_REG_SIZE) { + dev_err(codec->dev, "Invalid register format\n"); + ret = -EINVAL; + goto err; + } + anc_writes_size = (u32)(*anc_ptr); + anc_size_remaining -= sizeof(u32); + anc_ptr += 1; + + if (anc_writes_size * TOMTOM_PACKED_REG_SIZE + > anc_size_remaining) { + dev_err(codec->dev, "Invalid register format\n"); + ret = -EINVAL; + goto err; + } + + if (tomtom->anc_slot == i) + break; + + anc_size_remaining -= (anc_writes_size * + TOMTOM_PACKED_REG_SIZE); + anc_ptr += anc_writes_size; + } + if (i == num_anc_slots) { + dev_err(codec->dev, "Selected ANC slot not present\n"); + ret = -EINVAL; + goto err; + } + + i = 0; + anc_cal_size = anc_writes_size; + if (w->reg == TOMTOM_A_RX_HPH_L_DAC_CTL) { + snd_soc_update_bits(codec, + TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x03, 0x03); + anc_writes_size = (anc_cal_size/2); + } + + if (w->reg == TOMTOM_A_RX_HPH_R_DAC_CTL) { + snd_soc_update_bits(codec, + TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x0C, 0x0C); + i = (anc_cal_size/2); + anc_writes_size = anc_cal_size; + } + + for (; i < anc_writes_size; i++) { + TOMTOM_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, + mask, val); + /* + * ANC Soft reset register is ignored from ACDB + * because ANC left soft reset bits will be called + * while enabling ANC HPH Right DAC. + */ + if ((reg == TOMTOM_A_CDC_CLK_ANC_RESET_CTL) && + ((w->reg == TOMTOM_A_RX_HPH_L_DAC_CTL) || + (w->reg == TOMTOM_A_RX_HPH_R_DAC_CTL))) { + continue; + } + old_val = snd_soc_read(codec, reg); + snd_soc_write(codec, reg, (old_val & ~mask) | + (val & mask)); + } + if (w->reg == TOMTOM_A_RX_HPH_L_DAC_CTL) + snd_soc_update_bits(codec, + TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x03, 0x00); + + if (w->reg == TOMTOM_A_RX_HPH_R_DAC_CTL) + snd_soc_update_bits(codec, + TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x0C, 0x00); + if (!hwdep_cal) + release_firmware(fw); + txfe_clkdiv_update(codec); + break; + case SND_SOC_DAPM_PRE_PMD: + msleep(40); + snd_soc_update_bits(codec, TOMTOM_A_CDC_ANC1_B1_CTL, 0x01, + 0x00); + snd_soc_update_bits(codec, TOMTOM_A_CDC_ANC2_B1_CTL, 0x02, + 0x00); + msleep(20); + snd_soc_write(codec, TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x0F); + snd_soc_write(codec, TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL, 0); + snd_soc_write(codec, TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x00); + break; + } + return 0; +err: + if (!hwdep_cal) + release_firmware(fw); + return ret; +} + +static int tomtom_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + uint32_t impedl, impedr; + int ret = 0; + + pr_debug("%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tomtom_p->anc_func) { + tomtom_codec_enable_anc(w, kcontrol, event); + msleep(50); + } + + if (!high_perf_mode && !tomtom_p->uhqa_mode) { + wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d, + WCD9XXX_CLSH_STATE_HPHL, + WCD9XXX_CLSH_REQ_ENABLE, + WCD9XXX_CLSH_EVENT_PRE_DAC); + } else { + wcd9xxx_enable_high_perf_mode(codec, &tomtom_p->clsh_d, + tomtom_p->uhqa_mode, + WCD9XXX_CLSAB_STATE_HPHL, + WCD9XXX_CLSAB_REQ_ENABLE); + } + ret = wcd9xxx_mbhc_get_impedance(&tomtom_p->mbhc, + &impedl, &impedr); + if (!ret) + wcd9xxx_clsh_imped_config(codec, impedl); + else + dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n", + __func__, ret); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, TOMTOM_A_CDC_RX1_B3_CTL, 0xBC, 0x94); + snd_soc_update_bits(codec, TOMTOM_A_CDC_RX1_B4_CTL, 0x30, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, TOMTOM_A_CDC_RX1_B3_CTL, 0xBC, 0x00); + snd_soc_update_bits(codec, TOMTOM_A_CDC_RX1_B4_CTL, 0x30, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + if (!high_perf_mode && !tomtom_p->uhqa_mode) { + wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d, + WCD9XXX_CLSH_STATE_HPHL, + WCD9XXX_CLSH_REQ_DISABLE, + WCD9XXX_CLSH_EVENT_POST_PA); + } else { + wcd9xxx_enable_high_perf_mode(codec, &tomtom_p->clsh_d, + tomtom_p->uhqa_mode, + WCD9XXX_CLSAB_STATE_HPHL, + WCD9XXX_CLSAB_REQ_DISABLE); + } + break; + } + return 0; +} + +static int tomtom_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tomtom_p->anc_func) { + tomtom_codec_enable_anc(w, kcontrol, event); + msleep(50); + } + + snd_soc_update_bits(codec, w->reg, 0x40, 0x40); + if (!high_perf_mode && !tomtom_p->uhqa_mode) { + wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d, + WCD9XXX_CLSH_STATE_HPHR, + WCD9XXX_CLSH_REQ_ENABLE, + WCD9XXX_CLSH_EVENT_PRE_DAC); + } else { + wcd9xxx_enable_high_perf_mode(codec, &tomtom_p->clsh_d, + tomtom_p->uhqa_mode, + WCD9XXX_CLSAB_STATE_HPHR, + WCD9XXX_CLSAB_REQ_ENABLE); + } + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, TOMTOM_A_CDC_RX2_B3_CTL, 0xBC, 0x94); + snd_soc_update_bits(codec, TOMTOM_A_CDC_RX2_B4_CTL, 0x30, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, TOMTOM_A_CDC_RX2_B3_CTL, 0xBC, 0x00); + snd_soc_update_bits(codec, TOMTOM_A_CDC_RX2_B4_CTL, 0x30, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, w->reg, 0x40, 0x00); + if (!high_perf_mode && !tomtom_p->uhqa_mode) { + wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d, + WCD9XXX_CLSH_STATE_HPHR, + WCD9XXX_CLSH_REQ_DISABLE, + WCD9XXX_CLSH_EVENT_POST_PA); + } else { + wcd9xxx_enable_high_perf_mode(codec, &tomtom_p->clsh_d, + tomtom_p->uhqa_mode, + WCD9XXX_CLSAB_STATE_HPHR, + WCD9XXX_CLSAB_REQ_DISABLE); + } + break; + } + return 0; +} + +static int tomtom_hph_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + enum wcd9xxx_notify_event e_pre_on, e_post_off; + u8 req_clsh_state; + u32 pa_settle_time = TOMTOM_HPH_PA_SETTLE_COMP_OFF; + + pr_debug("%s: %s event = %d\n", __func__, w->name, event); + if (w->shift == 5) { + e_pre_on = WCD9XXX_EVENT_PRE_HPHL_PA_ON; + e_post_off = WCD9XXX_EVENT_POST_HPHL_PA_OFF; + req_clsh_state = WCD9XXX_CLSH_STATE_HPHL; + } else if (w->shift == 4) { + e_pre_on = WCD9XXX_EVENT_PRE_HPHR_PA_ON; + e_post_off = WCD9XXX_EVENT_POST_HPHR_PA_OFF; + req_clsh_state = WCD9XXX_CLSH_STATE_HPHR; + } else { + pr_err("%s: Invalid w->shift %d\n", __func__, w->shift); + return -EINVAL; + } + + if (tomtom->comp_enabled[COMPANDER_1]) + pa_settle_time = TOMTOM_HPH_PA_SETTLE_COMP_ON; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + set_bit(HPH_DELAY, &tomtom->status_mask); + /* Let MBHC module know PA is turning on */ + wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_pre_on); + break; + + case SND_SOC_DAPM_POST_PMU: + if (test_bit(HPH_DELAY, &tomtom->status_mask)) { + /* + * Make sure to wait 10ms after enabling HPHR_HPHL + * in register 0x1AB + */ + usleep_range(pa_settle_time, pa_settle_time + 1000); + clear_bit(HPH_DELAY, &tomtom->status_mask); + pr_debug("%s: sleep %d us after %s PA enable\n", + __func__, pa_settle_time, w->name); + } + if (!high_perf_mode && !tomtom->uhqa_mode) { + wcd9xxx_clsh_fsm(codec, &tomtom->clsh_d, + req_clsh_state, + WCD9XXX_CLSH_REQ_ENABLE, + WCD9XXX_CLSH_EVENT_POST_PA); + } + break; + + case SND_SOC_DAPM_PRE_PMD: + set_bit(HPH_DELAY, &tomtom->status_mask); + break; + + case SND_SOC_DAPM_POST_PMD: + /* Let MBHC module know PA turned off */ + wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_post_off); + if (test_bit(HPH_DELAY, &tomtom->status_mask)) { + /* + * Make sure to wait 10ms after disabling HPHR_HPHL + * in register 0x1AB + */ + usleep_range(pa_settle_time, pa_settle_time + 1000); + clear_bit(HPH_DELAY, &tomtom->status_mask); + pr_debug("%s: sleep %d us after %s PA disable\n", + __func__, pa_settle_time, w->name); + } + + break; + } + return 0; +} + +static int tomtom_codec_enable_anc_hph(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tomtom_hph_pa_event(w, kcontrol, event); + break; + case SND_SOC_DAPM_POST_PMU: + if ((snd_soc_read(codec, TOMTOM_A_RX_HPH_L_DAC_CTL) & 0x80) && + (snd_soc_read(codec, TOMTOM_A_RX_HPH_R_DAC_CTL) + & 0x80)) { + snd_soc_update_bits(codec, + TOMTOM_A_RX_HPH_CNP_EN, 0x30, 0x30); + msleep(30); + } + ret = tomtom_hph_pa_event(w, kcontrol, event); + break; + case SND_SOC_DAPM_PRE_PMD: + if (w->shift == 5) { + snd_soc_update_bits(codec, + TOMTOM_A_RX_HPH_CNP_EN, 0x30, 0x00); + msleep(40); + snd_soc_update_bits(codec, + TOMTOM_A_TX_7_MBHC_EN, 0x80, 00); + ret |= tomtom_codec_enable_anc(w, kcontrol, event); + } + break; + case SND_SOC_DAPM_POST_PMD: + ret = tomtom_hph_pa_event(w, kcontrol, event); + break; + } + return ret; +} + +static const struct snd_soc_dapm_widget tomtom_dapm_i2s_widgets[] = { + SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", TOMTOM_A_CDC_CLK_RX_I2S_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", TOMTOM_A_CDC_CLK_TX_I2S_CTL, 4, + 0, NULL, 0), +}; + +static int tomtom_lineout_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd9xxx_clsh_fsm(codec, &tomtom->clsh_d, + WCD9XXX_CLSH_STATE_LO, + WCD9XXX_CLSH_REQ_ENABLE, + WCD9XXX_CLSH_EVENT_PRE_DAC); + snd_soc_update_bits(codec, w->reg, 0x40, 0x40); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, w->reg, 0x40, 0x00); + wcd9xxx_clsh_fsm(codec, &tomtom->clsh_d, + WCD9XXX_CLSH_STATE_LO, + WCD9XXX_CLSH_REQ_DISABLE, + WCD9XXX_CLSH_EVENT_POST_PA); + break; + } + return 0; +} + +static int tomtom_spk_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL, + 0x80, 0x80); + break; + case SND_SOC_DAPM_POST_PMD: + if ((snd_soc_read(codec, w->reg) & 0x03) == 0) + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL, + 0x80, 0x00); + break; + } + return 0; +} + +static const struct snd_soc_dapm_route audio_i2s_map[] = { + {"SLIM RX1", NULL, "RX_I2S_CLK"}, + {"SLIM RX2", NULL, "RX_I2S_CLK"}, + {"SLIM RX3", NULL, "RX_I2S_CLK"}, + {"SLIM RX4", NULL, "RX_I2S_CLK"}, + + {"SLIM TX7 MUX", NULL, "TX_I2S_CLK"}, + {"SLIM TX8 MUX", NULL, "TX_I2S_CLK"}, + {"SLIM TX9 MUX", NULL, "TX_I2S_CLK"}, + {"SLIM TX10 MUX", NULL, "TX_I2S_CLK"}, + + {"RX_I2S_CLK", NULL, "CDC_I2S_RX_CONN"}, +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* SLIMBUS Connections */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + + /* VI Feedback */ + {"AIF4 VI", NULL, "VIONOFF"}, + {"VIONOFF", "Switch", "VIINPUT"}, + + /* MAD */ + {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"}, + {"MAD_SEL MUX", "MSM", "MADINPUT"}, + {"MADONOFF", "Switch", "MAD_SEL MUX"}, + {"AIF4 MAD", NULL, "MADONOFF"}, + + /* SLIM_MIXER("AIF1_CAP Mixer"),*/ + {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"}, + /* SLIM_MIXER("AIF2_CAP Mixer"),*/ + {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"}, + /* SLIM_MIXER("AIF3_CAP Mixer"),*/ + {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"}, + + {"SLIM TX1 MUX", "DEC1", "DEC1 MUX"}, + {"SLIM TX1 MUX", "RMIX1", "RX1 MIX1"}, + {"SLIM TX1 MUX", "RMIX2", "RX2 MIX1"}, + {"SLIM TX1 MUX", "RMIX3", "RX3 MIX1"}, + {"SLIM TX1 MUX", "RMIX4", "RX4 MIX1"}, + {"SLIM TX1 MUX", "RMIX5", "RX5 MIX1"}, + {"SLIM TX1 MUX", "RMIX6", "RX6 MIX1"}, + {"SLIM TX1 MUX", "RMIX7", "RX7 MIX1"}, + {"SLIM TX1 MUX", "RMIX8", "RX8 MIX1"}, + + {"SLIM TX2 MUX", "DEC2", "DEC2 MUX"}, + {"SLIM TX2 MUX", "RMIX1", "RX1 MIX1"}, + {"SLIM TX2 MUX", "RMIX2", "RX2 MIX1"}, + {"SLIM TX2 MUX", "RMIX3", "RX3 MIX1"}, + {"SLIM TX2 MUX", "RMIX4", "RX4 MIX1"}, + {"SLIM TX2 MUX", "RMIX5", "RX5 MIX1"}, + {"SLIM TX2 MUX", "RMIX6", "RX6 MIX1"}, + {"SLIM TX2 MUX", "RMIX7", "RX7 MIX1"}, + {"SLIM TX2 MUX", "RMIX8", "RX8 MIX1"}, + + {"SLIM TX3 MUX", "DEC3", "DEC3 MUX"}, + {"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"}, + {"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"}, + {"SLIM TX3 MUX", "RMIX3", "RX3 MIX1"}, + {"SLIM TX3 MUX", "RMIX4", "RX4 MIX1"}, + {"SLIM TX3 MUX", "RMIX5", "RX5 MIX1"}, + {"SLIM TX3 MUX", "RMIX6", "RX6 MIX1"}, + {"SLIM TX3 MUX", "RMIX7", "RX7 MIX1"}, + {"SLIM TX3 MUX", "RMIX8", "RX8 MIX1"}, + + {"SLIM TX4 MUX", "DEC4", "DEC4 MUX"}, + {"SLIM TX4 MUX", "RMIX1", "RX1 MIX1"}, + {"SLIM TX4 MUX", "RMIX2", "RX2 MIX1"}, + {"SLIM TX4 MUX", "RMIX3", "RX3 MIX1"}, + {"SLIM TX4 MUX", "RMIX4", "RX4 MIX1"}, + {"SLIM TX4 MUX", "RMIX5", "RX5 MIX1"}, + {"SLIM TX4 MUX", "RMIX6", "RX6 MIX1"}, + {"SLIM TX4 MUX", "RMIX7", "RX7 MIX1"}, + {"SLIM TX4 MUX", "RMIX8", "RX8 MIX1"}, + + {"SLIM TX5 MUX", "DEC5", "DEC5 MUX"}, + {"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"}, + {"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"}, + {"SLIM TX5 MUX", "RMIX3", "RX3 MIX1"}, + {"SLIM TX5 MUX", "RMIX4", "RX4 MIX1"}, + {"SLIM TX5 MUX", "RMIX5", "RX5 MIX1"}, + {"SLIM TX5 MUX", "RMIX6", "RX6 MIX1"}, + {"SLIM TX5 MUX", "RMIX7", "RX7 MIX1"}, + {"SLIM TX5 MUX", "RMIX8", "RX8 MIX1"}, + + {"SLIM TX6 MUX", "DEC6", "DEC6 MUX"}, + + {"SLIM TX7 MUX", "DEC1", "DEC1 MUX"}, + {"SLIM TX7 MUX", "DEC2", "DEC2 MUX"}, + {"SLIM TX7 MUX", "DEC3", "DEC3 MUX"}, + {"SLIM TX7 MUX", "DEC4", "DEC4 MUX"}, + {"SLIM TX7 MUX", "DEC5", "DEC5 MUX"}, + {"SLIM TX7 MUX", "DEC6", "DEC6 MUX"}, + {"SLIM TX7 MUX", "DEC7", "DEC7 MUX"}, + {"SLIM TX7 MUX", "DEC8", "DEC8 MUX"}, + {"SLIM TX7 MUX", "DEC9", "DEC9 MUX"}, + {"SLIM TX7 MUX", "DEC10", "DEC10 MUX"}, + {"SLIM TX7 MUX", "RMIX1", "RX1 MIX1"}, + {"SLIM TX7 MUX", "RMIX2", "RX2 MIX1"}, + {"SLIM TX7 MUX", "RMIX3", "RX3 MIX1"}, + {"SLIM TX7 MUX", "RMIX4", "RX4 MIX1"}, + {"SLIM TX7 MUX", "RMIX5", "RX5 MIX1"}, + {"SLIM TX7 MUX", "RMIX6", "RX6 MIX1"}, + {"SLIM TX7 MUX", "RMIX7", "RX7 MIX1"}, + + {"SLIM TX8 MUX", "DEC1", "DEC1 MUX"}, + {"SLIM TX8 MUX", "DEC2", "DEC2 MUX"}, + {"SLIM TX8 MUX", "DEC3", "DEC3 MUX"}, + {"SLIM TX8 MUX", "DEC4", "DEC4 MUX"}, + {"SLIM TX8 MUX", "DEC5", "DEC5 MUX"}, + {"SLIM TX8 MUX", "DEC6", "DEC6 MUX"}, + {"SLIM TX8 MUX", "DEC7", "DEC7 MUX"}, + {"SLIM TX8 MUX", "DEC8", "DEC8 MUX"}, + {"SLIM TX8 MUX", "DEC9", "DEC9 MUX"}, + {"SLIM TX8 MUX", "DEC10", "DEC10 MUX"}, + + {"SLIM TX9 MUX", "DEC1", "DEC1 MUX"}, + {"SLIM TX9 MUX", "DEC2", "DEC2 MUX"}, + {"SLIM TX9 MUX", "DEC3", "DEC3 MUX"}, + {"SLIM TX9 MUX", "DEC4", "DEC4 MUX"}, + {"SLIM TX9 MUX", "DEC5", "DEC5 MUX"}, + {"SLIM TX9 MUX", "DEC6", "DEC6 MUX"}, + {"SLIM TX9 MUX", "DEC7", "DEC7 MUX"}, + {"SLIM TX9 MUX", "DEC8", "DEC8 MUX"}, + {"SLIM TX9 MUX", "DEC9", "DEC9 MUX"}, + {"SLIM TX9 MUX", "DEC10", "DEC10 MUX"}, + + {"SLIM TX10 MUX", "DEC1", "DEC1 MUX"}, + {"SLIM TX10 MUX", "DEC2", "DEC2 MUX"}, + {"SLIM TX10 MUX", "DEC3", "DEC3 MUX"}, + {"SLIM TX10 MUX", "DEC4", "DEC4 MUX"}, + {"SLIM TX10 MUX", "DEC5", "DEC5 MUX"}, + {"SLIM TX10 MUX", "DEC6", "DEC6 MUX"}, + {"SLIM TX10 MUX", "DEC7", "DEC7 MUX"}, + {"SLIM TX10 MUX", "DEC8", "DEC8 MUX"}, + {"SLIM TX10 MUX", "DEC9", "DEC9 MUX"}, + {"SLIM TX10 MUX", "DEC10", "DEC10 MUX"}, + + /* Earpiece (RX MIX1) */ + {"EAR", NULL, "EAR PA"}, + {"EAR PA", NULL, "EAR_PA_MIXER"}, + {"EAR_PA_MIXER", NULL, "DAC1"}, + {"DAC1", NULL, "RX_BIAS"}, + + {"ANC EAR", NULL, "ANC EAR PA"}, + {"ANC EAR PA", NULL, "EAR_PA_MIXER"}, + {"ANC1 FB MUX", "EAR_HPH_L", "RX1 MIX2"}, + {"ANC1 FB MUX", "EAR_LINE_1", "RX2 MIX2"}, + + /* Headset (RX MIX1 and RX MIX2) */ + {"HEADPHONE", NULL, "HPHL"}, + {"HEADPHONE", NULL, "HPHR"}, + + {"HPHL", NULL, "HPHL_PA_MIXER"}, + {"HPHL_PA_MIXER", NULL, "HPHL DAC"}, + {"HPHL DAC", NULL, "RX_BIAS"}, + + {"HPHR", NULL, "HPHR_PA_MIXER"}, + {"HPHR_PA_MIXER", NULL, "HPHR DAC"}, + {"HPHR DAC", NULL, "RX_BIAS"}, + + {"ANC HEADPHONE", NULL, "ANC HPHL"}, + {"ANC HEADPHONE", NULL, "ANC HPHR"}, + + {"ANC HPHL", NULL, "HPHL_PA_MIXER"}, + {"ANC HPHR", NULL, "HPHR_PA_MIXER"}, + + {"ANC1 MUX", "ADC1", "ADC1"}, + {"ANC1 MUX", "ADC2", "ADC2"}, + {"ANC1 MUX", "ADC3", "ADC3"}, + {"ANC1 MUX", "ADC4", "ADC4"}, + {"ANC1 MUX", "ADC5", "ADC5"}, + {"ANC1 MUX", "ADC6", "ADC6"}, + {"ANC1 MUX", "DMIC1", "DMIC1"}, + {"ANC1 MUX", "DMIC2", "DMIC2"}, + {"ANC1 MUX", "DMIC3", "DMIC3"}, + {"ANC1 MUX", "DMIC4", "DMIC4"}, + {"ANC1 MUX", "DMIC5", "DMIC5"}, + {"ANC1 MUX", "DMIC6", "DMIC6"}, + {"ANC2 MUX", "ADC1", "ADC1"}, + {"ANC2 MUX", "ADC2", "ADC2"}, + {"ANC2 MUX", "ADC3", "ADC3"}, + {"ANC2 MUX", "ADC4", "ADC4"}, + {"ANC2 MUX", "ADC5", "ADC5"}, + {"ANC2 MUX", "ADC6", "ADC6"}, + {"ANC2 MUX", "DMIC1", "DMIC1"}, + {"ANC2 MUX", "DMIC2", "DMIC2"}, + {"ANC2 MUX", "DMIC3", "DMIC3"}, + {"ANC2 MUX", "DMIC4", "DMIC4"}, + {"ANC2 MUX", "DMIC5", "DMIC5"}, + {"ANC2 MUX", "DMIC6", "DMIC6"}, + + {"ANC HPHR", NULL, "CDC_CONN"}, + + {"DAC1", "Switch", "CLASS_H_DSM MUX"}, + {"HPHL DAC", "Switch", "CLASS_H_DSM MUX"}, + {"HPHR DAC", NULL, "RX2 CHAIN"}, + + {"LINEOUT1", NULL, "LINEOUT1 PA"}, + {"LINEOUT2", NULL, "LINEOUT2 PA"}, + {"LINEOUT3", NULL, "LINEOUT3 PA"}, + {"LINEOUT4", NULL, "LINEOUT4 PA"}, + {"SPK_OUT", NULL, "SPK PA"}, + {"SPK_OUT", NULL, "SPK2 PA"}, + + {"LINEOUT1 PA", NULL, "LINEOUT1_PA_MIXER"}, + {"LINEOUT1_PA_MIXER", NULL, "LINEOUT1 DAC"}, + + {"LINEOUT2 PA", NULL, "LINEOUT2_PA_MIXER"}, + {"LINEOUT2_PA_MIXER", NULL, "LINEOUT2 DAC"}, + + {"LINEOUT3 PA", NULL, "LINEOUT3_PA_MIXER"}, + {"LINEOUT3_PA_MIXER", NULL, "LINEOUT3 DAC"}, + + {"LINEOUT4 PA", NULL, "LINEOUT4_PA_MIXER"}, + {"LINEOUT4_PA_MIXER", NULL, "LINEOUT4 DAC"}, + + {"LINEOUT1 DAC", NULL, "RX3 MIX1"}, + + {"RDAC5 MUX", "DEM3_INV", "RX3 MIX1"}, + {"RDAC5 MUX", "DEM4", "RX4 MIX1"}, + + {"LINEOUT3 DAC", NULL, "RDAC5 MUX"}, + + {"LINEOUT2 DAC", NULL, "RX5 MIX1"}, + + {"RDAC7 MUX", "DEM5_INV", "RX5 MIX1"}, + {"RDAC7 MUX", "DEM6", "RX6 MIX1"}, + + {"LINEOUT4 DAC", NULL, "RDAC7 MUX"}, + + {"SPK PA", NULL, "SPK DAC"}, + {"SPK DAC", NULL, "RX7 MIX2"}, + {"SPK DAC", NULL, "VDD_SPKDRV"}, + + {"SPK2 PA", NULL, "SPK2 DAC"}, + {"SPK2 DAC", NULL, "RX8 MIX1"}, + {"SPK2 DAC", NULL, "VDD_SPKDRV2"}, + + {"CLASS_H_DSM MUX", "DSM_HPHL_RX1", "RX1 CHAIN"}, + + {"RX1 INTERP", NULL, "RX1 MIX2"}, + {"RX1 CHAIN", NULL, "RX1 INTERP"}, + {"RX2 INTERP", NULL, "RX2 MIX2"}, + {"RX2 CHAIN", NULL, "RX2 INTERP"}, + {"RX1 MIX2", NULL, "ANC1 MUX"}, + {"RX2 MIX2", NULL, "ANC2 MUX"}, + + {"LINEOUT1 DAC", NULL, "RX_BIAS"}, + {"LINEOUT2 DAC", NULL, "RX_BIAS"}, + {"LINEOUT3 DAC", NULL, "RX_BIAS"}, + {"LINEOUT4 DAC", NULL, "RX_BIAS"}, + {"SPK DAC", NULL, "RX_BIAS"}, + {"SPK2 DAC", NULL, "RX_BIAS"}, + + {"RX7 MIX1", NULL, "COMP0_CLK"}, + {"RX8 MIX1", NULL, "COMP0_CLK"}, + {"RX1 MIX1", NULL, "COMP1_CLK"}, + {"RX2 MIX1", NULL, "COMP1_CLK"}, + {"RX3 MIX1", NULL, "COMP2_CLK"}, + {"RX5 MIX1", NULL, "COMP2_CLK"}, + + {"RX1 MIX1", NULL, "RX1 MIX1 INP1"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP2"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP3"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP1"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP2"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP1"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP2"}, + {"RX4 MIX1", NULL, "RX4 MIX1 INP1"}, + {"RX4 MIX1", NULL, "RX4 MIX1 INP2"}, + {"RX5 MIX1", NULL, "RX5 MIX1 INP1"}, + {"RX5 MIX1", NULL, "RX5 MIX1 INP2"}, + {"RX6 MIX1", NULL, "RX6 MIX1 INP1"}, + {"RX6 MIX1", NULL, "RX6 MIX1 INP2"}, + {"RX7 MIX1", NULL, "RX7 MIX1 INP1"}, + {"RX7 MIX1", NULL, "RX7 MIX1 INP2"}, + {"RX8 MIX1", NULL, "RX8 MIX1 INP1"}, + {"RX8 MIX1", NULL, "RX8 MIX1 INP2"}, + {"RX1 MIX2", NULL, "RX1 MIX1"}, + {"RX1 MIX2", NULL, "RX1 MIX2 INP1"}, + {"RX1 MIX2", NULL, "RX1 MIX2 INP2"}, + {"RX2 MIX2", NULL, "RX2 MIX1"}, + {"RX2 MIX2", NULL, "RX2 MIX2 INP1"}, + {"RX2 MIX2", NULL, "RX2 MIX2 INP2"}, + {"RX7 MIX2", NULL, "RX7 MIX1"}, + {"RX7 MIX2", NULL, "RX7 MIX2 INP1"}, + {"RX7 MIX2", NULL, "RX7 MIX2 INP2"}, + + /* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/ + {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX8 MUX", "AIF1_PB", "AIF1 PB"}, + /* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/ + {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX8 MUX", "AIF2_PB", "AIF2 PB"}, + /* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/ + {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX8 MUX", "AIF3_PB", "AIF3 PB"}, + + {"SLIM RX1", NULL, "SLIM RX1 MUX"}, + {"SLIM RX2", NULL, "SLIM RX2 MUX"}, + {"SLIM RX3", NULL, "SLIM RX3 MUX"}, + {"SLIM RX4", NULL, "SLIM RX4 MUX"}, + {"SLIM RX5", NULL, "SLIM RX5 MUX"}, + {"SLIM RX6", NULL, "SLIM RX6 MUX"}, + {"SLIM RX7", NULL, "SLIM RX7 MUX"}, + {"SLIM RX8", NULL, "SLIM RX8 MUX"}, + + {"RX1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX1 MIX1 INP1", "IIR2", "IIR2"}, + {"RX1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX1 MIX1 INP2", "IIR2", "IIR2"}, + {"RX1 MIX1 INP3", "RX1", "SLIM RX1"}, + {"RX1 MIX1 INP3", "RX2", "SLIM RX2"}, + {"RX1 MIX1 INP3", "RX3", "SLIM RX3"}, + {"RX1 MIX1 INP3", "RX4", "SLIM RX4"}, + {"RX1 MIX1 INP3", "RX5", "SLIM RX5"}, + {"RX1 MIX1 INP3", "RX6", "SLIM RX6"}, + {"RX1 MIX1 INP3", "RX7", "SLIM RX7"}, + {"RX2 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX2 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX2 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX2 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX2 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX2 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX2 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX2 MIX1 INP1", "IIR1", "IIR1"}, + {"RX2 MIX1 INP1", "IIR2", "IIR2"}, + {"RX2 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX2 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX2 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX2 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX2 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX2 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX2 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX2 MIX1 INP2", "IIR1", "IIR1"}, + {"RX2 MIX1 INP2", "IIR2", "IIR2"}, + {"RX3 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX3 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX3 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX3 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX3 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX3 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX3 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX3 MIX1 INP1", "IIR1", "IIR1"}, + {"RX3 MIX1 INP1", "IIR2", "IIR2"}, + {"RX3 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX3 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX3 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX3 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX3 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX3 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX3 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX3 MIX1 INP2", "IIR1", "IIR1"}, + {"RX3 MIX1 INP2", "IIR2", "IIR2"}, + {"RX4 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX4 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX4 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX4 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX4 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX4 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX4 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX4 MIX1 INP1", "IIR1", "IIR1"}, + {"RX4 MIX1 INP1", "IIR2", "IIR2"}, + {"RX4 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX4 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX4 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX4 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX4 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX4 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX4 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX4 MIX1 INP2", "IIR1", "IIR1"}, + {"RX4 MIX1 INP2", "IIR2", "IIR2"}, + {"RX5 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX5 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX5 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX5 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX5 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX5 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX5 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX5 MIX1 INP1", "IIR1", "IIR1"}, + {"RX5 MIX1 INP1", "IIR2", "IIR2"}, + {"RX5 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX5 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX5 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX5 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX5 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX5 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX5 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX5 MIX1 INP2", "IIR1", "IIR1"}, + {"RX5 MIX1 INP2", "IIR2", "IIR2"}, + {"RX6 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX6 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX6 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX6 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX6 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX6 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX6 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX6 MIX1 INP1", "IIR1", "IIR1"}, + {"RX6 MIX1 INP1", "IIR2", "IIR2"}, + {"RX6 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX6 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX6 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX6 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX6 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX6 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX6 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX6 MIX1 INP2", "IIR1", "IIR1"}, + {"RX6 MIX1 INP2", "IIR2", "IIR2"}, + {"RX7 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX7 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX7 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX7 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX7 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX7 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX7 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX7 MIX1 INP1", "IIR1", "IIR1"}, + {"RX7 MIX1 INP1", "IIR2", "IIR2"}, + {"RX7 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX7 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX7 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX7 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX7 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX7 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX7 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX7 MIX1 INP2", "IIR1", "IIR1"}, + {"RX7 MIX1 INP2", "IIR2", "IIR2"}, + {"RX8 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX8 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX8 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX8 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX8 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX8 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX8 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX8 MIX1 INP1", "RX8", "SLIM RX8"}, + {"RX8 MIX1 INP1", "IIR1", "IIR1"}, + {"RX8 MIX1 INP1", "IIR2", "IIR2"}, + {"RX8 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX8 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX8 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX8 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX8 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX8 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX8 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX8 MIX1 INP2", "RX8", "SLIM RX8"}, + {"RX8 MIX1 INP2", "IIR1", "IIR1"}, + {"RX8 MIX1 INP2", "IIR2", "IIR2"}, + + /* IIR1, IIR2 inputs to Second RX Mixer on RX1, RX2 and RX7 chains. */ + {"RX1 MIX2 INP1", "IIR1", "IIR1"}, + {"RX1 MIX2 INP2", "IIR1", "IIR1"}, + {"RX2 MIX2 INP1", "IIR1", "IIR1"}, + {"RX2 MIX2 INP2", "IIR1", "IIR1"}, + {"RX7 MIX2 INP1", "IIR1", "IIR1"}, + {"RX7 MIX2 INP2", "IIR1", "IIR1"}, + {"RX1 MIX2 INP1", "IIR2", "IIR2"}, + {"RX1 MIX2 INP2", "IIR2", "IIR2"}, + {"RX2 MIX2 INP1", "IIR2", "IIR2"}, + {"RX2 MIX2 INP2", "IIR2", "IIR2"}, + {"RX7 MIX2 INP1", "IIR2", "IIR2"}, + {"RX7 MIX2 INP2", "IIR2", "IIR2"}, + + /* Decimator Inputs */ + {"DEC1 MUX", "DMIC1", "DMIC1"}, + {"DEC1 MUX", "ADC6", "ADC6"}, + {"DEC1 MUX", NULL, "CDC_CONN"}, + {"DEC2 MUX", "DMIC2", "DMIC2"}, + {"DEC2 MUX", "ADC5", "ADC5"}, + {"DEC2 MUX", NULL, "CDC_CONN"}, + {"DEC3 MUX", "DMIC3", "DMIC3"}, + {"DEC3 MUX", "ADC4", "ADC4"}, + {"DEC3 MUX", NULL, "CDC_CONN"}, + {"DEC4 MUX", "DMIC4", "DMIC4"}, + {"DEC4 MUX", "ADC3", "ADC3"}, + {"DEC4 MUX", NULL, "CDC_CONN"}, + {"DEC5 MUX", "DMIC5", "DMIC5"}, + {"DEC5 MUX", "ADC2", "ADC2"}, + {"DEC5 MUX", NULL, "CDC_CONN"}, + {"DEC6 MUX", "DMIC6", "DMIC6"}, + {"DEC6 MUX", "ADC1", "ADC1"}, + {"DEC6 MUX", NULL, "CDC_CONN"}, + {"DEC7 MUX", "DMIC1", "DMIC1"}, + {"DEC7 MUX", "DMIC6", "DMIC6"}, + {"DEC7 MUX", "ADC1", "ADC1"}, + {"DEC7 MUX", "ADC6", "ADC6"}, + {"DEC7 MUX", "ANC1_FB", "ANC1 MUX"}, + {"DEC7 MUX", "ANC2_FB", "ANC2 MUX"}, + {"DEC7 MUX", NULL, "CDC_CONN"}, + {"DEC8 MUX", "DMIC2", "DMIC2"}, + {"DEC8 MUX", "DMIC5", "DMIC5"}, + {"DEC8 MUX", "ADC2", "ADC2"}, + {"DEC8 MUX", "ADC5", "ADC5"}, + {"DEC8 MUX", "ANC1_FB", "ANC1 MUX"}, + {"DEC8 MUX", "ANC2_FB", "ANC2 MUX"}, + {"DEC8 MUX", NULL, "CDC_CONN"}, + {"DEC9 MUX", "DMIC4", "DMIC4"}, + {"DEC9 MUX", "DMIC5", "DMIC5"}, + {"DEC9 MUX", "ADC2", "ADC2"}, + {"DEC9 MUX", "ADC3", "ADC3"}, + {"DEC9 MUX", "ANC1_FB", "ANC1 MUX"}, + {"DEC9 MUX", "ANC2_FB", "ANC2 MUX"}, + {"DEC9 MUX", NULL, "CDC_CONN"}, + {"DEC10 MUX", "DMIC3", "DMIC3"}, + {"DEC10 MUX", "DMIC6", "DMIC6"}, + {"DEC10 MUX", "ADC1", "ADC1"}, + {"DEC10 MUX", "ADC4", "ADC4"}, + {"DEC10 MUX", "ANC1_FB", "ANC1 MUX"}, + {"DEC10 MUX", "ANC2_FB", "ANC2 MUX"}, + {"DEC10 MUX", NULL, "CDC_CONN"}, + + /* ADC Connections */ + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "AMIC2"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "AMIC4"}, + {"ADC5", NULL, "AMIC5"}, + {"ADC6", NULL, "AMIC6"}, + + /* AUX PGA Connections */ + {"EAR_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"}, + {"HPHL_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"}, + {"HPHR_PA_MIXER", "AUX_PGA_R Switch", "AUX_PGA_Right"}, + {"LINEOUT1_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"}, + {"LINEOUT2_PA_MIXER", "AUX_PGA_R Switch", "AUX_PGA_Right"}, + {"LINEOUT3_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"}, + {"LINEOUT4_PA_MIXER", "AUX_PGA_R Switch", "AUX_PGA_Right"}, + {"AUX_PGA_Left", NULL, "AMIC5"}, + {"AUX_PGA_Right", NULL, "AMIC6"}, + + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"}, + {"IIR1 INP1 MUX", "DEC3", "DEC3 MUX"}, + {"IIR1 INP1 MUX", "DEC4", "DEC4 MUX"}, + {"IIR1 INP1 MUX", "DEC5", "DEC5 MUX"}, + {"IIR1 INP1 MUX", "DEC6", "DEC6 MUX"}, + {"IIR1 INP1 MUX", "DEC7", "DEC7 MUX"}, + {"IIR1 INP1 MUX", "DEC8", "DEC8 MUX"}, + {"IIR1 INP1 MUX", "DEC9", "DEC9 MUX"}, + {"IIR1 INP1 MUX", "DEC10", "DEC10 MUX"}, + {"IIR1 INP1 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP1 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP1 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP1 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP1 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP1 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP1 MUX", "RX7", "SLIM RX7"}, + + {"IIR2", NULL, "IIR2 INP1 MUX"}, + {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"}, + {"IIR2 INP1 MUX", "DEC3", "DEC3 MUX"}, + {"IIR2 INP1 MUX", "DEC4", "DEC4 MUX"}, + {"IIR2 INP1 MUX", "DEC5", "DEC5 MUX"}, + {"IIR2 INP1 MUX", "DEC6", "DEC6 MUX"}, + {"IIR2 INP1 MUX", "DEC7", "DEC7 MUX"}, + {"IIR2 INP1 MUX", "DEC8", "DEC8 MUX"}, + {"IIR2 INP1 MUX", "DEC9", "DEC9 MUX"}, + {"IIR2 INP1 MUX", "DEC10", "DEC10 MUX"}, + {"IIR2 INP1 MUX", "RX1", "SLIM RX1"}, + {"IIR2 INP1 MUX", "RX2", "SLIM RX2"}, + {"IIR2 INP1 MUX", "RX3", "SLIM RX3"}, + {"IIR2 INP1 MUX", "RX4", "SLIM RX4"}, + {"IIR2 INP1 MUX", "RX5", "SLIM RX5"}, + {"IIR2 INP1 MUX", "RX6", "SLIM RX6"}, + {"IIR2 INP1 MUX", "RX7", "SLIM RX7"}, + + {"IIR1", NULL, "IIR1 INP2 MUX"}, + {"IIR1 INP2 MUX", "DEC1", "DEC1 MUX"}, + {"IIR1 INP2 MUX", "DEC2", "DEC2 MUX"}, + {"IIR1 INP2 MUX", "DEC3", "DEC3 MUX"}, + {"IIR1 INP2 MUX", "DEC4", "DEC4 MUX"}, + {"IIR1 INP2 MUX", "DEC5", "DEC5 MUX"}, + {"IIR1 INP2 MUX", "DEC6", "DEC6 MUX"}, + {"IIR1 INP2 MUX", "DEC7", "DEC7 MUX"}, + {"IIR1 INP2 MUX", "DEC8", "DEC8 MUX"}, + {"IIR1 INP2 MUX", "DEC9", "DEC9 MUX"}, + {"IIR1 INP2 MUX", "DEC10", "DEC10 MUX"}, + {"IIR1 INP2 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP2 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP2 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP2 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP2 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP2 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP2 MUX", "RX7", "SLIM RX7"}, + + {"IIR2", NULL, "IIR2 INP2 MUX"}, + {"IIR2 INP2 MUX", "DEC1", "DEC1 MUX"}, + {"IIR2 INP2 MUX", "DEC2", "DEC2 MUX"}, + {"IIR2 INP2 MUX", "DEC3", "DEC3 MUX"}, + {"IIR2 INP2 MUX", "DEC4", "DEC4 MUX"}, + {"IIR2 INP2 MUX", "DEC5", "DEC5 MUX"}, + {"IIR2 INP2 MUX", "DEC6", "DEC6 MUX"}, + {"IIR2 INP2 MUX", "DEC7", "DEC7 MUX"}, + {"IIR2 INP2 MUX", "DEC8", "DEC8 MUX"}, + {"IIR2 INP2 MUX", "DEC9", "DEC9 MUX"}, + {"IIR2 INP2 MUX", "DEC10", "DEC10 MUX"}, + {"IIR2 INP2 MUX", "RX1", "SLIM RX1"}, + {"IIR2 INP2 MUX", "RX2", "SLIM RX2"}, + {"IIR2 INP2 MUX", "RX3", "SLIM RX3"}, + {"IIR2 INP2 MUX", "RX4", "SLIM RX4"}, + {"IIR2 INP2 MUX", "RX5", "SLIM RX5"}, + {"IIR2 INP2 MUX", "RX6", "SLIM RX6"}, + {"IIR2 INP2 MUX", "RX7", "SLIM RX7"}, + + {"IIR1", NULL, "IIR1 INP3 MUX"}, + {"IIR1 INP3 MUX", "DEC1", "DEC1 MUX"}, + {"IIR1 INP3 MUX", "DEC2", "DEC2 MUX"}, + {"IIR1 INP3 MUX", "DEC3", "DEC3 MUX"}, + {"IIR1 INP3 MUX", "DEC4", "DEC4 MUX"}, + {"IIR1 INP3 MUX", "DEC5", "DEC5 MUX"}, + {"IIR1 INP3 MUX", "DEC6", "DEC6 MUX"}, + {"IIR1 INP3 MUX", "DEC7", "DEC7 MUX"}, + {"IIR1 INP3 MUX", "DEC8", "DEC8 MUX"}, + {"IIR1 INP3 MUX", "DEC9", "DEC9 MUX"}, + {"IIR1 INP3 MUX", "DEC10", "DEC10 MUX"}, + {"IIR1 INP3 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP3 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP3 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP3 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP3 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP3 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP3 MUX", "RX7", "SLIM RX7"}, + + {"IIR2", NULL, "IIR2 INP3 MUX"}, + {"IIR2 INP3 MUX", "DEC1", "DEC1 MUX"}, + {"IIR2 INP3 MUX", "DEC2", "DEC2 MUX"}, + {"IIR2 INP3 MUX", "DEC3", "DEC3 MUX"}, + {"IIR2 INP3 MUX", "DEC4", "DEC4 MUX"}, + {"IIR2 INP3 MUX", "DEC5", "DEC5 MUX"}, + {"IIR2 INP3 MUX", "DEC6", "DEC6 MUX"}, + {"IIR2 INP3 MUX", "DEC7", "DEC7 MUX"}, + {"IIR2 INP3 MUX", "DEC8", "DEC8 MUX"}, + {"IIR2 INP3 MUX", "DEC9", "DEC9 MUX"}, + {"IIR2 INP3 MUX", "DEC10", "DEC10 MUX"}, + {"IIR2 INP3 MUX", "RX1", "SLIM RX1"}, + {"IIR2 INP3 MUX", "RX2", "SLIM RX2"}, + {"IIR2 INP3 MUX", "RX3", "SLIM RX3"}, + {"IIR2 INP3 MUX", "RX4", "SLIM RX4"}, + {"IIR2 INP3 MUX", "RX5", "SLIM RX5"}, + {"IIR2 INP3 MUX", "RX6", "SLIM RX6"}, + {"IIR2 INP3 MUX", "RX7", "SLIM RX7"}, + + {"IIR1", NULL, "IIR1 INP4 MUX"}, + {"IIR1 INP4 MUX", "DEC1", "DEC1 MUX"}, + {"IIR1 INP4 MUX", "DEC2", "DEC2 MUX"}, + {"IIR1 INP4 MUX", "DEC3", "DEC3 MUX"}, + {"IIR1 INP4 MUX", "DEC4", "DEC4 MUX"}, + {"IIR1 INP4 MUX", "DEC5", "DEC5 MUX"}, + {"IIR1 INP4 MUX", "DEC6", "DEC6 MUX"}, + {"IIR1 INP4 MUX", "DEC7", "DEC7 MUX"}, + {"IIR1 INP4 MUX", "DEC8", "DEC8 MUX"}, + {"IIR1 INP4 MUX", "DEC9", "DEC9 MUX"}, + {"IIR1 INP4 MUX", "DEC10", "DEC10 MUX"}, + {"IIR1 INP4 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP4 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP4 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP4 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP4 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP4 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP4 MUX", "RX7", "SLIM RX7"}, + + {"IIR2", NULL, "IIR2 INP4 MUX"}, + {"IIR2 INP4 MUX", "DEC1", "DEC1 MUX"}, + {"IIR2 INP4 MUX", "DEC2", "DEC2 MUX"}, + {"IIR2 INP4 MUX", "DEC3", "DEC3 MUX"}, + {"IIR2 INP4 MUX", "DEC4", "DEC4 MUX"}, + {"IIR2 INP4 MUX", "DEC5", "DEC5 MUX"}, + {"IIR2 INP4 MUX", "DEC6", "DEC6 MUX"}, + {"IIR2 INP4 MUX", "DEC7", "DEC7 MUX"}, + {"IIR2 INP4 MUX", "DEC8", "DEC8 MUX"}, + {"IIR2 INP4 MUX", "DEC9", "DEC9 MUX"}, + {"IIR2 INP4 MUX", "DEC10", "DEC10 MUX"}, + {"IIR2 INP4 MUX", "RX1", "SLIM RX1"}, + {"IIR2 INP4 MUX", "RX2", "SLIM RX2"}, + {"IIR2 INP4 MUX", "RX3", "SLIM RX3"}, + {"IIR2 INP4 MUX", "RX4", "SLIM RX4"}, + {"IIR2 INP4 MUX", "RX5", "SLIM RX5"}, + {"IIR2 INP4 MUX", "RX6", "SLIM RX6"}, + {"IIR2 INP4 MUX", "RX7", "SLIM RX7"}, + + {"MIC BIAS1 Internal1", NULL, "LDO_H"}, + {"MIC BIAS1 Internal2", NULL, "LDO_H"}, + {"MIC BIAS1 External", NULL, "LDO_H"}, + {"MIC BIAS2 Internal1", NULL, "LDO_H"}, + {"MIC BIAS2 Internal2", NULL, "LDO_H"}, + {"MIC BIAS2 Internal3", NULL, "LDO_H"}, + {"MIC BIAS2 External", NULL, "LDO_H"}, + {"MIC BIAS3 Internal1", NULL, "LDO_H"}, + {"MIC BIAS3 Internal2", NULL, "LDO_H"}, + {"MIC BIAS3 External", NULL, "LDO_H"}, + {"MIC BIAS4 External", NULL, "LDO_H"}, + {DAPM_MICBIAS2_EXTERNAL_STANDALONE, NULL, "LDO_H Standalone"}, +}; + +static int tomtom_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + return 0; +} + +static void tomtom_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); +} + +int tomtom_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, bool dapm) +{ + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: mclk_enable = %u, dapm = %d\n", __func__, mclk_enable, + dapm); + + WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr); + if (mclk_enable) { + wcd9xxx_resmgr_get_bandgap(&tomtom->resmgr, + WCD9XXX_BANDGAP_AUDIO_MODE); + wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr, WCD9XXX_CLK_MCLK); + } else { + /* Put clock and BG */ + wcd9xxx_resmgr_put_clk_block(&tomtom->resmgr, WCD9XXX_CLK_MCLK); + wcd9xxx_resmgr_put_bandgap(&tomtom->resmgr, + WCD9XXX_BANDGAP_AUDIO_MODE); + } + WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr); + + return 0; +} + +static int tomtom_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static int tomtom_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + u8 val = 0; + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(dai->codec); + + pr_debug("%s\n", __func__); + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* CPU is master */ + if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + if (dai->id == AIF1_CAP) + snd_soc_update_bits(dai->codec, + TOMTOM_A_CDC_CLK_TX_I2S_CTL, + TOMTOM_I2S_MASTER_MODE_MASK, 0); + else if (dai->id == AIF1_PB) + snd_soc_update_bits(dai->codec, + TOMTOM_A_CDC_CLK_RX_I2S_CTL, + TOMTOM_I2S_MASTER_MODE_MASK, 0); + } + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* CPU is slave */ + if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + val = TOMTOM_I2S_MASTER_MODE_MASK; + if (dai->id == AIF1_CAP) + snd_soc_update_bits(dai->codec, + TOMTOM_A_CDC_CLK_TX_I2S_CTL, val, val); + else if (dai->id == AIF1_PB) + snd_soc_update_bits(dai->codec, + TOMTOM_A_CDC_CLK_RX_I2S_CTL, val, val); + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int tomtom_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 wcd9xxx_codec_dai_data *dai_data = NULL; + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(dai->codec); + struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent); + + if (!tx_slot || !rx_slot) { + pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n", + __func__, tx_slot, rx_slot); + return -EINVAL; + } + pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n" + "tomtom->intf_type %d\n", + __func__, dai->name, dai->id, tx_num, rx_num, + tomtom->intf_type); + + if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + wcd9xxx_init_slimslave(core, core->slim->laddr, + tx_num, tx_slot, rx_num, rx_slot); + /*Reserve tx11 and tx12 for VI feedback path*/ + dai_data = &tomtom->dai[AIF4_VIFEED]; + if (dai_data) { + list_add_tail(&core->tx_chs[TOMTOM_TX11].list, + &dai_data->wcd9xxx_ch_list); + list_add_tail(&core->tx_chs[TOMTOM_TX12].list, + &dai_data->wcd9xxx_ch_list); + } + + /* Reserve TX13 for MAD data channel */ + dai_data = &tomtom->dai[AIF4_MAD_TX]; + if (dai_data) + list_add_tail(&core->tx_chs[TOMTOM_TX13].list, + &dai_data->wcd9xxx_ch_list); + } + + return 0; +} + +static int tomtom_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 tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(dai->codec); + u32 i = 0; + struct wcd9xxx_ch *ch; + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + if (!rx_slot || !rx_num) { + pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n", + __func__, rx_slot, rx_num); + return -EINVAL; + } + list_for_each_entry(ch, &tomtom_p->dai[dai->id].wcd9xxx_ch_list, + list) { + pr_debug("%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + rx_slot[i++] = ch->ch_num; + } + pr_debug("%s: rx_num %d\n", __func__, i); + *rx_num = i; + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + case AIF4_VIFEED: + case AIF4_MAD_TX: + if (!tx_slot || !tx_num) { + pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n", + __func__, tx_slot, tx_num); + return -EINVAL; + } + list_for_each_entry(ch, &tomtom_p->dai[dai->id].wcd9xxx_ch_list, + list) { + pr_debug("%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + tx_slot[i++] = ch->ch_num; + } + pr_debug("%s: tx_num %d\n", __func__, i); + *tx_num = i; + break; + + default: + pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id); + break; + } + + return 0; +} + +static int tomtom_set_interpolator_rate(struct snd_soc_dai *dai, + u8 rx_fs_rate_reg_val, u32 compander_fs, u32 sample_rate) +{ + u32 j; + u8 rx_mix1_inp, rx8_mix1_inp; + u16 rx_mix_1_reg_1, rx_mix_1_reg_2; + u16 rx_fs_reg; + u8 rx_mix_1_reg_1_val, rx_mix_1_reg_2_val; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + int port_rx_8 = TOMTOM_RX_PORT_START_NUMBER + NUM_INTERPOLATORS - 1; + + list_for_each_entry(ch, &tomtom->dai[dai->id].wcd9xxx_ch_list, list) { + /* for RX port starting from 16 instead of 10 like tabla */ + rx_mix1_inp = ch->port + RX_MIX1_INP_SEL_RX1 - + TOMTOM_TX_PORT_NUMBER; + rx8_mix1_inp = ch->port + RX8_MIX1_INP_SEL_RX1 - + TOMTOM_RX_PORT_START_NUMBER; + if (((ch->port < port_rx_8) && + ((rx_mix1_inp < RX_MIX1_INP_SEL_RX1) || + (rx_mix1_inp > RX_MIX1_INP_SEL_RX7))) || + ((rx8_mix1_inp < RX8_MIX1_INP_SEL_RX1) || + (rx8_mix1_inp > RX8_MIX1_INP_SEL_RX8))) { + pr_err("%s: Invalid TOMTOM_RX%u port. Dai ID is %d\n", + __func__, rx8_mix1_inp - 2, + dai->id); + return -EINVAL; + } + + rx_mix_1_reg_1 = TOMTOM_A_CDC_CONN_RX1_B1_CTL; + + for (j = 0; j < NUM_INTERPOLATORS - 1; j++) { + rx_mix_1_reg_2 = rx_mix_1_reg_1 + 1; + + rx_mix_1_reg_1_val = snd_soc_read(codec, + rx_mix_1_reg_1); + rx_mix_1_reg_2_val = snd_soc_read(codec, + rx_mix_1_reg_2); + + if (((rx_mix_1_reg_1_val & 0x0F) == rx_mix1_inp) || + (((rx_mix_1_reg_1_val >> 4) & 0x0F) + == rx_mix1_inp) || + ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) { + + rx_fs_reg = TOMTOM_A_CDC_RX1_B5_CTL + 8 * j; + + pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n", + __func__, dai->id, j + 1); + + pr_debug("%s: set RX%u sample rate to %u\n", + __func__, j + 1, sample_rate); + + snd_soc_update_bits(codec, rx_fs_reg, + 0xE0, rx_fs_rate_reg_val); + + if (comp_rx_path[j] < COMPANDER_MAX) + tomtom->comp_fs[comp_rx_path[j]] + = compander_fs; + } + if (j < 2) + rx_mix_1_reg_1 += 3; + else + rx_mix_1_reg_1 += 2; + } + + /* RX8 interpolator path */ + rx_mix_1_reg_1_val = snd_soc_read(codec, + TOMTOM_A_CDC_CONN_RX8_B1_CTL); + if (((rx_mix_1_reg_1_val & 0x0F) == rx8_mix1_inp) || + (((rx_mix_1_reg_1_val >> 4) & 0x0F) == rx8_mix1_inp)) { + snd_soc_update_bits(codec, TOMTOM_A_CDC_RX8_B5_CTL, + 0xE0, rx_fs_rate_reg_val); + pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n", + __func__, dai->id, NUM_INTERPOLATORS); + + pr_debug("%s: set RX%u sample rate to %u\n", + __func__, NUM_INTERPOLATORS, + sample_rate); + if (comp_rx_path[NUM_INTERPOLATORS - 1] < COMPANDER_MAX) + tomtom->comp_fs[comp_rx_path[j]] = + compander_fs; + } + } + return 0; +} + +static int tomtom_set_decimator_rate(struct snd_soc_dai *dai, + u8 tx_fs_rate_reg_val, u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + u32 tx_port; + u16 tx_port_reg, tx_fs_reg; + u8 tx_port_reg_val; + s8 decimator; + + list_for_each_entry(ch, &tomtom->dai[dai->id].wcd9xxx_ch_list, list) { + + tx_port = ch->port + 1; + pr_debug("%s: dai->id = %d, tx_port = %d", + __func__, dai->id, tx_port); + + if ((tx_port < 1) || (tx_port > NUM_DECIMATORS)) { + pr_err("%s: Invalid SLIM TX%u port. DAI ID is %d\n", + __func__, tx_port, dai->id); + return -EINVAL; + } + + tx_port_reg = TOMTOM_A_CDC_CONN_TX_SB_B1_CTL + (tx_port - 1); + tx_port_reg_val = snd_soc_read(codec, tx_port_reg); + + decimator = 0; + + if ((tx_port >= 1) && (tx_port <= 6)) { + + tx_port_reg_val = tx_port_reg_val & 0x0F; + if (tx_port_reg_val == 0x8) + decimator = tx_port; + + } else if ((tx_port >= 7) && (tx_port <= NUM_DECIMATORS)) { + + tx_port_reg_val = tx_port_reg_val & 0x1F; + + if ((tx_port_reg_val >= 0x8) && + (tx_port_reg_val <= 0x11)) { + + decimator = (tx_port_reg_val - 0x8) + 1; + } + } + + if (decimator) { /* SLIM_TX port has a DEC as input */ + + tx_fs_reg = TOMTOM_A_CDC_TX1_CLK_FS_CTL + + 8 * (decimator - 1); + + pr_debug("%s: set DEC%u (-> SLIM_TX%u) rate to %u\n", + __func__, decimator, tx_port, sample_rate); + + snd_soc_update_bits(codec, tx_fs_reg, 0x07, + tx_fs_rate_reg_val); + + } else { + if ((tx_port_reg_val >= 0x1) && + (tx_port_reg_val <= 0x7)) { + + pr_debug("%s: RMIX%u going to SLIM TX%u\n", + __func__, tx_port_reg_val, tx_port); + + } else if ((tx_port_reg_val >= 0x8) && + (tx_port_reg_val <= 0x11)) { + + pr_err("%s: ERROR: Should not be here\n", + __func__); + pr_err("%s: ERROR: DEC connected to SLIM TX%u\n", + __func__, tx_port); + return -EINVAL; + + } else if (tx_port_reg_val == 0) { + pr_debug("%s: no signal to SLIM TX%u\n", + __func__, tx_port); + } else { + pr_err("%s: ERROR: wrong signal to SLIM TX%u\n", + __func__, tx_port); + pr_err("%s: ERROR: wrong signal = %u\n", + __func__, tx_port_reg_val); + return -EINVAL; + } + } + } + return 0; +} + +static void tomtom_set_rxsb_port_format(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *cdc_dai; + struct wcd9xxx_ch *ch; + int port; + u8 bit_sel; + u16 sb_ctl_reg, field_shift; + + switch (params_width(params)) { + case 16: + bit_sel = 0x2; + tomtom_p->dai[dai->id].bit_width = 16; + break; + case 24: + bit_sel = 0x0; + tomtom_p->dai[dai->id].bit_width = 24; + break; + default: + dev_err(codec->dev, "Invalid format\n"); + return; + } + + cdc_dai = &tomtom_p->dai[dai->id]; + + list_for_each_entry(ch, &cdc_dai->wcd9xxx_ch_list, list) { + port = wcd9xxx_get_slave_port(ch->ch_num); + + if (IS_ERR_VALUE(port) || + !TOMTOM_VALIDATE_RX_SBPORT_RANGE(port)) { + dev_warn(codec->dev, + "%s: invalid port ID %d returned for RX DAI\n", + __func__, port); + return; + } + + port = TOMTOM_CONVERT_RX_SBPORT_ID(port); + + if (port <= 3) { + sb_ctl_reg = TOMTOM_A_CDC_CONN_RX_SB_B1_CTL; + field_shift = port << 1; + } else if (port <= 7) { + sb_ctl_reg = TOMTOM_A_CDC_CONN_RX_SB_B2_CTL; + field_shift = (port - 4) << 1; + } else { /* should not happen */ + dev_warn(codec->dev, + "%s: bad port ID %d\n", __func__, port); + return; + } + + dev_dbg(codec->dev, "%s: sb_ctl_reg %x field_shift %x\n", + __func__, sb_ctl_reg, field_shift); + snd_soc_update_bits(codec, sb_ctl_reg, 0x3 << field_shift, + bit_sel << field_shift); + } +} + +static void tomtom_set_tx_sb_port_format(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *cdc_dai; + struct wcd9xxx_ch *ch; + int port; + u8 bit_sel, bit_shift; + u16 sb_ctl_reg; + + switch (params_width(params)) { + case 16: + bit_sel = 0x2; + tomtom_p->dai[dai->id].bit_width = 16; + break; + case 24: + bit_sel = 0x0; + tomtom_p->dai[dai->id].bit_width = 24; + break; + default: + dev_err(codec->dev, "%s: Invalid format %d\n", __func__, + params_width(params)); + return; + } + + cdc_dai = &tomtom_p->dai[dai->id]; + + list_for_each_entry(ch, &cdc_dai->wcd9xxx_ch_list, list) { + port = wcd9xxx_get_slave_port(ch->ch_num); + + if (IS_ERR_VALUE(port) || + !TOMTOM_VALIDATE_TX_SBPORT_RANGE(port)) { + dev_warn(codec->dev, + "%s: invalid port ID %d returned for TX DAI\n", + __func__, port); + return; + } + + if (port < 6) /* 6 = SLIMBUS TX7 */ + bit_shift = TOMTOM_BIT_ADJ_SHIFT_PORT1_6; + else if (port < 10) + bit_shift = TOMTOM_BIT_ADJ_SHIFT_PORT7_10; + else { + dev_warn(codec->dev, + "%s: port ID %d bitwidth is fixed\n", + __func__, port); + return; + } + + sb_ctl_reg = (TOMTOM_A_CDC_CONN_TX_SB_B1_CTL + port); + + dev_dbg(codec->dev, "%s: reg %x bit_sel %x bit_shift %x\n", + __func__, sb_ctl_reg, bit_sel, bit_shift); + snd_soc_update_bits(codec, sb_ctl_reg, 0x3 << + bit_shift, bit_sel << bit_shift); + } +} + +static int tomtom_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(dai->codec); + u8 tx_fs_rate, rx_fs_rate, i2s_bit_mode; + u32 compander_fs; + int ret; + + pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, + dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (params_rate(params)) { + case 8000: + tx_fs_rate = 0x00; + rx_fs_rate = 0x00; + compander_fs = COMPANDER_FS_8KHZ; + break; + case 16000: + tx_fs_rate = 0x01; + rx_fs_rate = 0x20; + compander_fs = COMPANDER_FS_16KHZ; + break; + case 32000: + tx_fs_rate = 0x02; + rx_fs_rate = 0x40; + compander_fs = COMPANDER_FS_32KHZ; + break; + case 48000: + tx_fs_rate = 0x03; + rx_fs_rate = 0x60; + compander_fs = COMPANDER_FS_48KHZ; + break; + case 96000: + tx_fs_rate = 0x04; + rx_fs_rate = 0x80; + compander_fs = COMPANDER_FS_96KHZ; + break; + case 192000: + tx_fs_rate = 0x05; + rx_fs_rate = 0xA0; + compander_fs = COMPANDER_FS_192KHZ; + break; + default: + pr_err("%s: Invalid sampling rate %d\n", __func__, + params_rate(params)); + return -EINVAL; + } + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + if (dai->id != AIF4_VIFEED && + dai->id != AIF4_MAD_TX) { + ret = tomtom_set_decimator_rate(dai, tx_fs_rate, + params_rate(params)); + if (ret < 0) { + pr_err("%s: set decimator rate failed %d\n", + __func__, ret); + return ret; + } + } + + tomtom->dai[dai->id].rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + i2s_bit_mode = 0x01; + tomtom->dai[dai->id].bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + tomtom->dai[dai->id].bit_width = 24; + i2s_bit_mode = 0x00; + break; + case SNDRV_PCM_FORMAT_S32_LE: + tomtom->dai[dai->id].bit_width = 32; + i2s_bit_mode = 0x00; + break; + default: + dev_err(codec->dev, + "%s: Invalid format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + + if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_TX_I2S_CTL, + 0x20, i2s_bit_mode << 5); + snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_TX_I2S_CTL, + 0x07, tx_fs_rate); + } else { + /* only generic ports can have sample bit adjustment */ + if (dai->id != AIF4_VIFEED && + dai->id != AIF4_MAD_TX) + tomtom_set_tx_sb_port_format(params, dai); + } + + break; + + case SNDRV_PCM_STREAM_PLAYBACK: + ret = tomtom_set_interpolator_rate(dai, rx_fs_rate, + compander_fs, + params_rate(params)); + if (ret < 0) { + pr_err("%s: set decimator rate failed %d\n", __func__, + ret); + return ret; + } + if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(codec, + TOMTOM_A_CDC_CLK_RX_I2S_CTL, + 0x20, 0x20); + break; + case SNDRV_PCM_FORMAT_S32_LE: + snd_soc_update_bits(codec, + TOMTOM_A_CDC_CLK_RX_I2S_CTL, + 0x20, 0x00); + break; + default: + pr_err("invalid format\n"); + break; + } + snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_I2S_CTL, + 0x03, (rx_fs_rate >> 0x05)); + } else { + tomtom_set_rxsb_port_format(params, dai); + tomtom->dai[dai->id].rate = params_rate(params); + } + break; + default: + pr_err("%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + } + + return 0; +} + +static struct snd_soc_dai_ops tomtom_dai_ops = { + .startup = tomtom_startup, + .shutdown = tomtom_shutdown, + .hw_params = tomtom_hw_params, + .set_sysclk = tomtom_set_dai_sysclk, + .set_fmt = tomtom_set_dai_fmt, + .set_channel_map = tomtom_set_channel_map, + .get_channel_map = tomtom_get_channel_map, +}; + +static struct snd_soc_dai_driver tomtom_dai[] = { + { + .name = "tomtom_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD9330_RATES, + .formats = TOMTOM_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tomtom_dai_ops, + }, + { + .name = "tomtom_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD9330_RATES, + .formats = TOMTOM_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tomtom_dai_ops, + }, + { + .name = "tomtom_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD9330_RATES, + .formats = TOMTOM_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tomtom_dai_ops, + }, + { + .name = "tomtom_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD9330_RATES, + .formats = TOMTOM_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tomtom_dai_ops, + }, + { + .name = "tomtom_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD9330_RATES, + .formats = TOMTOM_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tomtom_dai_ops, + }, + { + .name = "tomtom_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD9330_RATES, + .formats = TOMTOM_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tomtom_dai_ops, + }, + { + .name = "tomtom_vifeedback", + .id = AIF4_VIFEED, + .capture = { + .stream_name = "VIfeed", + .rates = SNDRV_PCM_RATE_48000, + .formats = TOMTOM_FORMATS, + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &tomtom_dai_ops, + }, + { + .name = "tomtom_mad1", + .id = AIF4_MAD_TX, + .capture = { + .stream_name = "AIF4 MAD TX", + .rates = SNDRV_PCM_RATE_16000, + .formats = TOMTOM_FORMATS_S16_S24_LE, + .rate_min = 16000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &tomtom_dai_ops, + }, +}; + +static struct snd_soc_dai_driver tomtom_i2s_dai[] = { + { + .name = "tomtom_i2s_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD9330_RATES, + .formats = TOMTOM_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tomtom_dai_ops, + }, + { + .name = "tomtom_i2s_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD9330_RATES, + .formats = TOMTOM_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tomtom_dai_ops, + }, + { + .name = "tomtom_i2s_rx2", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD9330_RATES, + .formats = TOMTOM_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tomtom_dai_ops, + }, + { + .name = "tomtom_i2s_tx2", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD9330_RATES, + .formats = TOMTOM_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tomtom_dai_ops, + }, +}; + +static int tomtom_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai, + bool up) +{ + int ret = 0; + struct wcd9xxx_ch *ch; + + if (up) { + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + ret = wcd9xxx_get_slave_port(ch->ch_num); + if (ret < 0) { + pr_err("%s: Invalid slave port ID: %d\n", + __func__, ret); + ret = -EINVAL; + } else { + set_bit(ret, &dai->ch_mask); + } + } + } else { + ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0), + msecs_to_jiffies( + TOMTOM_SLIM_CLOSE_TIMEOUT)); + if (!ret) { + pr_err("%s: Slim close tx/rx wait timeout\n", __func__); + ret = -ETIMEDOUT; + } else { + ret = 0; + } + } + return ret; +} + +static void tomtom_codec_enable_int_port(struct wcd9xxx_codec_dai_data *dai, + struct snd_soc_codec *codec) +{ + struct wcd9xxx_ch *ch; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int port_num = 0; + unsigned short reg = 0; + u8 val = 0; + + if (!dai || !codec) { + pr_err("%s: Invalid params\n", __func__); + return; + } + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + if (ch->port >= TOMTOM_RX_PORT_START_NUMBER) { + port_num = ch->port - TOMTOM_RX_PORT_START_NUMBER; + reg = TOMTOM_SLIM_PGD_PORT_INT_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(wcd9xxx, + reg); + if (!(val & (1 << (port_num % 8)))) { + val |= (1 << (port_num % 8)); + wcd9xxx_interface_reg_write( + wcd9xxx, reg, val); + val = wcd9xxx_interface_reg_read( + wcd9xxx, reg); + } + } else { + port_num = ch->port; + reg = TOMTOM_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(wcd9xxx, + reg); + if (!(val & (1 << (port_num % 8)))) { + val |= (1 << (port_num % 8)); + wcd9xxx_interface_reg_write(wcd9xxx, + reg, val); + val = wcd9xxx_interface_reg_read( + wcd9xxx, reg); + } + } + } +} + +static int tomtom_codec_enable_slimrx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + int ret = 0; + struct wcd9xxx_codec_dai_data *dai; + + core = dev_get_drvdata(codec->dev->parent); + + pr_debug("%s: event called! codec name %s num_dai %d\n" + "stream name %s event %d\n", + __func__, codec->component.name, + codec->component.num_dai, w->sname, event); + + /* Execute the callback only if interface type is slimbus */ + if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) + return 0; + + dai = &tomtom_p->dai[w->shift]; + pr_debug("%s: w->name %s w->shift %d event %d\n", + __func__, w->name, w->shift, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tomtom_codec_enable_int_port(dai, codec); + (void) tomtom_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (!dai->bus_down_in_recovery) + ret = tomtom_codec_enable_slim_chmask(dai, false); + else + pr_debug("%s: bus in recovery skip enable slim_chmask", + __func__); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + pr_debug("%s: Disconnect RX port, ret = %d\n", + __func__, ret); + } + break; + } + return ret; +} + +static int tomtom_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core = NULL; + struct snd_soc_codec *codec = NULL; + struct tomtom_priv *tomtom_p = NULL; + u32 ret = 0; + struct wcd9xxx_codec_dai_data *dai = NULL; + + if (!w) { + pr_err("%s invalid params\n", __func__); + return -EINVAL; + } + codec = snd_soc_dapm_to_codec(w->dapm); + tomtom_p = snd_soc_codec_get_drvdata(codec); + core = dev_get_drvdata(codec->dev->parent); + + pr_debug("%s: event called! codec name %s num_dai %d stream name %s\n", + __func__, codec->component.name, + codec->component.num_dai, w->sname); + + /* Execute the callback only if interface type is slimbus */ + if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + pr_err("%s Interface is not correct", __func__); + return 0; + } + + pr_debug("%s(): w->name %s event %d w->shift %d\n", + __func__, w->name, event, w->shift); + if (w->shift != AIF4_VIFEED) { + pr_err("%s Error in enabling the tx path\n", __func__); + ret = -EINVAL; + goto out_vi; + } + dai = &tomtom_p->dai[w->shift]; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /*Enable V&I sensing*/ + snd_soc_update_bits(codec, TOMTOM_A_SPKR1_PROT_EN, + 0x88, 0x88); + /*Enable spkr VI clocks*/ + snd_soc_update_bits(codec, + TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL, 0xC, 0xC); + dai->bus_down_in_recovery = false; + tomtom_codec_enable_int_port(dai, codec); + (void) tomtom_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (ret) + pr_err("%s error in close_slim_sch_tx %d\n", + __func__, ret); + if (!dai->bus_down_in_recovery) + ret = tomtom_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + pr_debug("%s: Disconnect TX port, ret = %d\n", + __func__, ret); + } + + snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL, + 0xC, 0x0); + /*Disable V&I sensing*/ + snd_soc_update_bits(codec, TOMTOM_A_SPKR1_PROT_EN, + 0x88, 0x00); + break; + } +out_vi: + return ret; +} + +/* __tomtom_codec_enable_slimtx: Enable the slimbus slave port + * for TX path + * @codec: Handle to the codec for which the slave port is to be + * enabled. + * @dai_data: The dai specific data for dai which is enabled. + */ +static int __tomtom_codec_enable_slimtx(struct snd_soc_codec *codec, + int event, struct wcd9xxx_codec_dai_data *dai_data) +{ + struct wcd9xxx *core; + int ret = 0; + + core = dev_get_drvdata(codec->dev->parent); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai_data->bus_down_in_recovery = false; + tomtom_codec_enable_int_port(dai_data, codec); + (void) tomtom_codec_enable_slim_chmask(dai_data, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai_data->wcd9xxx_ch_list, + dai_data->rate, + dai_data->bit_width, + &dai_data->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, + &dai_data->wcd9xxx_ch_list, + dai_data->grph); + if (!dai_data->bus_down_in_recovery) + ret = tomtom_codec_enable_slim_chmask(dai_data, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai_data->wcd9xxx_ch_list, + dai_data->grph); + dev_dbg(codec->dev, + "%s: Disconnect TX port, ret = %d\n", + __func__, ret); + } + break; + } + + return ret; +} + +/* + * tomtom_codec_enable_slimtx_mad: Callback function that will be invoked + * to setup the slave port for MAD. + * @codec: Handle to the codec + * @event: Indicates whether to enable or disable the slave port + */ +static int tomtom_codec_enable_slimtx_mad(struct snd_soc_codec *codec, + u8 event) +{ + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + int dapm_event = SND_SOC_DAPM_POST_PMU; + + dai = &tomtom_p->dai[AIF4_MAD_TX]; + + if (event == 0) + dapm_event = SND_SOC_DAPM_POST_PMD; + + dev_dbg(codec->dev, + "%s: mad_channel, event = 0x%x\n", + __func__, event); + return __tomtom_codec_enable_slimtx(codec, dapm_event, dai); +} + +/* + * tomtom_codec_enable_slimtx: DAPM widget allback for TX widgets + * @w: widget for which this callback is invoked + * @kcontrol: kcontrol associated with this widget + * @event: DAPM supplied event indicating enable/disable + */ +static int tomtom_codec_enable_slimtx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + + dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d stream name %s\n", + __func__, codec->component.name, + codec->component.num_dai, w->sname); + + /* Execute the callback only if interface type is slimbus */ + if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) + return 0; + + dev_dbg(codec->dev, + "%s(): w->name %s event %d w->shift %d\n", + __func__, w->name, event, w->shift); + + dai = &tomtom_p->dai[w->shift]; + return __tomtom_codec_enable_slimtx(codec, event, dai); +} + +static int tomtom_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d, + WCD9XXX_CLSH_STATE_EAR, + WCD9XXX_CLSH_REQ_ENABLE, + WCD9XXX_CLSH_EVENT_POST_PA); + + usleep_range(5000, 5100); + break; + } + return 0; +} + +static int tomtom_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d, + WCD9XXX_CLSH_STATE_EAR, + WCD9XXX_CLSH_REQ_ENABLE, + WCD9XXX_CLSH_EVENT_PRE_DAC); + break; + case SND_SOC_DAPM_POST_PMD: + wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d, + WCD9XXX_CLSH_STATE_EAR, + WCD9XXX_CLSH_REQ_DISABLE, + WCD9XXX_CLSH_EVENT_POST_PA); + usleep_range(5000, 5100); + break; + default: + break; + } + return 0; +} + +static int tomtom_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: /* fall through */ + case SND_SOC_DAPM_PRE_PMD: + if (strnstr(w->name, "IIR1", sizeof("IIR1"))) { + snd_soc_write(codec, TOMTOM_A_CDC_IIR1_GAIN_B1_CTL, + snd_soc_read(codec, + TOMTOM_A_CDC_IIR1_GAIN_B1_CTL)); + snd_soc_write(codec, TOMTOM_A_CDC_IIR1_GAIN_B2_CTL, + snd_soc_read(codec, + TOMTOM_A_CDC_IIR1_GAIN_B2_CTL)); + snd_soc_write(codec, TOMTOM_A_CDC_IIR1_GAIN_B3_CTL, + snd_soc_read(codec, + TOMTOM_A_CDC_IIR1_GAIN_B3_CTL)); + snd_soc_write(codec, TOMTOM_A_CDC_IIR1_GAIN_B4_CTL, + snd_soc_read(codec, + TOMTOM_A_CDC_IIR1_GAIN_B4_CTL)); + } else { + snd_soc_write(codec, TOMTOM_A_CDC_IIR2_GAIN_B1_CTL, + snd_soc_read(codec, + TOMTOM_A_CDC_IIR2_GAIN_B1_CTL)); + snd_soc_write(codec, TOMTOM_A_CDC_IIR2_GAIN_B2_CTL, + snd_soc_read(codec, + TOMTOM_A_CDC_IIR2_GAIN_B2_CTL)); + snd_soc_write(codec, TOMTOM_A_CDC_IIR2_GAIN_B3_CTL, + snd_soc_read(codec, + TOMTOM_A_CDC_IIR2_GAIN_B3_CTL)); + snd_soc_write(codec, TOMTOM_A_CDC_IIR2_GAIN_B4_CTL, + snd_soc_read(codec, + TOMTOM_A_CDC_IIR2_GAIN_B4_CTL)); + } + break; + } + return 0; +} + +static int tomtom_codec_dsm_mux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u8 reg_val, zoh_mux_val = 0x00; + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + reg_val = snd_soc_read(codec, TOMTOM_A_CDC_CONN_CLSH_CTL); + + if ((reg_val & 0x30) == 0x10) + zoh_mux_val = 0x04; + else if ((reg_val & 0x30) == 0x20) + zoh_mux_val = 0x08; + + if (zoh_mux_val != 0x00) + snd_soc_update_bits(codec, + TOMTOM_A_CDC_CONN_CLSH_CTL, + 0x0C, zoh_mux_val); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, TOMTOM_A_CDC_CONN_CLSH_CTL, + 0x0C, 0x00); + break; + } + return 0; +} + +static int tomtom_codec_enable_anc_ear(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tomtom_codec_enable_anc(w, kcontrol, event); + msleep(50); + snd_soc_update_bits(codec, TOMTOM_A_RX_EAR_EN, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMU: + ret = tomtom_codec_enable_ear_pa(w, kcontrol, event); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, TOMTOM_A_RX_EAR_EN, 0x10, 0x00); + msleep(40); + ret |= tomtom_codec_enable_anc(w, kcontrol, event); + break; + case SND_SOC_DAPM_POST_PMD: + ret = tomtom_codec_enable_ear_pa(w, kcontrol, event); + break; + } + return ret; +} + +/* Todo: Have separate dapm widgets for I2S and Slimbus. + * Might Need to have callbacks registered only for slimbus + */ +static const struct snd_soc_dapm_widget tomtom_dapm_widgets[] = { + /*RX stuff */ + SND_SOC_DAPM_OUTPUT("EAR"), + + SND_SOC_DAPM_PGA_E("EAR PA", TOMTOM_A_RX_EAR_EN, 4, 0, NULL, 0, + tomtom_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MIXER_E("DAC1", TOMTOM_A_RX_EAR_EN, 6, 0, dac1_switch, + ARRAY_SIZE(dac1_switch), tomtom_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, tomtom_codec_enable_slimrx, + 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, tomtom_codec_enable_slimrx, + 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, tomtom_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TOMTOM_RX1, 0, + &slim_rx_mux[TOMTOM_RX1]), + SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TOMTOM_RX2, 0, + &slim_rx_mux[TOMTOM_RX2]), + SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TOMTOM_RX3, 0, + &slim_rx_mux[TOMTOM_RX3]), + SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TOMTOM_RX4, 0, + &slim_rx_mux[TOMTOM_RX4]), + SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TOMTOM_RX5, 0, + &slim_rx_mux[TOMTOM_RX5]), + SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TOMTOM_RX6, 0, + &slim_rx_mux[TOMTOM_RX6]), + SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TOMTOM_RX7, 0, + &slim_rx_mux[TOMTOM_RX7]), + SND_SOC_DAPM_MUX("SLIM RX8 MUX", SND_SOC_NOPM, TOMTOM_RX8, 0, + &slim_rx_mux[TOMTOM_RX8]), + + 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_MIXER("SLIM RX8", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Headphone */ + SND_SOC_DAPM_OUTPUT("HEADPHONE"), + SND_SOC_DAPM_PGA_E("HPHL", TOMTOM_A_RX_HPH_CNP_EN, 5, 0, NULL, 0, + tomtom_hph_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("HPHL DAC", TOMTOM_A_RX_HPH_L_DAC_CTL, 7, 0, + hphl_switch, ARRAY_SIZE(hphl_switch), tomtom_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_PGA_E("HPHR", TOMTOM_A_RX_HPH_CNP_EN, 4, 0, NULL, 0, + tomtom_hph_pa_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("HPHR DAC", NULL, TOMTOM_A_RX_HPH_R_DAC_CTL, 7, 0, + tomtom_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + /* Speaker */ + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + SND_SOC_DAPM_OUTPUT("LINEOUT3"), + SND_SOC_DAPM_OUTPUT("LINEOUT4"), + SND_SOC_DAPM_OUTPUT("SPK_OUT"), + + SND_SOC_DAPM_PGA_E("LINEOUT1 PA", TOMTOM_A_RX_LINE_CNP_EN, 0, 0, NULL, + 0, tomtom_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT2 PA", TOMTOM_A_RX_LINE_CNP_EN, 1, 0, NULL, + 0, tomtom_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT3 PA", TOMTOM_A_RX_LINE_CNP_EN, 2, 0, NULL, + 0, tomtom_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT4 PA", TOMTOM_A_RX_LINE_CNP_EN, 3, 0, NULL, + 0, tomtom_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, + 0, tomtom_codec_enable_spk_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("SPK2 PA", SND_SOC_NOPM, 0, 0, NULL, + 0, tomtom_codec_enable_spk_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("LINEOUT1 DAC", NULL, TOMTOM_A_RX_LINE_1_DAC_CTL, 7, + 0, tomtom_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("LINEOUT2 DAC", NULL, TOMTOM_A_RX_LINE_2_DAC_CTL, 7, + 0, tomtom_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("LINEOUT3 DAC", NULL, TOMTOM_A_RX_LINE_3_DAC_CTL, 7, + 0, tomtom_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SWITCH("LINEOUT3 DAC GROUND", SND_SOC_NOPM, 0, 0, + &lineout3_ground_switch), + SND_SOC_DAPM_DAC_E("LINEOUT4 DAC", NULL, TOMTOM_A_RX_LINE_4_DAC_CTL, 7, + 0, tomtom_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SWITCH("LINEOUT4 DAC GROUND", SND_SOC_NOPM, 0, 0, + &lineout4_ground_switch), + + SND_SOC_DAPM_DAC_E("SPK DAC", NULL, TOMTOM_A_CDC_BOOST_TRGR_EN, 0, 0, + tomtom_spk_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("SPK2 DAC", NULL, TOMTOM_A_CDC_BOOST_TRGR_EN, 1, 0, + tomtom_spk_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("VDD_SPKDRV", SND_SOC_NOPM, 0, 0, + tomtom_codec_enable_vdd_spkr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("VDD_SPKDRV2", SND_SOC_NOPM, 0, 0, + tomtom_codec_enable_vdd_spkr2, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX7 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("RX3 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL, + 0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MIXER_E("RX4 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 3, 0, NULL, + 0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MIXER_E("RX5 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 4, 0, NULL, + 0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MIXER_E("RX6 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 5, 0, NULL, + 0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MIXER_E("RX7 MIX2", TOMTOM_A_CDC_CLK_RX_B1_CTL, 6, 0, NULL, + 0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MIXER_E("RX8 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 7, 0, NULL, + 0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("RX1 INTERP", TOMTOM_A_CDC_CLK_RX_B1_CTL, 0, 0, + &rx1_interp_mux, tomtom_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX2 INTERP", TOMTOM_A_CDC_CLK_RX_B1_CTL, 1, 0, + &rx2_interp_mux, tomtom_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + + SND_SOC_DAPM_MIXER("RX1 CHAIN", TOMTOM_A_CDC_RX1_B6_CTL, 5, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 CHAIN", TOMTOM_A_CDC_RX2_B6_CTL, 5, 0, NULL, 0), + + SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp3_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX4 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx4_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX4 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx4_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX5 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx5_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX5 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx5_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX6 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx6_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX6 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx6_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX7 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx7_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX7 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx7_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX8 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx8_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX8 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx8_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx1_mix2_inp1_mux), + SND_SOC_DAPM_MUX("RX1 MIX2 INP2", SND_SOC_NOPM, 0, 0, + &rx1_mix2_inp2_mux), + SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix2_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX2 INP2", SND_SOC_NOPM, 0, 0, + &rx2_mix2_inp2_mux), + SND_SOC_DAPM_MUX("RX7 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx7_mix2_inp1_mux), + SND_SOC_DAPM_MUX("RX7 MIX2 INP2", SND_SOC_NOPM, 0, 0, + &rx7_mix2_inp2_mux), + + SND_SOC_DAPM_MUX("RDAC5 MUX", SND_SOC_NOPM, 0, 0, + &rx_dac5_mux), + SND_SOC_DAPM_MUX("RDAC7 MUX", SND_SOC_NOPM, 0, 0, + &rx_dac7_mux), + + SND_SOC_DAPM_MUX("MAD_SEL MUX", SND_SOC_NOPM, 0, 0, + &mad_sel_mux), + + SND_SOC_DAPM_MUX_E("CLASS_H_DSM MUX", SND_SOC_NOPM, 0, 0, + &class_h_dsm_mux, tomtom_codec_dsm_mux_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0, + tomtom_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("CDC_I2S_RX_CONN", WCD9XXX_A_CDC_CLK_OTHR_CTL, 5, 0, + NULL, 0), + + /* TX */ + + SND_SOC_DAPM_SUPPLY("CDC_CONN", WCD9XXX_A_CDC_CLK_OTHR_CTL, 2, 0, NULL, + 0), + + SND_SOC_DAPM_SUPPLY("LDO_H", SND_SOC_NOPM, 7, 0, + tomtom_codec_enable_ldo_h, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + /* + * DAPM 'LDO_H Standalone' is to be powered by mbhc driver after + * acquring codec_resource lock. + * So call __tomtom_codec_enable_ldo_h instead and avoid deadlock. + */ + SND_SOC_DAPM_SUPPLY("LDO_H Standalone", SND_SOC_NOPM, 7, 0, + __tomtom_codec_enable_ldo_h, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("COMP0_CLK", SND_SOC_NOPM, 0, 0, + tomtom_config_compander, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("COMP1_CLK", SND_SOC_NOPM, 1, 0, + tomtom_config_compander, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("COMP2_CLK", SND_SOC_NOPM, 2, 0, + tomtom_config_compander, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD), + + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 External", SND_SOC_NOPM, 7, 0, + tomtom_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 Internal1", SND_SOC_NOPM, 7, 0, + tomtom_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 Internal2", SND_SOC_NOPM, 7, 0, + tomtom_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("AMIC3"), + + SND_SOC_DAPM_INPUT("AMIC4"), + + SND_SOC_DAPM_INPUT("AMIC5"), + + SND_SOC_DAPM_INPUT("AMIC6"), + + SND_SOC_DAPM_MUX_E("DEC1 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0, + &dec1_mux, tomtom_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("DEC2 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 1, 0, + &dec2_mux, tomtom_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("DEC3 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 2, 0, + &dec3_mux, tomtom_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("DEC4 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 3, 0, + &dec4_mux, tomtom_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("DEC5 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 4, 0, + &dec5_mux, tomtom_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("DEC6 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 5, 0, + &dec6_mux, tomtom_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("DEC7 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 6, 0, + &dec7_mux, tomtom_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("DEC8 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 7, 0, + &dec8_mux, tomtom_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("DEC9 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL, 0, 0, + &dec9_mux, tomtom_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("DEC10 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL, 1, 0, + &dec10_mux, tomtom_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("ANC1 MUX", SND_SOC_NOPM, 0, 0, &anc1_mux), + SND_SOC_DAPM_MUX("ANC2 MUX", SND_SOC_NOPM, 0, 0, &anc2_mux), + + SND_SOC_DAPM_OUTPUT("ANC HEADPHONE"), + SND_SOC_DAPM_PGA_E("ANC HPHL", SND_SOC_NOPM, 5, 0, NULL, 0, + tomtom_codec_enable_anc_hph, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("ANC HPHR", SND_SOC_NOPM, 4, 0, NULL, 0, + tomtom_codec_enable_anc_hph, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("ANC EAR"), + SND_SOC_DAPM_PGA_E("ANC EAR PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tomtom_codec_enable_anc_ear, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux), + + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_EXTERNAL_STANDALONE, SND_SOC_NOPM, + 7, 0, tomtom_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 External", SND_SOC_NOPM, 7, 0, + tomtom_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 Internal1", SND_SOC_NOPM, 7, 0, + tomtom_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 Internal2", SND_SOC_NOPM, 7, 0, + tomtom_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 Internal3", SND_SOC_NOPM, 7, 0, + tomtom_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 External", SND_SOC_NOPM, 7, 0, + tomtom_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 Internal1", SND_SOC_NOPM, 7, 0, + tomtom_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 Internal2", SND_SOC_NOPM, 7, 0, + tomtom_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS4 External", SND_SOC_NOPM, 7, + 0, tomtom_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, tomtom_codec_enable_slimtx, + 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, tomtom_codec_enable_slimtx, + 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, tomtom_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM, + AIF4_VIFEED, 0, tomtom_codec_enable_slimvi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF4 MAD", "AIF4 MAD TX", 0, + SND_SOC_NOPM, 0, 0, + tomtom_codec_enable_mad, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SWITCH("MADONOFF", SND_SOC_NOPM, 0, 0, + &aif4_mad_switch), + SND_SOC_DAPM_INPUT("MADINPUT"), + SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"), + + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)), + + SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TOMTOM_TX1, 0, + &sb_tx1_mux), + SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TOMTOM_TX2, 0, + &sb_tx2_mux), + SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TOMTOM_TX3, 0, + &sb_tx3_mux), + SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TOMTOM_TX4, 0, + &sb_tx4_mux), + SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TOMTOM_TX5, 0, + &sb_tx5_mux), + SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TOMTOM_TX6, 0, + &sb_tx6_mux), + SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TOMTOM_TX7, 0, + &sb_tx7_mux), + SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TOMTOM_TX8, 0, + &sb_tx8_mux), + SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TOMTOM_TX9, 0, + &sb_tx9_mux), + SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TOMTOM_TX10, 0, + &sb_tx10_mux), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + tomtom_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, + tomtom_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, + tomtom_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, + tomtom_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, + tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 0, 0, + tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* Sidetone */ + 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_MUX("IIR1 INP4 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp4_mux), + + SND_SOC_DAPM_MIXER_E("IIR1", TOMTOM_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0, + tomtom_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux), + + SND_SOC_DAPM_MUX("IIR2 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp2_mux), + + SND_SOC_DAPM_MUX("IIR2 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp3_mux), + + SND_SOC_DAPM_MUX("IIR2 INP4 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp4_mux), + + SND_SOC_DAPM_MIXER_E("IIR2", TOMTOM_A_CDC_CLK_SD_CTL, 1, 0, NULL, 0, + tomtom_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + + /* AUX PGA */ + SND_SOC_DAPM_ADC_E("AUX_PGA_Left", NULL, TOMTOM_A_RX_AUX_SW_CTL, 7, 0, + tomtom_codec_enable_aux_pga, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("AUX_PGA_Right", NULL, TOMTOM_A_RX_AUX_SW_CTL, 6, 0, + tomtom_codec_enable_aux_pga, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* Lineout, ear and HPH PA Mixers */ + + SND_SOC_DAPM_MIXER("EAR_PA_MIXER", SND_SOC_NOPM, 0, 0, + ear_pa_mix, ARRAY_SIZE(ear_pa_mix)), + + SND_SOC_DAPM_MIXER("HPHL_PA_MIXER", SND_SOC_NOPM, 0, 0, + hphl_pa_mix, ARRAY_SIZE(hphl_pa_mix)), + + SND_SOC_DAPM_MIXER("HPHR_PA_MIXER", SND_SOC_NOPM, 0, 0, + hphr_pa_mix, ARRAY_SIZE(hphr_pa_mix)), + + SND_SOC_DAPM_MIXER("LINEOUT1_PA_MIXER", SND_SOC_NOPM, 0, 0, + lineout1_pa_mix, ARRAY_SIZE(lineout1_pa_mix)), + + SND_SOC_DAPM_MIXER("LINEOUT2_PA_MIXER", SND_SOC_NOPM, 0, 0, + lineout2_pa_mix, ARRAY_SIZE(lineout2_pa_mix)), + + SND_SOC_DAPM_MIXER("LINEOUT3_PA_MIXER", SND_SOC_NOPM, 0, 0, + lineout3_pa_mix, ARRAY_SIZE(lineout3_pa_mix)), + + SND_SOC_DAPM_MIXER("LINEOUT4_PA_MIXER", SND_SOC_NOPM, 0, 0, + lineout4_pa_mix, ARRAY_SIZE(lineout4_pa_mix)), + + SND_SOC_DAPM_SWITCH("VIONOFF", SND_SOC_NOPM, 0, 0, + &aif4_vi_switch), + + SND_SOC_DAPM_INPUT("VIINPUT"), +}; + +static irqreturn_t tomtom_slimbus_irq(int irq, void *data) +{ + struct tomtom_priv *priv = data; + struct snd_soc_codec *codec = priv->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + unsigned long status = 0; + int i, j, port_id, k; + u32 bit; + u8 val, int_val = 0; + bool tx, cleared; + unsigned short reg = 0; + + for (i = TOMTOM_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0; + i <= TOMTOM_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) { + val = wcd9xxx_interface_reg_read(wcd9xxx, i); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = (j >= 16 ? true : false); + port_id = (tx ? j - 16 : j); + val = wcd9xxx_interface_reg_read(wcd9xxx, + TOMTOM_SLIM_PGD_PORT_INT_RX_SOURCE0 + j); + if (val) { + if (!tx) + reg = TOMTOM_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = TOMTOM_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + wcd9xxx, reg); + /* + * Ignore interrupts for ports for which the + * interrupts are not specifically enabled. + */ + if (!(int_val & (1 << (port_id % 8)))) + continue; + } + if (val & TOMTOM_SLIM_IRQ_OVERFLOW) + pr_err_ratelimited( + "%s: overflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if (val & TOMTOM_SLIM_IRQ_UNDERFLOW) + pr_err_ratelimited( + "%s: underflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if ((val & TOMTOM_SLIM_IRQ_OVERFLOW) || + (val & TOMTOM_SLIM_IRQ_UNDERFLOW)) { + if (!tx) + reg = TOMTOM_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = TOMTOM_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read(wcd9xxx, reg); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + wcd9xxx_interface_reg_write(wcd9xxx, reg, + int_val); + } + } + if (val & TOMTOM_SLIM_IRQ_PORT_CLOSED) { + /* + * INT SOURCE register starts from RX to TX + * but port number in the ch_mask is in opposite way + */ + bit = (tx ? j - 16 : j + 16); + pr_debug("%s: %s port %d closed value %x, bit %u\n", + __func__, (tx ? "TX" : "RX"), port_id, val, + bit); + for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) { + pr_debug("%s: priv->dai[%d].ch_mask = 0x%lx\n", + __func__, k, priv->dai[k].ch_mask); + if (test_and_clear_bit(bit, + &priv->dai[k].ch_mask)) { + cleared = true; + if (!priv->dai[k].ch_mask) + wake_up(&priv->dai[k].dai_wait); + /* + * There are cases when multiple DAIs + * might be using the same slimbus + * channel. Hence don't break here. + */ + } + } + WARN(!cleared, + "Couldn't find slimbus %s port %d for closing\n", + (tx ? "TX" : "RX"), port_id); + } + wcd9xxx_interface_reg_write(wcd9xxx, + TOMTOM_SLIM_PGD_PORT_INT_CLR_RX_0 + + (j / 8), + 1 << (j % 8)); + } + + return IRQ_HANDLED; +} + +static int tomtom_handle_pdata(struct tomtom_priv *tomtom) +{ + struct snd_soc_codec *codec = tomtom->codec; + struct wcd9xxx_pdata *pdata = tomtom->resmgr.pdata; + int k1, k2, k3, dec, rc = 0; + u8 leg_mode, txfe_bypass, txfe_buff, flag; + u8 i = 0, j = 0; + u8 val_txfe = 0, value = 0; + u8 dmic_ctl_val, mad_dmic_ctl_val; + u8 anc_ctl_value = 0; + u32 def_dmic_rate; + u16 tx_dmic_ctl_reg; + + if (!pdata) { + pr_err("%s: NULL pdata\n", __func__); + rc = -ENODEV; + goto done; + } + + leg_mode = pdata->amic_settings.legacy_mode; + txfe_bypass = pdata->amic_settings.txfe_enable; + txfe_buff = pdata->amic_settings.txfe_buff; + flag = pdata->amic_settings.use_pdata; + + /* Make sure settings are correct */ + if ((pdata->micbias.ldoh_v > WCD9XXX_LDOH_3P0_V) || + (pdata->micbias.bias1_cfilt_sel > WCD9XXX_CFILT3_SEL) || + (pdata->micbias.bias2_cfilt_sel > WCD9XXX_CFILT3_SEL) || + (pdata->micbias.bias3_cfilt_sel > WCD9XXX_CFILT3_SEL) || + (pdata->micbias.bias4_cfilt_sel > WCD9XXX_CFILT3_SEL)) { + rc = -EINVAL; + goto done; + } + /* figure out k value */ + k1 = wcd9xxx_resmgr_get_k_val(&tomtom->resmgr, + pdata->micbias.cfilt1_mv); + k2 = wcd9xxx_resmgr_get_k_val(&tomtom->resmgr, + pdata->micbias.cfilt2_mv); + k3 = wcd9xxx_resmgr_get_k_val(&tomtom->resmgr, + pdata->micbias.cfilt3_mv); + + if (IS_ERR_VALUE(k1) || IS_ERR_VALUE(k2) || IS_ERR_VALUE(k3)) { + rc = -EINVAL; + goto done; + } + /* Set voltage level and always use LDO */ + snd_soc_update_bits(codec, TOMTOM_A_LDO_H_MODE_1, 0x0C, + (pdata->micbias.ldoh_v << 2)); + + snd_soc_update_bits(codec, TOMTOM_A_MICB_CFILT_1_VAL, 0xFC, (k1 << 2)); + snd_soc_update_bits(codec, TOMTOM_A_MICB_CFILT_2_VAL, 0xFC, (k2 << 2)); + snd_soc_update_bits(codec, TOMTOM_A_MICB_CFILT_3_VAL, 0xFC, (k3 << 2)); + + snd_soc_update_bits(codec, TOMTOM_A_MICB_1_CTL, 0x60, + (pdata->micbias.bias1_cfilt_sel << 5)); + snd_soc_update_bits(codec, TOMTOM_A_MICB_2_CTL, 0x60, + (pdata->micbias.bias2_cfilt_sel << 5)); + snd_soc_update_bits(codec, TOMTOM_A_MICB_3_CTL, 0x60, + (pdata->micbias.bias3_cfilt_sel << 5)); + snd_soc_update_bits(codec, tomtom->resmgr.reg_addr->micb_4_ctl, 0x60, + (pdata->micbias.bias4_cfilt_sel << 5)); + + for (i = 0; i < 6; j++, i += 2) { + if (flag & (0x01 << i)) { + val_txfe = (txfe_bypass & (0x01 << i)) ? 0x20 : 0x00; + val_txfe = val_txfe | + ((txfe_buff & (0x01 << i)) ? 0x10 : 0x00); + snd_soc_update_bits(codec, + TOMTOM_A_TX_1_2_TEST_EN + j * 10, + 0x30, val_txfe); + } + if (flag & (0x01 << (i + 1))) { + val_txfe = (txfe_bypass & + (0x01 << (i + 1))) ? 0x02 : 0x00; + val_txfe |= (txfe_buff & + (0x01 << (i + 1))) ? 0x01 : 0x00; + snd_soc_update_bits(codec, + TOMTOM_A_TX_1_2_TEST_EN + j * 10, + 0x03, val_txfe); + } + } + if (flag & 0x40) { + value = (leg_mode & 0x40) ? 0x10 : 0x00; + value = value | ((txfe_bypass & 0x40) ? 0x02 : 0x00); + value = value | ((txfe_buff & 0x40) ? 0x01 : 0x00); + snd_soc_update_bits(codec, TOMTOM_A_TX_7_MBHC_EN, + 0x13, value); + } + + if (pdata->ocp.use_pdata) { + /* not defined in CODEC specification */ + if (pdata->ocp.hph_ocp_limit == 1 || + pdata->ocp.hph_ocp_limit == 5) { + rc = -EINVAL; + goto done; + } + snd_soc_update_bits(codec, TOMTOM_A_RX_COM_OCP_CTL, + 0x0F, pdata->ocp.num_attempts); + snd_soc_write(codec, TOMTOM_A_RX_COM_OCP_COUNT, + ((pdata->ocp.run_time << 4) | pdata->ocp.wait_time)); + snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_OCP_CTL, + 0xE0, (pdata->ocp.hph_ocp_limit << 5)); + } + + for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) { + if (pdata->regulator[i].name && + !strcmp(pdata->regulator[i].name, "CDC_VDDA_RX")) { + if (pdata->regulator[i].min_uV == 1800000 && + pdata->regulator[i].max_uV == 1800000) { + snd_soc_write(codec, TOMTOM_A_BIAS_REF_CTL, + 0x1C); + } else if (pdata->regulator[i].min_uV == 2200000 && + pdata->regulator[i].max_uV == 2200000) { + snd_soc_write(codec, TOMTOM_A_BIAS_REF_CTL, + 0x1E); + } else { + pr_err("%s: unsupported CDC_VDDA_RX voltage\n" + "min %d, max %d\n", __func__, + pdata->regulator[i].min_uV, + pdata->regulator[i].max_uV); + rc = -EINVAL; + } + break; + } + } + + /* Set micbias capless mode with tail current */ + value = (pdata->micbias.bias1_cap_mode == MICBIAS_EXT_BYP_CAP ? + 0x00 : 0x16); + snd_soc_update_bits(codec, TOMTOM_A_MICB_1_CTL, 0x1E, value); + value = (pdata->micbias.bias2_cap_mode == MICBIAS_EXT_BYP_CAP ? + 0x00 : 0x16); + snd_soc_update_bits(codec, TOMTOM_A_MICB_2_CTL, 0x1E, value); + value = (pdata->micbias.bias3_cap_mode == MICBIAS_EXT_BYP_CAP ? + 0x00 : 0x16); + snd_soc_update_bits(codec, TOMTOM_A_MICB_3_CTL, 0x1E, value); + value = (pdata->micbias.bias4_cap_mode == MICBIAS_EXT_BYP_CAP ? + 0x00 : 0x16); + snd_soc_update_bits(codec, TOMTOM_A_MICB_4_CTL, 0x1E, value); + + /* Set the DMIC sample rate */ + switch (pdata->mclk_rate) { + case TOMTOM_MCLK_CLK_9P6MHZ: + def_dmic_rate = + WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + break; + case TOMTOM_MCLK_CLK_12P288MHZ: + def_dmic_rate = + WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ; + break; + default: + /* should never happen */ + pr_err("%s: Invalid mclk_rate %d\n", + __func__, pdata->mclk_rate); + rc = -EINVAL; + goto done; + } + + if (pdata->dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + pr_info("%s: dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + pdata->dmic_sample_rate = def_dmic_rate; + } + + if (pdata->mad_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + pr_info("%s: mad_dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + /* + * use dmic_sample_rate as the default for MAD + * if mad dmic sample rate is undefined + */ + pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate; + } + + /* + * Default the DMIC clk rates to mad_dmic_sample_rate, + * whereas, the anc/txfe dmic rates to dmic_sample_rate + * since the anc/txfe are independent of mad block. + */ + mad_dmic_ctl_val = tomtom_get_dmic_clk_val(tomtom->codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + snd_soc_update_bits(codec, TOMTOM_A_DMIC_B1_CTL, + 0xE0, mad_dmic_ctl_val << 5); + snd_soc_update_bits(codec, TOMTOM_A_DMIC_B2_CTL, + 0x70, mad_dmic_ctl_val << 4); + snd_soc_update_bits(codec, TOMTOM_A_DMIC_B2_CTL, + 0x0E, mad_dmic_ctl_val << 1); + + dmic_ctl_val = tomtom_get_dmic_clk_val(tomtom->codec, + pdata->mclk_rate, + pdata->dmic_sample_rate); + + if (dmic_ctl_val == WCD9330_DMIC_CLK_DIV_2) + anc_ctl_value = WCD9XXX_ANC_DMIC_X2_ON; + else + anc_ctl_value = WCD9XXX_ANC_DMIC_X2_OFF; + + for (dec = 0; dec < NUM_DECIMATORS; dec++) { + tx_dmic_ctl_reg = + TOMTOM_A_CDC_TX1_DMIC_CTL + (8 * dec); + snd_soc_update_bits(codec, tx_dmic_ctl_reg, + 0x07, dmic_ctl_val); + } + snd_soc_update_bits(codec, TOMTOM_A_CDC_ANC1_B2_CTL, + 0x1, anc_ctl_value); + snd_soc_update_bits(codec, TOMTOM_A_CDC_ANC2_B2_CTL, + 0x1, anc_ctl_value); +done: + return rc; +} + +static const struct wcd9xxx_reg_mask_val tomtom_reg_defaults[] = { + + /* set MCLk to 9.6 */ + TOMTOM_REG_VAL(TOMTOM_A_CHIP_CTL, 0x02), + + /* EAR PA deafults */ + TOMTOM_REG_VAL(TOMTOM_A_RX_EAR_CMBUFF, 0x05), + + /* RX deafults */ + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX1_B5_CTL, 0x79), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX2_B5_CTL, 0x79), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX3_B5_CTL, 0x79), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX4_B5_CTL, 0x79), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX5_B5_CTL, 0x79), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX6_B5_CTL, 0x79), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX7_B5_CTL, 0x79), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX8_B5_CTL, 0x79), + + /* RX1 and RX2 defaults */ + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX1_B6_CTL, 0xA0), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX2_B6_CTL, 0xA0), + + /* RX3 to RX7 defaults */ + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX3_B6_CTL, 0x80), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX4_B6_CTL, 0x80), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX5_B6_CTL, 0x80), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX6_B6_CTL, 0x80), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX7_B6_CTL, 0x80), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX8_B6_CTL, 0x80), + + /* MAD registers */ + TOMTOM_REG_VAL(TOMTOM_A_MAD_ANA_CTRL, 0xF1), + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_MAIN_CTL_1, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_MAIN_CTL_2, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_1, 0x00), + /* Set SAMPLE_TX_EN bit */ + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_2, 0x03), + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_3, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_4, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_5, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_6, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_7, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_8, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL, 0x40), + TOMTOM_REG_VAL(TOMTOM_A_CDC_DEBUG_B7_CTL, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_CLK_OTHR_CTL, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_INP_SEL, 0x01), + + /* Set HPH Path to low power mode */ + TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_BIAS_PA, 0x57), + + /* BUCK default */ + TOMTOM_REG_VAL(TOMTOM_A_BUCK_CTRL_CCL_4, 0x51), + TOMTOM_REG_VAL(TOMTOM_A_BUCK_CTRL_CCL_1, 0x5B), +}; + +/* + * Don't update TOMTOM_A_CHIP_CTL, TOMTOM_A_BUCK_CTRL_CCL_1 and + * TOMTOM_A_RX_EAR_CMBUFF as those are updated in tomtom_reg_defaults + */ +static const struct wcd9xxx_reg_mask_val tomtom_1_0_reg_defaults[] = { + TOMTOM_REG_VAL(TOMTOM_A_TX_1_GAIN, 0x2), + TOMTOM_REG_VAL(TOMTOM_A_TX_2_GAIN, 0x2), + TOMTOM_REG_VAL(TOMTOM_A_TX_1_2_ADC_IB, 0x44), + TOMTOM_REG_VAL(TOMTOM_A_TX_3_GAIN, 0x2), + TOMTOM_REG_VAL(TOMTOM_A_TX_4_GAIN, 0x2), + TOMTOM_REG_VAL(TOMTOM_A_TX_3_4_ADC_IB, 0x44), + TOMTOM_REG_VAL(TOMTOM_A_TX_5_GAIN, 0x2), + TOMTOM_REG_VAL(TOMTOM_A_TX_6_GAIN, 0x2), + TOMTOM_REG_VAL(TOMTOM_A_TX_5_6_ADC_IB, 0x44), + TOMTOM_REG_VAL(WCD9XXX_A_BUCK_MODE_3, 0xCE), + TOMTOM_REG_VAL(WCD9XXX_A_BUCK_CTRL_VCL_1, 0x8), + TOMTOM_REG_VAL(TOMTOM_A_BUCK_CTRL_CCL_4, 0x51), + TOMTOM_REG_VAL(TOMTOM_A_NCP_DTEST, 0x10), + TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_CHOP_CTL, 0xA4), + TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_OCP_CTL, 0x69), + TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_CNP_WG_CTL, 0xDA), + TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_CNP_WG_TIME, 0x15), + TOMTOM_REG_VAL(TOMTOM_A_RX_EAR_BIAS_PA, 0x76), + TOMTOM_REG_VAL(TOMTOM_A_RX_EAR_CNP, 0xC0), + TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_BIAS_PA, 0x78), + TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_1_TEST, 0x2), + TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_2_TEST, 0x2), + TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_3_TEST, 0x2), + TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_4_TEST, 0x2), + TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV1_OCP_CTL, 0x97), + TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV1_CLIP_DET, 0x1), + TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV1_IEC, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV2_OCP_CTL, 0x97), + TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV2_CLIP_DET, 0x1), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX1_MUX_CTL, 0x4A), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX2_MUX_CTL, 0x4A), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX3_MUX_CTL, 0x4A), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX4_MUX_CTL, 0x4A), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX5_MUX_CTL, 0x4A), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX6_MUX_CTL, 0x4A), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX7_MUX_CTL, 0x4A), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX8_MUX_CTL, 0x4A), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX9_MUX_CTL, 0x4A), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX10_MUX_CTL, 0x4A), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX1_B4_CTL, 0xB), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX2_B4_CTL, 0xB), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX3_B4_CTL, 0xB), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX4_B4_CTL, 0xB), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX5_B4_CTL, 0xB), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX6_B4_CTL, 0xB), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX7_B4_CTL, 0xB), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX8_B4_CTL, 0xB), + TOMTOM_REG_VAL(TOMTOM_A_CDC_VBAT_GAIN_UPD_MON, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_CDC_PA_RAMP_B1_CTL, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_CDC_PA_RAMP_B2_CTL, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_CDC_PA_RAMP_B3_CTL, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_CDC_PA_RAMP_B4_CTL, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_CDC_COMP0_B4_CTL, 0x37), + TOMTOM_REG_VAL(TOMTOM_A_CDC_COMP0_B5_CTL, 0x7f), + TOMTOM_REG_VAL(TOMTOM_A_CDC_COMP0_B5_CTL, 0x7f), +}; + +static const struct wcd9xxx_reg_mask_val tomtom_2_0_reg_defaults[] = { + TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_MAIN_CTL_2, 0x32), + TOMTOM_REG_VAL(TOMTOM_A_RCO_CTRL, 0x10), + TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_L_TEST, 0x0A), + TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_R_TEST, 0x0A), + TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE0, 0xC3), + TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_DATA0, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX_I2S_SCK_MODE, 0x04), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX_I2S_WS_MODE, 0x04), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX_I2S_SCK_MODE, 0x04), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX_I2S_WS_MODE, 0x04), + TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE1, 0xE0), + TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE2, 0x03), + TOMTOM_REG_VAL(TOMTOM_A_CDC_JTCK_MODE, 0x04), + TOMTOM_REG_VAL(TOMTOM_A_CDC_JTDI_MODE, 0x04), + TOMTOM_REG_VAL(TOMTOM_A_CDC_JTMS_MODE, 0x04), + TOMTOM_REG_VAL(TOMTOM_A_CDC_JTDO_MODE, 0x04), + TOMTOM_REG_VAL(TOMTOM_A_CDC_JTRST_MODE, 0x04), +}; + +static const struct wcd9xxx_reg_mask_val tomtom_2_0_reg_i2c_defaults[] = { + TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE0, 0x00), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX_I2S_SCK_MODE, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_CDC_TX_I2S_WS_MODE, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX_I2S_SCK_MODE, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_CDC_RX_I2S_WS_MODE, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE1, 0x0), + TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE2, 0x0), +}; + +static void tomtom_update_reg_defaults(struct snd_soc_codec *codec) +{ + u32 i; + struct wcd9xxx *tomtom_core = dev_get_drvdata(codec->dev->parent); + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + for (i = 0; i < ARRAY_SIZE(tomtom_reg_defaults); i++) + snd_soc_write(codec, tomtom_reg_defaults[i].reg, + tomtom_reg_defaults[i].val); + + for (i = 0; i < ARRAY_SIZE(tomtom_1_0_reg_defaults); i++) + snd_soc_write(codec, tomtom_1_0_reg_defaults[i].reg, + tomtom_1_0_reg_defaults[i].val); + + if (!TOMTOM_IS_1_0(tomtom_core->version)) { + for (i = 0; i < ARRAY_SIZE(tomtom_2_0_reg_defaults); i++) + snd_soc_write(codec, tomtom_2_0_reg_defaults[i].reg, + tomtom_2_0_reg_defaults[i].val); + + if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + for (i = 0; i < ARRAY_SIZE(tomtom_2_0_reg_i2c_defaults); + i++) + snd_soc_write(codec, + tomtom_2_0_reg_i2c_defaults[i].reg, + tomtom_2_0_reg_i2c_defaults[i].val); + } + } +} + +static const struct wcd9xxx_reg_mask_val tomtom_codec_reg_init_val[] = { + /* Initialize current threshold to 350MA + * number of wait and run cycles to 4096 + */ + {TOMTOM_A_RX_HPH_OCP_CTL, 0xE1, 0x61}, + {TOMTOM_A_RX_COM_OCP_COUNT, 0xFF, 0xFF}, + {TOMTOM_A_RX_HPH_L_TEST, 0x01, 0x01}, + {TOMTOM_A_RX_HPH_R_TEST, 0x01, 0x01}, + + /* Initialize gain registers to use register gain */ + {TOMTOM_A_RX_HPH_L_GAIN, 0x20, 0x20}, + {TOMTOM_A_RX_HPH_R_GAIN, 0x20, 0x20}, + {TOMTOM_A_RX_LINE_1_GAIN, 0x20, 0x20}, + {TOMTOM_A_RX_LINE_2_GAIN, 0x20, 0x20}, + {TOMTOM_A_RX_LINE_3_GAIN, 0x20, 0x20}, + {TOMTOM_A_RX_LINE_4_GAIN, 0x20, 0x20}, + {TOMTOM_A_SPKR_DRV1_GAIN, 0x04, 0x04}, + {TOMTOM_A_SPKR_DRV2_GAIN, 0x04, 0x04}, + + /* Use 16 bit sample size for TX1 to TX6 */ + {TOMTOM_A_CDC_CONN_TX_SB_B1_CTL, 0x30, 0x20}, + {TOMTOM_A_CDC_CONN_TX_SB_B2_CTL, 0x30, 0x20}, + {TOMTOM_A_CDC_CONN_TX_SB_B3_CTL, 0x30, 0x20}, + {TOMTOM_A_CDC_CONN_TX_SB_B4_CTL, 0x30, 0x20}, + {TOMTOM_A_CDC_CONN_TX_SB_B5_CTL, 0x30, 0x20}, + {TOMTOM_A_CDC_CONN_TX_SB_B6_CTL, 0x30, 0x20}, + + /* Use 16 bit sample size for TX7 to TX10 */ + {TOMTOM_A_CDC_CONN_TX_SB_B7_CTL, 0x60, 0x40}, + {TOMTOM_A_CDC_CONN_TX_SB_B8_CTL, 0x60, 0x40}, + {TOMTOM_A_CDC_CONN_TX_SB_B9_CTL, 0x60, 0x40}, + {TOMTOM_A_CDC_CONN_TX_SB_B10_CTL, 0x60, 0x40}, + + /*enable HPF filter for TX paths */ + {TOMTOM_A_CDC_TX1_MUX_CTL, 0x8, 0x0}, + {TOMTOM_A_CDC_TX2_MUX_CTL, 0x8, 0x0}, + {TOMTOM_A_CDC_TX3_MUX_CTL, 0x8, 0x0}, + {TOMTOM_A_CDC_TX4_MUX_CTL, 0x8, 0x0}, + {TOMTOM_A_CDC_TX5_MUX_CTL, 0x8, 0x0}, + {TOMTOM_A_CDC_TX6_MUX_CTL, 0x8, 0x0}, + {TOMTOM_A_CDC_TX7_MUX_CTL, 0x8, 0x0}, + {TOMTOM_A_CDC_TX8_MUX_CTL, 0x8, 0x0}, + {TOMTOM_A_CDC_TX9_MUX_CTL, 0x8, 0x0}, + {TOMTOM_A_CDC_TX10_MUX_CTL, 0x8, 0x0}, + + /* Compander zone selection */ + {TOMTOM_A_CDC_COMP0_B4_CTL, 0x3F, 0x37}, + {TOMTOM_A_CDC_COMP1_B4_CTL, 0x3F, 0x37}, + {TOMTOM_A_CDC_COMP2_B4_CTL, 0x3F, 0x37}, + {TOMTOM_A_CDC_COMP0_B5_CTL, 0x7F, 0x7F}, + {TOMTOM_A_CDC_COMP1_B5_CTL, 0x7F, 0x7F}, + {TOMTOM_A_CDC_COMP2_B5_CTL, 0x7F, 0x7F}, + + /* + * Setup wavegen timer to 20msec and disable chopper + * as default. This corresponds to Compander OFF + */ + {TOMTOM_A_RX_HPH_CNP_WG_CTL, 0xFF, 0xDB}, + {TOMTOM_A_RX_HPH_CNP_WG_TIME, 0xFF, 0x58}, + {TOMTOM_A_RX_HPH_BIAS_WG_OCP, 0xFF, 0x1A}, + {TOMTOM_A_RX_HPH_CHOP_CTL, 0xFF, 0x24}, + + /* Choose max non-overlap time for NCP */ + {TOMTOM_A_NCP_CLK, 0xFF, 0xFC}, + + /* Program the 0.85 volt VBG_REFERENCE */ + {TOMTOM_A_BIAS_CURR_CTL_2, 0xFF, 0x04}, + + /* set MAD input MIC to DMIC1 */ + {TOMTOM_A_CDC_MAD_INP_SEL, 0x0F, 0x08}, + + {TOMTOM_A_INTR_MODE, 0x04, 0x04}, +}; + +static const struct wcd9xxx_reg_mask_val tomtom_codec_2_0_reg_init_val[] = { + {TOMTOM_A_RX_HPH_L_TEST, 0x08, 0x00}, + {TOMTOM_A_RX_HPH_R_TEST, 0x08, 0x00}, + {TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD, 0xFF, 0x00}, + {TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD, 0xFF, 0x00}, + {TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING, 0x01, 0x01}, + {TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING, 0x01, 0x01}, + {TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL, 0x01, 0x00}, + {TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL, 0x01, 0x00}, +}; + +static void tomtom_codec_init_reg(struct snd_soc_codec *codec) +{ + u32 i; + struct wcd9xxx *tomtom_core = dev_get_drvdata(codec->dev->parent); + + for (i = 0; i < ARRAY_SIZE(tomtom_codec_reg_init_val); i++) + snd_soc_update_bits(codec, tomtom_codec_reg_init_val[i].reg, + tomtom_codec_reg_init_val[i].mask, + tomtom_codec_reg_init_val[i].val); + + if (!TOMTOM_IS_1_0(tomtom_core->version)) { + for (i = 0; i < ARRAY_SIZE(tomtom_codec_2_0_reg_init_val); i++) + snd_soc_update_bits(codec, + tomtom_codec_2_0_reg_init_val[i].reg, + tomtom_codec_2_0_reg_init_val[i].mask, + tomtom_codec_2_0_reg_init_val[i].val); + } + +} + +static void tomtom_slim_interface_init_reg(struct snd_soc_codec *codec) +{ + int i; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) + wcd9xxx_interface_reg_write(wcd9xxx, + TOMTOM_SLIM_PGD_PORT_INT_EN0 + i, + 0xFF); +} + +static int tomtom_setup_irqs(struct tomtom_priv *tomtom) +{ + int ret = 0; + struct snd_soc_codec *codec = tomtom->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS, + tomtom_slimbus_irq, "SLIMBUS Slave", tomtom); + if (ret) + pr_err("%s: Failed to request irq %d\n", __func__, + WCD9XXX_IRQ_SLIMBUS); + else + tomtom_slim_interface_init_reg(codec); + + return ret; +} + +static void tomtom_cleanup_irqs(struct tomtom_priv *tomtom) +{ + struct snd_soc_codec *codec = tomtom->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tomtom); +} + +static +struct firmware_cal *tomtom_get_hwdep_fw_cal(struct snd_soc_codec *codec, + enum wcd_cal_type type) +{ + struct tomtom_priv *tomtom; + struct firmware_cal *hwdep_cal; + + if (!codec) { + pr_err("%s: NULL codec pointer\n", __func__); + return NULL; + } + tomtom = snd_soc_codec_get_drvdata(codec); + hwdep_cal = wcdcal_get_fw_cal(tomtom->fw_data, type); + if (!hwdep_cal) { + dev_err(codec->dev, "%s: cal not sent by %d\n", + __func__, type); + return NULL; + } else { + return hwdep_cal; + } +} + +int tomtom_hs_detect(struct snd_soc_codec *codec, + struct wcd9xxx_mbhc_config *mbhc_cfg) +{ + int rc; + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + if (mbhc_cfg->insert_detect) { + rc = wcd9xxx_mbhc_start(&tomtom->mbhc, mbhc_cfg); + if (!rc) + tomtom->mbhc_started = true; + } else { + /* MBHC is disabled, so disable Auto pulldown */ + snd_soc_update_bits(codec, TOMTOM_A_MBHC_INSERT_DETECT2, 0xC0, + 0x00); + snd_soc_update_bits(codec, TOMTOM_A_MICB_CFILT_2_CTL, 0x01, + 0x00); + tomtom->mbhc.mbhc_cfg = NULL; + rc = 0; + } + return rc; +} +EXPORT_SYMBOL(tomtom_hs_detect); + +void tomtom_hs_detect_exit(struct snd_soc_codec *codec) +{ + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + wcd9xxx_mbhc_stop(&tomtom->mbhc); + tomtom->mbhc_started = false; +} +EXPORT_SYMBOL(tomtom_hs_detect_exit); + +void tomtom_event_register( + int (*machine_event_cb)(struct snd_soc_codec *codec, + enum wcd9xxx_codec_event), + struct snd_soc_codec *codec) +{ + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + tomtom->machine_codec_event_cb = machine_event_cb; +} +EXPORT_SYMBOL(tomtom_event_register); + +void tomtom_register_ext_clk_cb( + int (*codec_ext_clk_en)(struct snd_soc_codec *codec, + int enable, bool dapm), + int (*get_ext_clk_cnt)(void), + struct snd_soc_codec *codec) +{ + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + + tomtom->codec_ext_clk_en_cb = codec_ext_clk_en; + tomtom->codec_get_ext_clk_cnt = get_ext_clk_cnt; +} +EXPORT_SYMBOL(tomtom_register_ext_clk_cb); + +static void tomtom_init_slim_slave_cfg(struct snd_soc_codec *codec) +{ + struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec); + struct afe_param_cdc_slimbus_slave_cfg *cfg; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + uint64_t eaddr = 0; + + cfg = &priv->slimbus_slave_cfg; + cfg->minor_version = 1; + cfg->tx_slave_port_offset = 0; + cfg->rx_slave_port_offset = 16; + + memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr)); + WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6); + cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF; + cfg->device_enum_addr_msw = eaddr >> 32; + + pr_debug("%s: slimbus logical address 0x%llx\n", __func__, eaddr); +} + +static int tomtom_device_down(struct wcd9xxx *wcd9xxx) +{ + int count; + struct snd_soc_codec *codec; + struct tomtom_priv *priv; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + priv = snd_soc_codec_get_drvdata(codec); + wcd_cpe_ssr_event(priv->cpe_core, WCD_CPE_BUS_DOWN_EVENT); + snd_soc_card_change_online_state(codec->component.card, 0); + set_bit(BUS_DOWN, &priv->status_mask); + + for (count = 0; count < NUM_CODEC_DAIS; count++) + priv->dai[count].bus_down_in_recovery = true; + return 0; +} + +static int wcd9xxx_prepare_static_pa(struct wcd9xxx_mbhc *mbhc, + struct list_head *lh) +{ + int i; + struct snd_soc_codec *codec = mbhc->codec; + u32 delay; + + const struct wcd9xxx_reg_mask_val reg_set_paon[] = { + {TOMTOM_A_TX_COM_BIAS, 0xff, 0xF0}, + {WCD9XXX_A_CDC_RX1_B6_CTL, 0xff, 0x81}, + {WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x01, 0x01}, + {WCD9XXX_A_BUCK_MODE_2, 0xff, 0xEF}, + {WCD9XXX_A_BUCK_MODE_2, 0xff, 0xEE}, + {TOMTOM_A_NCP_DTEST, 0xff, 0x20}, + {WCD9XXX_A_CDC_CLK_OTHR_CTL, 0xff, 0x21}, + {WCD9XXX_A_CDC_RX2_B6_CTL, 0xff, 0x81}, + {WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x02, 0x02}, + + {WCD9XXX_A_BUCK_MODE_2, 0xff, 0xAE}, + {WCD9XXX_A_BUCK_MODE_2, 0xff, 0xAA}, + {WCD9XXX_A_NCP_CLK, 0xff, 0x9C}, + {WCD9XXX_A_NCP_CLK, 0xff, 0xFC}, + {WCD9XXX_A_RX_COM_BIAS, 0xff, 0xA0}, + {WCD9XXX_A_BUCK_MODE_3, 0xff, 0xC6}, + {WCD9XXX_A_BUCK_MODE_4, 0xff, 0xE6}, + {WCD9XXX_A_BUCK_MODE_5, 0xff, 0x02}, + {WCD9XXX_A_BUCK_MODE_1, 0xff, 0xA1}, + /* Add a delay of 1ms after this reg write */ + + {WCD9XXX_A_NCP_STATIC, 0xff, 0x28}, + {WCD9XXX_A_NCP_EN, 0xff, 0xFF}, + /* Add a delay of 1ms after this reg write */ + + /* set HPHL */ + {WCD9XXX_A_RX_HPH_L_TEST, 0xff, 0x00}, + {TOMTOM_A_RX_HPH_L_PA_CTL, 0xff, 0x42}, + {TOMTOM_A_RX_HPH_BIAS_LDO, 0xff, 0x8C}, + {TOMTOM_A_RX_HPH_CHOP_CTL, 0xff, 0xA4}, + {WCD9XXX_A_RX_HPH_L_GAIN, 0xff, 0xE0}, + {WCD9XXX_A_RX_HPH_L_GAIN, 0xff, 0xEC}, + + /* set HPHR */ + {WCD9XXX_A_RX_HPH_R_TEST, 0xff, 0x00}, + {TOMTOM_A_RX_HPH_R_PA_CTL, 0xff, 0x42}, + {WCD9XXX_A_RX_HPH_R_GAIN, 0xff, 0x20}, + {WCD9XXX_A_RX_HPH_R_GAIN, 0xff, 0x2C}, + + /* set HPH PAs */ + {WCD9XXX_A_RX_HPH_BIAS_WG_OCP, 0xff, 0x2A}, + {WCD9XXX_A_RX_HPH_CNP_WG_CTL, 0xff, 0xDA}, + {WCD9XXX_A_RX_HPH_CNP_WG_TIME, 0xff, 0x15}, + {WCD9XXX_A_CDC_CLSH_B1_CTL, 0xff, 0xE6}, + {WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xff, 0x40}, + {WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xff, 0xC0}, + {WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xff, 0x40}, + {WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xff, 0xC0}, + + {TOMTOM_A_RX_HPH_L_ATEST, 0xff, 0x00}, + {TOMTOM_A_RX_HPH_R_ATEST, 0xff, 0x00}, + }; + + for (i = 0; i < ARRAY_SIZE(reg_set_paon); i++) { + /* + * Some of the codec registers like BUCK_MODE_1 + * and NCP_EN requires 1ms wait time for them + * to take effect. Other register writes for + * PA configuration do not require any wait time. + */ + if (reg_set_paon[i].reg == WCD9XXX_A_BUCK_MODE_1 || + reg_set_paon[i].reg == WCD9XXX_A_NCP_EN) + delay = 1000; + else + delay = 0; + wcd9xxx_soc_update_bits_push(codec, lh, + reg_set_paon[i].reg, + reg_set_paon[i].mask, + reg_set_paon[i].val, delay); + } + pr_debug("%s: PAs are prepared\n", __func__); + + return 0; +} + +static int wcd9xxx_enable_static_pa(struct wcd9xxx_mbhc *mbhc, bool enable, + u8 hph_pa) +{ + struct snd_soc_codec *codec = mbhc->codec; + const int wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) * + TOMTOM_WG_TIME_FACTOR_US; + u8 mask = (hph_pa << 4); + u8 pa_en = enable ? mask : ~mask; + + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, mask, pa_en); + /* Wait for wave gen time to avoid pop noise */ + usleep_range(wg_time, wg_time + WCD9XXX_USLEEP_RANGE_MARGIN_US); + pr_debug("%s: PAs are %s as static mode (wg_time %d)\n", __func__, + enable ? "enabled" : "disabled", wg_time); + return 0; +} + +static int tomtom_setup_zdet(struct wcd9xxx_mbhc *mbhc, + enum mbhc_impedance_detect_stages stage) +{ + int ret = 0; + struct snd_soc_codec *codec = mbhc->codec; + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + +#define __wr(reg, mask, value) \ + do { \ + ret = wcd9xxx_soc_update_bits_push(codec, \ + &tomtom->reg_save_restore, \ + reg, mask, value, 0); \ + if (ret < 0) \ + return ret; \ + } while (0) + + switch (stage) { + + case MBHC_ZDET_PRE_MEASURE: + INIT_LIST_HEAD(&tomtom->reg_save_restore); + wcd9xxx_prepare_static_pa(mbhc, &tomtom->reg_save_restore); + /* Set HPH_MBHC for zdet */ + __wr(WCD9XXX_A_MBHC_HPH, 0xff, 0xC4); + usleep_range(10, 10 + WCD9XXX_USLEEP_RANGE_MARGIN_US); + wcd9xxx_enable_static_pa(mbhc, HPH_PA_ENABLE, HPH_PA_L_R); + + /* save old value of registers and write the new value */ + __wr(WCD9XXX_A_RX_HPH_OCP_CTL, 0xff, 0x69); + __wr(WCD9XXX_A_CDC_RX1_B6_CTL, 0xff, 0x80); + __wr(WCD9XXX_A_CDC_RX2_B6_CTL, 0xff, 0x80); + /* Enable MBHC MUX, Set MUX current to 37.5uA and ADC7 */ + __wr(WCD9XXX_A_MBHC_SCALING_MUX_1, 0xff, 0xC0); + __wr(WCD9XXX_A_MBHC_SCALING_MUX_2, 0xff, 0xF0); + __wr(TOMTOM_A_TX_7_TXFE_CLKDIV, 0xff, 0x8B); + __wr(WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0xff, 0x78); + __wr(WCD9XXX_A_TX_7_MBHC_EN, 0xff, 0x8C); + __wr(WCD9XXX_A_CDC_MBHC_B1_CTL, 0xff, 0xDC); + /* Reset MBHC and set it up for STA */ + __wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xff, 0x0A); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x00); + __wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xff, 0x02); + __wr(WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL, 0xff, 0x80); + __wr(WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0xff, 0x25); + /* Wait for ~50us to let MBHC hardware settle down */ + usleep_range(50, 50 + WCD9XXX_USLEEP_RANGE_MARGIN_US); + break; + case MBHC_ZDET_POST_MEASURE: + /* 0x69 for 105 number of samples for PA RAMP */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x69); + /* Program the PA Ramp to FS_16K, L shift 1 */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL, + 0x1 << 4 | 0x6); + /* Reset the PA Ramp */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1C); + /* + * Connect the PA Ramp to PA chain and release reset with + * keep it connected. + */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1F); + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03); + + /* Start the PA ramp on HPH L and R */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x05); + /* Ramp generator takes ~30ms */ + usleep_range(TOMTOM_HPH_PA_RAMP_DELAY, + TOMTOM_HPH_PA_RAMP_DELAY + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + + /* + * Set the multiplication factor for zdet calculation + * based on the Ramp voltage and Gain used + */ + tomtom->zdet_gain_mul_fact = TOMTOM_ZDET_MUL_FACTOR_1X; + break; + case MBHC_ZDET_GAIN_0: + /* Set Gain at 1x */ + snd_soc_write(codec, TOMTOM_A_RX_HPH_L_ATEST, 0x00); + snd_soc_write(codec, TOMTOM_A_RX_HPH_R_ATEST, 0x00); + snd_soc_write(codec, TOMTOM_A_RX_HPH_L_PA_CTL, 0x42); + /* Allow 100us for gain registers to settle */ + usleep_range(100, + 100 + WCD9XXX_USLEEP_RANGE_MARGIN_US); + break; + case MBHC_ZDET_GAIN_UPDATE_1X: + /* + * Set the multiplication factor for zdet calculation + * based on the Gain value used + */ + tomtom->zdet_gain_mul_fact = TOMTOM_ZDET_MUL_FACTOR_1X; + break; + case MBHC_ZDET_GAIN_1: + /* Set Gain at 10x */ + snd_soc_write(codec, TOMTOM_A_RX_HPH_L_ATEST, 0x10); + snd_soc_write(codec, TOMTOM_A_RX_HPH_R_ATEST, 0x00); + snd_soc_write(codec, TOMTOM_A_RX_HPH_L_PA_CTL, 0x42); + /* Allow 100us for gain registers to settle */ + usleep_range(100, + 100 + WCD9XXX_USLEEP_RANGE_MARGIN_US); + + /* + * Set the multiplication factor for zdet calculation + * based on the Gain value used + */ + tomtom->zdet_gain_mul_fact = TOMTOM_ZDET_MUL_FACTOR_10X; + break; + case MBHC_ZDET_GAIN_2: + /* Set Gain at 100x */ + snd_soc_write(codec, TOMTOM_A_RX_HPH_L_ATEST, 0x00); + snd_soc_write(codec, TOMTOM_A_RX_HPH_R_ATEST, 0x10); + snd_soc_write(codec, TOMTOM_A_RX_HPH_L_PA_CTL, 0x43); + /* Allow 100us for gain registers to settle */ + usleep_range(100, + 100 + WCD9XXX_USLEEP_RANGE_MARGIN_US); + + /* + * Set the multiplication factor for zdet calculation + * based on the Gain value used + */ + tomtom->zdet_gain_mul_fact = TOMTOM_ZDET_MUL_FACTOR_100X; + break; + case MBHC_ZDET_RAMP_DISABLE: + /* Ramp HPH L & R back to Zero */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00); + /* 0x69 for 105 number of samples for PA RAMP */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x69); + /* Program the PA Ramp to FS_16K, L shift 1 */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL, + 0x1 << 4 | 0x6); + /* Reset the PA Ramp */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x17); + /* + * Connect the PA Ramp to PA chain and release reset with + * keep it connected. + */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03); + /* Start the PA ramp on HPH L and R */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x0A); + /* Ramp generator takes ~30ms to settle down */ + usleep_range(TOMTOM_HPH_PA_RAMP_DELAY, + TOMTOM_HPH_PA_RAMP_DELAY + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + break; + case MBHC_ZDET_HPHR_RAMP_DISABLE: + /* Ramp HPHR back to Zero */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00); + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x69); + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL, + 0x1 << 4 | 0x6); + /* Reset the PA Ramp */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x17); + /* + * Connect the PA Ramp to PA chain and release reset with + * keep it connected. + */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03); + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x08); + /* Ramp generator takes ~30ms to settle down */ + usleep_range(TOMTOM_HPH_PA_RAMP_DELAY, + TOMTOM_HPH_PA_RAMP_DELAY + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + break; + case MBHC_ZDET_HPHL_RAMP_DISABLE: + /* Ramp back to Zero */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00); + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x69); + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL, + 0x1 << 4 | 0x6); + /* Reset the PA Ramp */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x17); + /* + * Connect the PA Ramp to PA chain and release reset with + * keep it connected. + */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03); + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x02); + /* Ramp generator takes ~30ms to settle down */ + usleep_range(TOMTOM_HPH_PA_RAMP_DELAY, + TOMTOM_HPH_PA_RAMP_DELAY + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + break; + case MBHC_ZDET_HPHR_PA_DISABLE: + /* Disable PA */ + wcd9xxx_enable_static_pa(mbhc, HPH_PA_DISABLE, HPH_PA_R); + break; + case MBHC_ZDET_PA_DISABLE: + /* Disable PA */ + if (!mbhc->hph_pa_dac_state && + (!(test_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state) || + test_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state)))) + wcd9xxx_enable_static_pa(mbhc, HPH_PA_DISABLE, + HPH_PA_L_R); + else if (!(snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN) & 0x10)) + wcd9xxx_enable_static_pa(mbhc, HPH_PA_ENABLE, HPH_PA_R); + + /* Turn off PA ramp generator */ + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x00); + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00); + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL, 0x00); + snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x00); + + /* Restore registers */ + wcd9xxx_restore_registers(codec, &tomtom->reg_save_restore); + break; + } +#undef __wr + + return ret; +} + +/* Calculate final impedance values for HPH left and right based on formulae */ +static void tomtom_compute_impedance(struct wcd9xxx_mbhc *mbhc, s16 *l, s16 *r, + uint32_t *zl, uint32_t *zr) +{ + s64 zln, zrn; + int zld, zrd; + s64 rl = 0, rr = 0; + struct snd_soc_codec *codec; + struct tomtom_priv *tomtom; + + if (!mbhc) { + pr_err("%s: Invalid parameters mbhc = %pK\n", + __func__, mbhc); + return; + } + codec = mbhc->codec; + tomtom = snd_soc_codec_get_drvdata(codec); + + if (l && zl) { + zln = (s64) (l[1] - l[0]) * tomtom->zdet_gain_mul_fact; + zld = (l[2] - l[0]); + if (zld) + rl = div_s64(zln, zld); + else + /* If L0 and L2 are same, Z has to be on Zone 3. + * Assign a default value so that atleast the value + * is read again with Ramp-up + */ + rl = TOMTOM_ZDET_ZONE_3_DEFAULT_VAL; + + /* 32-bit LSBs are enough to hold Impedance values */ + *zl = (u32) rl; + } + if (r && zr) { + zrn = (s64) (r[1] - r[0]) * tomtom->zdet_gain_mul_fact; + zrd = (r[2] - r[0]); + if (zrd) + rr = div_s64(zrn, zrd); + else + /* If R0 and R2 are same, Z has to be on Zone 3. + * Assign a default value so that atleast the value + * is read again with Ramp-up + */ + rr = TOMTOM_ZDET_ZONE_3_DEFAULT_VAL; + + /* 32-bit LSBs are enough to hold Impedance values */ + *zr = (u32) rr; + } +} + +/* + * Calculate error approximation of impedance values for HPH left + * and HPH right based on QFuse values + */ +static void tomtom_zdet_error_approx(struct wcd9xxx_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + struct snd_soc_codec *codec; + struct tomtom_priv *tomtom; + s8 q1_t, q2_t; + s8 q1_m, q2_m; + s8 q1, q2; + u8 div_shift; + int rl_alpha = 0, rr_alpha = 0; + int rl_beta = 0, rr_beta = 0; + u64 rl = 0, rr = 0; + const int mult_factor = TOMTOM_ZDET_ERROR_APPROX_MUL_FACTOR; + const int shift = TOMTOM_ZDET_ERROR_APPROX_SHIFT; + + if (!zl || !zr || !mbhc) { + pr_err("%s: Invalid parameters zl = %pK zr = %pK, mbhc = %pK\n", + __func__, zl, zr, mbhc); + return; + } + codec = mbhc->codec; + tomtom = snd_soc_codec_get_drvdata(codec); + + if ((tomtom->zdet_gain_mul_fact == TOMTOM_ZDET_MUL_FACTOR_1X) || + (tomtom->zdet_gain_mul_fact == TOMTOM_ZDET_MUL_FACTOR_10X)) { + q1_t = ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT0) & + 0x3) << 0x5); + q1_t |= ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT1) & + 0xF8) >> 0x3); + q2_t = ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT1) & + 0x7) << 0x4); + q2_t |= ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT2) & + 0xF0) >> 0x4); + /* Take out the numeric part of the Qfuse value */ + q1_m = q1_t & 0x3F; + q2_m = q2_t & 0x3F; + /* Check the sign part of the Qfuse and adjust value */ + q1 = (q1_t & 0x40) ? -q1_m : q1_m; + q2 = (q2_t & 0x40) ? -q2_m : q2_m; + div_shift = 1; + } else { + q1_t = ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT2) & + 0xF) << 0x2); + q1_t |= ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT3) & + 0xC0) >> 0x6); + q2_t = (snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT3) & 0x3F); + /* Take out the numeric part of the Qfuse value */ + q1_m = q1_t & 0x1F; + q2_m = q2_t & 0x1F; + /* Check the sign part of the Qfuse and adjust value */ + q1 = (q1_t & 0x20) ? -q1_m : q1_m; + q2 = (q2_t & 0x20) ? -q2_m : q2_m; + div_shift = 0; + } + + dev_dbg(codec->dev, "%s: qfuse1 = %d, qfuse2 = %d\n", + __func__, q1, q2); + if (!q1 && !q2) { + dev_dbg(codec->dev, "%s: qfuse1 and qfuse2 are 0. Exiting\n", + __func__); + return; + } + + /* + * Use multiplication and shift to avoid floating point math + * The Z value is calculated with the below formulae using + * the Qfuse value- + * zl = zl * [1 - {(Q1 / div) / 100}] (Include sign for Q1) + * zr = zr * [1 - {(Q2 / div) / 100}] (Include sign for Q2) + * We multiply by 65536 and shift 16 times to get the approx result + * div = 4 for 1x gain, div = 2 for 10x/100x gain + */ + /* Q1/4 */ + rl_alpha = q1 >> div_shift; + rl_alpha = 100 - rl_alpha; + /* {rl_alpha/100} * 65536 */ + rl_beta = rl_alpha * mult_factor; + rl = (u64) *zl * rl_beta; + /* rl/65536 */ + rl = (u64) rl >> shift; + + rr_alpha = q2 >> div_shift; + rr_alpha = 100 - rr_alpha; + rr_beta = rr_alpha * mult_factor; + rr = (u64) *zr * rr_beta; + rr = (u64) rr >> shift; + + dev_dbg(codec->dev, "%s: rl = 0x%llx (%lld) \t rr = 0x%llx (%lld)\n", + __func__, rl, rl, rr, rr); + + *zl = (u32) rl; + *zr = (u32) rr; +} + +static enum wcd9xxx_cdc_type tomtom_get_cdc_type(void) +{ + return WCD9XXX_CDC_TYPE_TOMTOM; +} + +static bool tomtom_mbhc_ins_rem_status(struct snd_soc_codec *codec) +{ + return !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DET_STATUS) & + (1 << 4)); +} + +static void tomtom_mbhc_micb_pulldown_ctrl(struct wcd9xxx_mbhc *mbhc, + bool enable) +{ + struct snd_soc_codec *codec = mbhc->codec; + + if (!enable) { + /* Remove automatic pulldown on micbias */ + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, + 0x01, 0x00); + } else { + /* Enable automatic pulldown on micbias */ + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, + 0x01, 0x01); + } +} + +static void tomtom_codec_hph_auto_pull_down(struct snd_soc_codec *codec, + bool enable) +{ + struct wcd9xxx *tomtom_core = dev_get_drvdata(codec->dev->parent); + + if (TOMTOM_IS_1_0(tomtom_core->version)) + return; + + dev_dbg(codec->dev, "%s: %s auto pull down\n", __func__, + enable ? "enable" : "disable"); + if (enable) { + snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_L_TEST, 0x08, 0x08); + snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_R_TEST, 0x08, 0x08); + } else { + snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_L_TEST, 0x08, 0x00); + snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_R_TEST, 0x08, 0x00); + } +} + +static const struct wcd9xxx_mbhc_cb mbhc_cb = { + .get_cdc_type = tomtom_get_cdc_type, + .setup_zdet = tomtom_setup_zdet, + .compute_impedance = tomtom_compute_impedance, + .zdet_error_approx = tomtom_zdet_error_approx, + .insert_rem_status = tomtom_mbhc_ins_rem_status, + .micbias_pulldown_ctrl = tomtom_mbhc_micb_pulldown_ctrl, + .codec_rco_ctrl = tomtom_codec_internal_rco_ctrl, + .hph_auto_pulldown_ctrl = tomtom_codec_hph_auto_pull_down, + .get_hwdep_fw_cal = tomtom_get_hwdep_fw_cal, +}; + +static const struct wcd9xxx_mbhc_intr cdc_intr_ids = { + .poll_plug_rem = WCD9XXX_IRQ_MBHC_REMOVAL, + .shortavg_complete = WCD9XXX_IRQ_MBHC_SHORT_TERM, + .potential_button_press = WCD9XXX_IRQ_MBHC_PRESS, + .button_release = WCD9XXX_IRQ_MBHC_RELEASE, + .dce_est_complete = WCD9XXX_IRQ_MBHC_POTENTIAL, + .insertion = WCD9XXX_IRQ_MBHC_INSERTION, + .hph_left_ocp = WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, + .hph_right_ocp = WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, + .hs_jack_switch = WCD9330_IRQ_MBHC_JACK_SWITCH, +}; + +static int tomtom_post_reset_cb(struct wcd9xxx *wcd9xxx) +{ + int ret = 0; + struct snd_soc_codec *codec; + struct tomtom_priv *tomtom; + int rco_clk_rate; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + tomtom = snd_soc_codec_get_drvdata(codec); + + snd_soc_card_change_online_state(codec->component.card, 1); + clear_bit(BUS_DOWN, &tomtom->status_mask); + + mutex_lock(&tomtom->codec_mutex); + + tomtom_update_reg_defaults(codec); + if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, TOMTOM_A_CHIP_CTL, 0x06, 0x0); + else if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_9P6MHZ) + snd_soc_update_bits(codec, TOMTOM_A_CHIP_CTL, 0x06, 0x2); + tomtom_codec_init_reg(codec); + + snd_soc_cache_sync(codec); + + ret = tomtom_handle_pdata(tomtom); + if (IS_ERR_VALUE(ret)) + pr_err("%s: bad pdata\n", __func__); + + tomtom_init_slim_slave_cfg(codec); + tomtom_slim_interface_init_reg(codec); + wcd_cpe_ssr_event(tomtom->cpe_core, WCD_CPE_BUS_UP_EVENT); + wcd9xxx_resmgr_post_ssr(&tomtom->resmgr); + + if (tomtom->mbhc_started) { + wcd9xxx_mbhc_deinit(&tomtom->mbhc); + tomtom->mbhc_started = false; + + if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_12P288MHZ) + rco_clk_rate = TOMTOM_MCLK_CLK_12P288MHZ; + else + rco_clk_rate = TOMTOM_MCLK_CLK_9P6MHZ; + + ret = wcd9xxx_mbhc_init(&tomtom->mbhc, &tomtom->resmgr, codec, + tomtom_enable_mbhc_micbias, + &mbhc_cb, &cdc_intr_ids, + rco_clk_rate, TOMTOM_ZDET_SUPPORTED); + if (ret) + pr_err("%s: mbhc init failed %d\n", __func__, ret); + else + tomtom_hs_detect(codec, tomtom->mbhc.mbhc_cfg); + } + + if (tomtom->machine_codec_event_cb) + tomtom->machine_codec_event_cb(codec, + WCD9XXX_CODEC_EVENT_CODEC_UP); + + tomtom_cleanup_irqs(tomtom); + ret = tomtom_setup_irqs(tomtom); + if (ret) + pr_err("%s: Failed to setup irq: %d\n", __func__, ret); + + /* + * After SSR, the qfuse sensing is lost. + * Perform qfuse sensing again after SSR + * handling is finished. + */ + tomtom_enable_qfuse_sensing(codec); + mutex_unlock(&tomtom->codec_mutex); + return ret; +} + +void *tomtom_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type) +{ + struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (config_type) { + case AFE_SLIMBUS_SLAVE_CONFIG: + return &priv->slimbus_slave_cfg; + case AFE_CDC_REGISTERS_CONFIG: + return &tomtom_audio_reg_cfg; + case AFE_SLIMBUS_SLAVE_PORT_CONFIG: + return &tomtom_slimbus_slave_port_cfg; + case AFE_AANC_VERSION: + return &tomtom_cdc_aanc_version; + case AFE_CLIP_BANK_SEL: + return &clip_bank_sel; + case AFE_CDC_CLIP_REGISTERS_CONFIG: + return &tomtom_clip_reg_cfg; + default: + pr_err("%s: Unknown config_type 0x%x\n", __func__, config_type); + return NULL; + } +} + +static struct wcd9xxx_reg_address tomtom_reg_address = { + .micb_4_mbhc = TOMTOM_A_MICB_4_MBHC, + .micb_4_int_rbias = TOMTOM_A_MICB_4_INT_RBIAS, + .micb_4_ctl = TOMTOM_A_MICB_4_CTL, +}; + +static int wcd9xxx_ssr_register(struct wcd9xxx *control, + int (*device_down_cb)(struct wcd9xxx *wcd9xxx), + int (*device_up_cb)(struct wcd9xxx *wcd9xxx), + void *priv) +{ + control->dev_down = device_down_cb; + control->post_reset = device_up_cb; + control->ssr_priv = priv; + return 0; +} + +static const struct snd_soc_dapm_widget tomtom_1_dapm_widgets[] = { + SND_SOC_DAPM_ADC_E("ADC1", NULL, TOMTOM_A_TX_1_GAIN, 7, 0, + tomtom_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2", NULL, TOMTOM_A_TX_2_GAIN, 7, 0, + tomtom_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC3", NULL, TOMTOM_A_TX_3_GAIN, 7, 0, + tomtom_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC4", NULL, TOMTOM_A_TX_4_GAIN, 7, 0, + tomtom_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC5", NULL, TOMTOM_A_TX_5_GAIN, 7, 0, + tomtom_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC6", NULL, TOMTOM_A_TX_6_GAIN, 7, 0, + tomtom_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static struct regulator *tomtom_codec_find_regulator(struct snd_soc_codec *cdc, + const char *name) +{ + int i; + struct wcd9xxx *core = dev_get_drvdata(cdc->dev->parent); + + for (i = 0; i < core->num_of_supplies; i++) { + if (core->supplies[i].supply && + !strcmp(core->supplies[i].supply, name)) + return core->supplies[i].consumer; + } + + return NULL; +} + +static struct wcd_cpe_core *tomtom_codec_get_cpe_core( + struct snd_soc_codec *codec) +{ + struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec); + + return priv->cpe_core; +} + +static int tomtom_codec_fll_enable(struct snd_soc_codec *codec, + bool enable) +{ + struct wcd9xxx *wcd9xxx; + + if (!codec || !codec->control_data) { + pr_err("%s: Invalid codec handle, %pK\n", + __func__, codec); + return -EINVAL; + } + + wcd9xxx = codec->control_data; + + dev_dbg(codec->dev, "%s: %s, mclk_rate = %d\n", + __func__, (enable ? "enable" : "disable"), + wcd9xxx->mclk_rate); + + switch (wcd9xxx->mclk_rate) { + case TOMTOM_MCLK_CLK_9P6MHZ: + snd_soc_update_bits(codec, TOMTOM_A_FLL_NREF, + 0x1F, 0x15); + snd_soc_update_bits(codec, TOMTOM_A_FLL_KDCO_TUNE, + 0x07, 0x06); + snd_soc_write(codec, TOMTOM_A_FLL_LOCK_THRESH, 0xD1); + snd_soc_write(codec, TOMTOM_A_FLL_LOCK_DET_COUNT, + 0x40); + break; + case TOMTOM_MCLK_CLK_12P288MHZ: + snd_soc_update_bits(codec, TOMTOM_A_FLL_NREF, + 0x1F, 0x11); + snd_soc_update_bits(codec, TOMTOM_A_FLL_KDCO_TUNE, + 0x07, 0x05); + snd_soc_write(codec, TOMTOM_A_FLL_LOCK_THRESH, 0xB1); + snd_soc_write(codec, TOMTOM_A_FLL_LOCK_DET_COUNT, + 0x40); + break; + } + + return 0; +} + +static int tomtom_codec_slim_reserve_bw(struct snd_soc_codec *codec, + u32 bw_ops, bool commit) +{ + struct wcd9xxx *wcd9xxx; + + if (!codec) { + pr_err("%s: Invalid handle to codec\n", + __func__); + return -EINVAL; + } + + wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!wcd9xxx) { + dev_err(codec->dev, "%s: Invalid parent drv_data\n", + __func__); + return -EINVAL; + } + + return wcd9xxx_slim_reserve_bw(wcd9xxx, bw_ops, commit); +} + +static int tomtom_codec_vote_max_bw(struct snd_soc_codec *codec, + bool vote) +{ + u32 bw_ops; + + if (vote) + bw_ops = SLIM_BW_CLK_GEAR_9; + else + bw_ops = SLIM_BW_UNVOTE; + + return tomtom_codec_slim_reserve_bw(codec, + bw_ops, true); +} + +static const struct wcd9xxx_resmgr_cb resmgr_cb = { + .cdc_rco_ctrl = tomtom_codec_internal_rco_ctrl, +}; + +static int tomtom_cpe_err_irq_control(struct snd_soc_codec *codec, + enum cpe_err_irq_cntl_type cntl_type, u8 *status) +{ + switch (cntl_type) { + case CPE_ERR_IRQ_MASK: + snd_soc_update_bits(codec, + TOMTOM_A_SVASS_INT_MASK, + 0x3F, 0x3F); + break; + case CPE_ERR_IRQ_UNMASK: + snd_soc_update_bits(codec, + TOMTOM_A_SVASS_INT_MASK, + 0x3F, 0x0C); + break; + case CPE_ERR_IRQ_CLEAR: + snd_soc_update_bits(codec, + TOMTOM_A_SVASS_INT_CLR, + 0x3F, 0x3F); + break; + case CPE_ERR_IRQ_STATUS: + if (!status) + return -EINVAL; + *status = snd_soc_read(codec, + TOMTOM_A_SVASS_INT_STATUS); + break; + } + + return 0; +} + +static const struct wcd_cpe_cdc_cb cpe_cb = { + .cdc_clk_en = tomtom_codec_internal_rco_ctrl, + .cpe_clk_en = tomtom_codec_fll_enable, + .lab_cdc_ch_ctl = tomtom_codec_enable_slimtx_mad, + .cdc_ext_clk = tomtom_codec_ext_clk_en, + .bus_vote_bw = tomtom_codec_vote_max_bw, + .cpe_err_irq_control = tomtom_cpe_err_irq_control, +}; + +static struct cpe_svc_init_param cpe_svc_params = { + .version = 0, + .query_freq_plans_cb = NULL, + .change_freq_plan_cb = NULL, +}; + +static int tomtom_cpe_initialize(struct snd_soc_codec *codec) +{ + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + struct wcd_cpe_params cpe_params; + + memset(&cpe_params, 0, + sizeof(struct wcd_cpe_params)); + cpe_params.codec = codec; + cpe_params.get_cpe_core = tomtom_codec_get_cpe_core; + cpe_params.cdc_cb = &cpe_cb; + cpe_params.dbg_mode = cpe_debug_mode; + cpe_params.cdc_major_ver = CPE_SVC_CODEC_TOMTOM; + cpe_params.cdc_minor_ver = CPE_SVC_CODEC_V1P0; + cpe_params.cdc_id = CPE_SVC_CODEC_TOMTOM; + + cpe_params.cdc_irq_info.cpe_engine_irq = + WCD9330_IRQ_SVASS_ENGINE; + cpe_params.cdc_irq_info.cpe_err_irq = + WCD9330_IRQ_SVASS_ERR_EXCEPTION; + cpe_params.cdc_irq_info.cpe_fatal_irqs = + TOMTOM_CPE_FATAL_IRQS; + + cpe_svc_params.context = codec; + cpe_params.cpe_svc_params = &cpe_svc_params; + + tomtom->cpe_core = wcd_cpe_init("cpe", codec, + &cpe_params); + if (IS_ERR_OR_NULL(tomtom->cpe_core)) { + dev_err(codec->dev, + "%s: Failed to enable CPE\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int tomtom_codec_probe(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tomtom_priv *tomtom; + struct wcd9xxx_pdata *pdata; + struct wcd9xxx *wcd9xxx; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int ret = 0; + int i, rco_clk_rate; + void *ptr = NULL; + struct wcd9xxx_core_resource *core_res; + struct clk *wcd_ext_clk = NULL; + + dev_info(codec->dev, "%s()\n", __func__); + + control = dev_get_drvdata(codec->dev->parent); + + tomtom = snd_soc_codec_get_drvdata(codec); + + wcd9xxx_ssr_register(control, tomtom_device_down, + tomtom_post_reset_cb, (void *)codec); + + for (i = 0; i < NUM_DECIMATORS; i++) { + tx_hpf_work[i].tomtom = tomtom; + tx_hpf_work[i].decimator = i + 1; + tx_hpf_work[i].tx_hpf_bypass = false; + INIT_DELAYED_WORK(&tx_hpf_work[i].dwork, + tx_hpf_corner_freq_callback); + } + + wcd9xxx = control; + if (!of_find_property(wcd9xxx->dev->of_node, "clock-names", NULL)) { + dev_dbg(wcd9xxx->dev, "%s: codec not using audio-ext-clk driver\n", + __func__); + } else { + wcd_ext_clk = clk_get(wcd9xxx->dev, "wcd_clk"); + if (IS_ERR(wcd_ext_clk)) { + dev_err(codec->dev, "%s: clk get %s failed\n", + __func__, "wcd_ext_clk"); + goto err_nomem_slimch; + } + } + tomtom->wcd_ext_clk = wcd_ext_clk; + core_res = &wcd9xxx->core_res; + pdata = dev_get_platdata(codec->dev->parent); + /* codec resmgr module init */ + ret = wcd9xxx_resmgr_init(&tomtom->resmgr, codec, core_res, pdata, + &pdata->micbias, &tomtom_reg_address, + &resmgr_cb, WCD9XXX_CDC_TYPE_TOMTOM); + if (ret) { + pr_err("%s: wcd9xxx init failed %d\n", __func__, ret); + goto err_nomem_slimch; + } + + tomtom->clsh_d.buck_mv = tomtom_codec_get_buck_mv(codec); + /* TomTom does not support dynamic switching of vdd_cp */ + tomtom->clsh_d.is_dynamic_vdd_cp = false; + wcd9xxx_clsh_init(&tomtom->clsh_d, &tomtom->resmgr); + + if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_12P288MHZ) + rco_clk_rate = TOMTOM_MCLK_CLK_12P288MHZ; + else + rco_clk_rate = TOMTOM_MCLK_CLK_9P6MHZ; + + tomtom->fw_data = kzalloc(sizeof(*(tomtom->fw_data)), GFP_KERNEL); + if (!tomtom->fw_data) + goto err_nomem_slimch; + set_bit(WCD9XXX_ANC_CAL, tomtom->fw_data->cal_bit); + set_bit(WCD9XXX_MAD_CAL, tomtom->fw_data->cal_bit); + set_bit(WCD9XXX_MBHC_CAL, tomtom->fw_data->cal_bit); + ret = wcd_cal_create_hwdep(tomtom->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + goto err_hwdep; + } + + /* init and start mbhc */ + ret = wcd9xxx_mbhc_init(&tomtom->mbhc, &tomtom->resmgr, codec, + tomtom_enable_mbhc_micbias, + &mbhc_cb, &cdc_intr_ids, + rco_clk_rate, TOMTOM_ZDET_SUPPORTED); + if (ret) { + pr_err("%s: mbhc init failed %d\n", __func__, ret); + goto err_hwdep; + } + + tomtom->codec = codec; + for (i = 0; i < COMPANDER_MAX; i++) { + tomtom->comp_enabled[i] = 0; + tomtom->comp_fs[i] = COMPANDER_FS_48KHZ; + } + tomtom->intf_type = wcd9xxx_get_intf_type(); + tomtom->aux_pga_cnt = 0; + tomtom->aux_l_gain = 0x1F; + tomtom->aux_r_gain = 0x1F; + tomtom->ldo_h_users = 0; + tomtom->micb_2_users = 0; + tomtom_update_reg_defaults(codec); + pr_debug("%s: MCLK Rate = %x\n", __func__, wcd9xxx->mclk_rate); + if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, TOMTOM_A_CHIP_CTL, 0x06, 0x0); + else if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_9P6MHZ) + snd_soc_update_bits(codec, TOMTOM_A_CHIP_CTL, 0x06, 0x2); + tomtom_codec_init_reg(codec); + + ret = tomtom_handle_pdata(tomtom); + if (IS_ERR_VALUE(ret)) { + pr_err("%s: bad pdata\n", __func__); + goto err_hwdep; + } + + tomtom->spkdrv_reg = tomtom_codec_find_regulator(codec, + WCD9XXX_VDD_SPKDRV_NAME); + tomtom->spkdrv2_reg = tomtom_codec_find_regulator(codec, + WCD9XXX_VDD_SPKDRV2_NAME); + + ptr = kmalloc((sizeof(tomtom_rx_chs) + + sizeof(tomtom_tx_chs)), GFP_KERNEL); + if (!ptr) { + ret = -ENOMEM; + goto err_hwdep; + } + + if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + snd_soc_dapm_new_controls(dapm, tomtom_dapm_i2s_widgets, + ARRAY_SIZE(tomtom_dapm_i2s_widgets)); + snd_soc_dapm_add_routes(dapm, audio_i2s_map, + ARRAY_SIZE(audio_i2s_map)); + for (i = 0; i < ARRAY_SIZE(tomtom_i2s_dai); i++) + INIT_LIST_HEAD(&tomtom->dai[i].wcd9xxx_ch_list); + } else if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + for (i = 0; i < NUM_CODEC_DAIS; i++) { + INIT_LIST_HEAD(&tomtom->dai[i].wcd9xxx_ch_list); + init_waitqueue_head(&tomtom->dai[i].dai_wait); + } + tomtom_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tomtom_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tomtom_slimbus_slave_port_cfg.slave_port_mapping[0] = + TOMTOM_MAD_SLIMBUS_TX_PORT; + + tomtom_init_slim_slave_cfg(codec); + } + + snd_soc_dapm_new_controls(dapm, tomtom_1_dapm_widgets, + ARRAY_SIZE(tomtom_1_dapm_widgets)); + snd_soc_add_codec_controls(codec, + tomtom_1_x_analog_gain_controls, + ARRAY_SIZE(tomtom_1_x_analog_gain_controls)); + + snd_soc_add_codec_controls(codec, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_codec_controls(codec, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + control->num_rx_port = TOMTOM_RX_MAX; + control->rx_chs = ptr; + memcpy(control->rx_chs, tomtom_rx_chs, sizeof(tomtom_rx_chs)); + control->num_tx_port = TOMTOM_TX_MAX; + control->tx_chs = ptr + sizeof(tomtom_rx_chs); + memcpy(control->tx_chs, tomtom_tx_chs, sizeof(tomtom_tx_chs)); + + snd_soc_dapm_sync(dapm); + + ret = tomtom_setup_irqs(tomtom); + if (ret) { + pr_err("%s: tomtom irq setup failed %d\n", __func__, ret); + goto err_pdata; + } + + atomic_set(&kp_tomtom_priv, (unsigned long)tomtom); + mutex_lock(&tomtom->codec_mutex); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_disable_pin(dapm, "ANC HEADPHONE"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + mutex_unlock(&tomtom->codec_mutex); + snd_soc_dapm_sync(dapm); + + codec->component.ignore_pmdown_time = 1; + ret = tomtom_cpe_initialize(codec); + if (ret) { + dev_info(codec->dev, + "%s: cpe initialization failed, ret = %d\n", + __func__, ret); + /* Do not fail probe if CPE failed */ + ret = 0; + } + return ret; + +err_pdata: + kfree(ptr); + control->rx_chs = NULL; + control->tx_chs = NULL; +err_hwdep: + kfree(tomtom->fw_data); + tomtom->fw_data = NULL; +err_nomem_slimch: + devm_kfree(codec->dev, tomtom); + return ret; +} +static int tomtom_codec_remove(struct snd_soc_codec *codec) +{ + struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *control; + + WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr); + atomic_set(&kp_tomtom_priv, 0); + + WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr); + + control = dev_get_drvdata(codec->dev->parent); + control->rx_chs = NULL; + control->tx_chs = NULL; + + if (tomtom->wcd_ext_clk) + clk_put(tomtom->wcd_ext_clk); + tomtom_cleanup_irqs(tomtom); + + /* cleanup MBHC */ + wcd9xxx_mbhc_deinit(&tomtom->mbhc); + /* cleanup resmgr */ + wcd9xxx_resmgr_deinit(&tomtom->resmgr); + + tomtom->spkdrv_reg = NULL; + tomtom->spkdrv2_reg = NULL; + + devm_kfree(codec->dev, tomtom); + return 0; +} + +static struct regmap *tomtom_get_regmap(struct device *dev) +{ + struct wcd9xxx *control = dev_get_drvdata(dev->parent); + + return control->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_tomtom = { + .probe = tomtom_codec_probe, + .remove = tomtom_codec_remove, + .controls = tomtom_snd_controls, + .num_controls = ARRAY_SIZE(tomtom_snd_controls), + .dapm_widgets = tomtom_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tomtom_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + .get_regmap = tomtom_get_regmap, +}; + +#ifdef CONFIG_PM +static int tomtom_suspend(struct device *dev) +{ + dev_dbg(dev, "%s: system suspend\n", __func__); + return 0; +} + +static int tomtom_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tomtom_priv *tomtom = platform_get_drvdata(pdev); + + if (!tomtom) { + dev_err(dev, "%s: tomtom private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system resume\n", __func__); + /* Notify */ + wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, + WCD9XXX_EVENT_POST_RESUME); + return 0; +} + +static const struct dev_pm_ops tomtom_pm_ops = { + .suspend = tomtom_suspend, + .resume = tomtom_resume, +}; +#endif + +static int tomtom_probe(struct platform_device *pdev) +{ + int ret = 0; + struct tomtom_priv *tomtom; + + tomtom = devm_kzalloc(&pdev->dev, sizeof(struct tomtom_priv), + GFP_KERNEL); + if (!tomtom) + return -ENOMEM; + + platform_set_drvdata(pdev, tomtom); + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tomtom, + tomtom_dai, ARRAY_SIZE(tomtom_dai)); + else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tomtom, + tomtom_i2s_dai, ARRAY_SIZE(tomtom_i2s_dai)); + mutex_init(&tomtom->codec_mutex); + return ret; +} +static int tomtom_remove(struct platform_device *pdev) +{ + struct tomtom_priv *tomtom = platform_get_drvdata(pdev); + + mutex_destroy(&tomtom->codec_mutex); + snd_soc_unregister_codec(&pdev->dev); + return 0; +} +static struct platform_driver tomtom_codec_driver = { + .probe = tomtom_probe, + .remove = tomtom_remove, + .driver = { + .name = "tomtom_codec", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &tomtom_pm_ops, +#endif + }, +}; + +static int __init tomtom_codec_init(void) +{ + return platform_driver_register(&tomtom_codec_driver); +} + +static void __exit tomtom_codec_exit(void) +{ + platform_driver_unregister(&tomtom_codec_driver); +} + +module_init(tomtom_codec_init); +module_exit(tomtom_codec_exit); + +MODULE_DESCRIPTION("TomTom codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9330.h b/sound/soc/codecs/wcd9330.h new file mode 100644 index 0000000000000000000000000000000000000000..8679d013729af18e13d2c8c38c0898ec040e8053 --- /dev/null +++ b/sound/soc/codecs/wcd9330.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef WCD9330_H +#define WCD9330_H + +#include +#include +#include +#include +#include "wcd9xxx-mbhc.h" +#include "wcd9xxx-resmgr.h" +#include "wcd9xxx-common.h" + +#define TOMTOM_NUM_REGISTERS 0x400 +#define TOMTOM_MAX_REGISTER (TOMTOM_NUM_REGISTERS-1) +#define TOMTOM_CACHE_SIZE TOMTOM_NUM_REGISTERS + +#define TOMTOM_REG_VAL(reg, val) {reg, 0, val} +#define TOMTOM_MCLK_ID 0 + +#define TOMTOM_REGISTER_START_OFFSET 0x800 +#define TOMTOM_SB_PGD_PORT_RX_BASE 0x40 +#define TOMTOM_SB_PGD_PORT_TX_BASE 0x50 + +#define WCD9330_DMIC_CLK_DIV_2 0x00 +#define WCD9330_DMIC_CLK_DIV_3 0x01 +#define WCD9330_DMIC_CLK_DIV_4 0x02 +#define WCD9330_DMIC_CLK_DIV_6 0x03 +#define WCD9330_DMIC_CLK_DIV_16 0x04 + +#define TOMTOM_ZDET_SUPPORTED true + +extern const u8 tomtom_reset_reg_defaults[TOMTOM_CACHE_SIZE]; +struct tomtom_codec_dai_data { + u32 rate; + u32 *ch_num; + u32 ch_act; + u32 ch_tot; +}; + +enum tomtom_pid_current { + TOMTOM_PID_MIC_2P5_UA, + TOMTOM_PID_MIC_5_UA, + TOMTOM_PID_MIC_10_UA, + TOMTOM_PID_MIC_20_UA, +}; + +enum tomtom_mbhc_analog_pwr_cfg { + TOMTOM_ANALOG_PWR_COLLAPSED = 0, + TOMTOM_ANALOG_PWR_ON, + TOMTOM_NUM_ANALOG_PWR_CONFIGS, +}; + +enum { + HPH_PA_NONE = 0, + HPH_PA_R, + HPH_PA_L, + HPH_PA_L_R, +}; + +/* Number of input and output Slimbus port */ +enum { + TOMTOM_RX1 = 0, + TOMTOM_RX2, + TOMTOM_RX3, + TOMTOM_RX4, + TOMTOM_RX5, + TOMTOM_RX6, + TOMTOM_RX7, + TOMTOM_RX8, + TOMTOM_RX9, + TOMTOM_RX10, + TOMTOM_RX11, + TOMTOM_RX12, + TOMTOM_RX13, + TOMTOM_RX_MAX, +}; + +enum { + TOMTOM_TX1 = 0, + TOMTOM_TX2, + TOMTOM_TX3, + TOMTOM_TX4, + TOMTOM_TX5, + TOMTOM_TX6, + TOMTOM_TX7, + TOMTOM_TX8, + TOMTOM_TX9, + TOMTOM_TX10, + TOMTOM_TX11, + TOMTOM_TX12, + TOMTOM_TX13, + TOMTOM_TX14, + TOMTOM_TX15, + TOMTOM_TX16, + TOMTOM_TX_MAX, +}; + +extern int tomtom_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, + bool dapm); +extern int tomtom_codec_mclk_enable(struct snd_soc_codec *codec, + int mclk_enable, bool dapm); +extern int tomtom_hs_detect(struct snd_soc_codec *codec, + struct wcd9xxx_mbhc_config *mbhc_cfg); +extern void tomtom_hs_detect_exit(struct snd_soc_codec *codec); +extern void *tomtom_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type); + +extern void tomtom_event_register( + int (*machine_event_cb)(struct snd_soc_codec *codec, + enum wcd9xxx_codec_event), + struct snd_soc_codec *codec); +extern void tomtom_register_ext_clk_cb( + int (*codec_ext_clk_en)(struct snd_soc_codec *codec, + int enable, bool dapm), + int (*get_ext_clk_cnt)(void), + struct snd_soc_codec *codec); +extern int tomtom_enable_qfuse_sensing(struct snd_soc_codec *codec); +#endif diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c new file mode 100644 index 0000000000000000000000000000000000000000..337d347a7c639d42bf982b517c33dfc95a87f483 --- /dev/null +++ b/sound/soc/codecs/wcd9335.c @@ -0,0 +1,14378 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd9335.h" +#include "wcd-mbhc-v2.h" +#include "wcd9xxx-common-v2.h" +#include "wcd9xxx-resmgr-v2.h" +#include "wcd_cpe_core.h" +#include "wcdcal-hwdep.h" + +#define TASHA_RX_PORT_START_NUMBER 16 + +#define WCD9335_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 WCD9335_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100) + +#define WCD9335_MIX_RATES_MASK (SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) + +#define TASHA_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define TASHA_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define TASHA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +/* + * Timeout in milli seconds and it is the wait time for + * slim channel removal interrupt to receive. + */ +#define TASHA_SLIM_CLOSE_TIMEOUT 1000 +#define TASHA_SLIM_IRQ_OVERFLOW (1 << 0) +#define TASHA_SLIM_IRQ_UNDERFLOW (1 << 1) +#define TASHA_SLIM_IRQ_PORT_CLOSED (1 << 2) +#define TASHA_MCLK_CLK_12P288MHZ 12288000 +#define TASHA_MCLK_CLK_9P6MHZ 9600000 + +#define TASHA_SLIM_PGD_PORT_INT_TX_EN0 (TASHA_SLIM_PGD_PORT_INT_EN0 + 2) + +#define TASHA_NUM_INTERPOLATORS 9 +#define TASHA_NUM_DECIMATORS 9 + +#define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE)) +#define TASHA_MAD_AUDIO_FIRMWARE_PATH "wcd9335/wcd9335_mad_audio.bin" +#define TASHA_CPE_SS_ERR_STATUS_MEM_ACCESS (1 << 0) +#define TASHA_CPE_SS_ERR_STATUS_WDOG_BITE (1 << 1) + +#define TASHA_CPE_FATAL_IRQS \ + (TASHA_CPE_SS_ERR_STATUS_WDOG_BITE | \ + TASHA_CPE_SS_ERR_STATUS_MEM_ACCESS) + +#define SLIM_BW_CLK_GEAR_9 6200000 +#define SLIM_BW_UNVOTE 0 + +#define CPE_FLL_CLK_75MHZ 75000000 +#define CPE_FLL_CLK_150MHZ 150000000 +#define WCD9335_REG_BITS 8 + +#define WCD9335_MAX_VALID_ADC_MUX 13 +#define WCD9335_INVALID_ADC_MUX 9 + +#define TASHA_DIG_CORE_REG_MIN WCD9335_CDC_ANC0_CLK_RESET_CTL +#define TASHA_DIG_CORE_REG_MAX 0xDFF + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) + +#define TASHA_ZDET_NUM_MEASUREMENTS 150 +#define TASHA_MBHC_GET_C1(c) ((c & 0xC000) >> 14) +#define TASHA_MBHC_GET_X1(x) (x & 0x3FFF) +/* z value compared in milliOhm */ +#define TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000)) +#define TASHA_MBHC_ZDET_CONST (86 * 16384) +#define TASHA_MBHC_MOISTURE_VREF V_45_MV +#define TASHA_MBHC_MOISTURE_IREF I_3P0_UA + +#define TASHA_VERSION_ENTRY_SIZE 17 + +#define WCD9335_AMIC_PWR_LEVEL_LP 0 +#define WCD9335_AMIC_PWR_LEVEL_DEFAULT 1 +#define WCD9335_AMIC_PWR_LEVEL_HP 2 +#define WCD9335_AMIC_PWR_LVL_MASK 0x60 +#define WCD9335_AMIC_PWR_LVL_SHIFT 0x5 + +#define WCD9335_DEC_PWR_LVL_MASK 0x06 +#define WCD9335_DEC_PWR_LVL_LP 0x02 +#define WCD9335_DEC_PWR_LVL_HP 0x04 +#define WCD9335_DEC_PWR_LVL_DF 0x00 +#define WCD9335_STRING_LEN 100 + +#define CALCULATE_VOUT_D(req_mv) (((req_mv - 650) * 10) / 25) + +static int cpe_debug_mode; + +#define TASHA_MAX_MICBIAS 4 +#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone" +#define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone" +#define DAPM_MICBIAS3_STANDALONE "MIC BIAS3 Standalone" +#define DAPM_MICBIAS4_STANDALONE "MIC BIAS4 Standalone" + +#define DAPM_LDO_H_STANDALONE "LDO_H" +module_param(cpe_debug_mode, int, 0664); +MODULE_PARM_DESC(cpe_debug_mode, "boot cpe in debug mode"); + +#define TASHA_DIG_CORE_COLLAPSE_TIMER_MS (5 * 1000) + +#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64 + +static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = { + "cdc-vdd-mic-bias", +}; + +enum { + POWER_COLLAPSE, + POWER_RESUME, +}; + +enum tasha_sido_voltage { + SIDO_VOLTAGE_SVS_MV = 950, + SIDO_VOLTAGE_NOMINAL_MV = 1100, +}; + +static enum codec_variant codec_ver; + +static int dig_core_collapse_enable = 1; +module_param(dig_core_collapse_enable, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_enable, "enable/disable power gating"); + +/* dig_core_collapse timer in seconds */ +static int dig_core_collapse_timer = (TASHA_DIG_CORE_COLLAPSE_TIMER_MS/1000); +module_param(dig_core_collapse_timer, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_timer, "timer for power gating"); + +/* SVS Scaling enable/disable */ +static int svs_scaling_enabled = 1; +module_param(svs_scaling_enabled, int, 0664); +MODULE_PARM_DESC(svs_scaling_enabled, "enable/disable svs scaling"); + +/* SVS buck setting */ +static int sido_buck_svs_voltage = SIDO_VOLTAGE_SVS_MV; +module_param(sido_buck_svs_voltage, int, 0664); +MODULE_PARM_DESC(sido_buck_svs_voltage, + "setting for SVS voltage for SIDO BUCK"); + +#define TASHA_TX_UNMUTE_DELAY_MS 25 + +static int tx_unmute_delay = TASHA_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + +static struct afe_param_slimbus_slave_port_cfg tasha_slimbus_slave_port_cfg = { + .minor_version = 1, + .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1, + .slave_dev_pgd_la = 0, + .slave_dev_intfdev_la = 0, + .bit_width = 16, + .data_format = 0, + .num_channels = 1 +}; + +struct tasha_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static struct afe_param_cdc_reg_page_cfg tasha_cdc_reg_page_cfg = { + .minor_version = AFE_API_VERSION_CDC_REG_PAGE_CFG, + .enable = 1, + .proc_id = AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1, +}; + +static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = { + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_MAIN_CTL_1), + HW_MAD_AUDIO_ENABLE, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_AUDIO_CTL_3), + HW_MAD_AUDIO_SLEEP_TIME, 0xF, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_AUDIO_CTL_4), + HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG), + MAD_AUDIO_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3), + MAD_AUDIO_INT_MASK_REG, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3), + MAD_AUDIO_INT_STATUS_REG, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3), + MAD_AUDIO_INT_CLEAR_REG, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG), + VBAT_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3), + VBAT_INT_MASK_REG, 0x08, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3), + VBAT_INT_STATUS_REG, 0x08, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3), + VBAT_INT_CLEAR_REG, 0x08, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG), + VBAT_RELEASE_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3), + VBAT_RELEASE_INT_MASK_REG, 0x10, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3), + VBAT_RELEASE_INT_STATUS_REG, 0x10, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3), + VBAT_RELEASE_INT_CLEAR_REG, 0x10, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_WATERMARK_N, 0x1E, WCD9335_REG_BITS, 0x1 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_ENABLE_N, 0x1, WCD9335_REG_BITS, 0x1 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_WATERMARK_N, 0x1E, WCD9335_REG_BITS, 0x1 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD9335_REG_BITS, 0x1 + }, + { 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FF_GAIN_ADAPTIVE, 0x4, WCD9335_REG_BITS, 0 + }, + { 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_FF_A_GAIN_CTL), + AANC_GAIN_CONTROL, 0xFF, WCD9335_REG_BITS, 0 + }, +}; + +static struct afe_param_cdc_reg_cfg_data tasha_audio_reg_cfg = { + .num_registers = ARRAY_SIZE(audio_reg_cfg), + .reg_data = audio_reg_cfg, +}; + +static struct afe_param_id_cdc_aanc_version tasha_cdc_aanc_version = { + .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION, + .aanc_hw_version = AANC_HW_BLOCK_VERSION_2, +}; + +enum { + VI_SENSE_1, + VI_SENSE_2, + AIF4_SWITCH_VALUE, + AUDIO_NOMINAL, + CPE_NOMINAL, + HPH_PA_DELAY, + SB_CLK_GEAR, + ANC_MIC_AMIC1, + ANC_MIC_AMIC2, + ANC_MIC_AMIC3, + ANC_MIC_AMIC4, + ANC_MIC_AMIC5, + ANC_MIC_AMIC6, + CLASSH_CONFIG, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_PB, + AIF2_CAP, + AIF3_PB, + AIF3_CAP, + AIF4_PB, + AIF_MIX1_PB, + AIF4_MAD_TX, + AIF4_VIFEED, + AIF5_CPE_TX, + NUM_CODEC_DAIS, +}; + +enum { + INTn_1_MIX_INP_SEL_ZERO = 0, + INTn_1_MIX_INP_SEL_DEC0, + INTn_1_MIX_INP_SEL_DEC1, + INTn_1_MIX_INP_SEL_IIR0, + INTn_1_MIX_INP_SEL_IIR1, + INTn_1_MIX_INP_SEL_RX0, + INTn_1_MIX_INP_SEL_RX1, + INTn_1_MIX_INP_SEL_RX2, + INTn_1_MIX_INP_SEL_RX3, + INTn_1_MIX_INP_SEL_RX4, + INTn_1_MIX_INP_SEL_RX5, + INTn_1_MIX_INP_SEL_RX6, + INTn_1_MIX_INP_SEL_RX7, + +}; + +#define IS_VALID_NATIVE_FIFO_PORT(inp) \ + ((inp >= INTn_1_MIX_INP_SEL_RX0) && \ + (inp <= INTn_1_MIX_INP_SEL_RX3)) + +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_EAR = 0, + INTERP_HPHL, + INTERP_HPHR, + INTERP_LO1, + INTERP_LO2, + INTERP_LO3, + INTERP_LO4, + INTERP_SPKR1, + INTERP_SPKR2, +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate int_prim_sample_rate_val[] = { + {8000, 0x0}, /* 8K */ + {16000, 0x1}, /* 16K */ + {24000, -EINVAL},/* 24K */ + {32000, 0x3}, /* 32K */ + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ + {384000, 0x7}, /* 384K */ + {44100, 0x8}, /* 44.1K */ +}; + +static struct interp_sample_rate int_mix_sample_rate_val[] = { + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ +}; + +static const struct wcd9xxx_ch tasha_rx_chs[TASHA_RX_MAX] = { + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER, 0), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 1, 1), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 2, 2), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 3, 3), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 4, 4), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 5, 5), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 6, 6), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 7, 7), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 8, 8), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 9, 9), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 10, 10), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 11, 11), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 12, 12), +}; + +static const struct wcd9xxx_ch tasha_tx_chs[TASHA_TX_MAX] = { + WCD9XXX_CH(0, 0), + WCD9XXX_CH(1, 1), + WCD9XXX_CH(2, 2), + WCD9XXX_CH(3, 3), + WCD9XXX_CH(4, 4), + WCD9XXX_CH(5, 5), + WCD9XXX_CH(6, 6), + WCD9XXX_CH(7, 7), + WCD9XXX_CH(8, 8), + WCD9XXX_CH(9, 9), + WCD9XXX_CH(10, 10), + WCD9XXX_CH(11, 11), + WCD9XXX_CH(12, 12), + WCD9XXX_CH(13, 13), + WCD9XXX_CH(14, 14), + WCD9XXX_CH(15, 15), +}; + +static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = { + /* Needs to define in the same order of DAI enum definitions */ + 0, + BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), +}; + +static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = { + 0, /* AIF1_PB */ + BIT(AIF2_CAP), /* AIF1_CAP */ + 0, /* AIF2_PB */ + BIT(AIF1_CAP), /* AIF2_CAP */ +}; + +/* 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 */ + COMPANDER_6, /* LO4_SE */ + COMPANDER_7, /* SWR SPK CH1 */ + COMPANDER_8, /* SWR SPK CH2 */ + COMPANDER_MAX, +}; + +enum { + SRC_IN_HPHL, + SRC_IN_LO1, + SRC_IN_HPHR, + SRC_IN_LO2, + SRC_IN_SPKRL, + SRC_IN_LO3, + SRC_IN_SPKRR, + SRC_IN_LO4, +}; + +enum { + SPLINE_SRC0, + SPLINE_SRC1, + SPLINE_SRC2, + SPLINE_SRC3, + SPLINE_SRC_MAX, +}; + +/* wcd9335 interrupt table */ +static const struct intr_data wcd9335_intr_table[] = { + {WCD9XXX_IRQ_SLIMBUS, false}, + {WCD9335_IRQ_MBHC_SW_DET, true}, + {WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, true}, + {WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, true}, + {WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, true}, + {WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true}, + {WCD9335_IRQ_FLL_LOCK_LOSS, false}, + {WCD9335_IRQ_HPH_PA_CNPL_COMPLETE, false}, + {WCD9335_IRQ_HPH_PA_CNPR_COMPLETE, false}, + {WCD9335_IRQ_EAR_PA_CNP_COMPLETE, false}, + {WCD9335_IRQ_LINE_PA1_CNP_COMPLETE, false}, + {WCD9335_IRQ_LINE_PA2_CNP_COMPLETE, false}, + {WCD9335_IRQ_LINE_PA3_CNP_COMPLETE, false}, + {WCD9335_IRQ_LINE_PA4_CNP_COMPLETE, false}, + {WCD9335_IRQ_HPH_PA_OCPL_FAULT, false}, + {WCD9335_IRQ_HPH_PA_OCPR_FAULT, false}, + {WCD9335_IRQ_EAR_PA_OCP_FAULT, false}, + {WCD9335_IRQ_SOUNDWIRE, false}, + {WCD9335_IRQ_VDD_DIG_RAMP_COMPLETE, false}, + {WCD9335_IRQ_RCO_ERROR, false}, + {WCD9335_IRQ_SVA_ERROR, false}, + {WCD9335_IRQ_MAD_AUDIO, false}, + {WCD9335_IRQ_MAD_BEACON, false}, + {WCD9335_IRQ_SVA_OUTBOX1, true}, + {WCD9335_IRQ_SVA_OUTBOX2, true}, + {WCD9335_IRQ_MAD_ULTRASOUND, false}, + {WCD9335_IRQ_VBAT_ATTACK, false}, + {WCD9335_IRQ_VBAT_RESTORE, false}, +}; + +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 struct snd_soc_dai_driver tasha_dai[]; +static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv); + +static int tasha_config_compander(struct snd_soc_codec *, int, int); +static void tasha_codec_set_tx_hold(struct snd_soc_codec *, u16, bool); +static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable); + +/* Hold instance to soundwire platform device */ +struct tasha_swr_ctrl_data { + struct platform_device *swr_pdev; + struct ida swr_ida; +}; + +struct wcd_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action); +}; + +static struct wcd_mbhc_register + wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN", + WCD9335_ANA_MBHC_MECH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN", + WCD9335_ANA_MBHC_MECH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE", + WCD9335_ANA_MBHC_MECH, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL", + WCD9335_MBHC_PLUG_DETECT_CTL, 0x30, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE", + WCD9335_ANA_MBHC_ELECT, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL", + WCD9335_MBHC_PLUG_DETECT_CTL, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL", + WCD9335_ANA_MBHC_MECH, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE", + WCD9335_ANA_MBHC_MECH, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE", + WCD9335_ANA_MBHC_MECH, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND", + WCD9335_ANA_MBHC_MECH, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC", + WCD9335_ANA_MBHC_ELECT, 0x06, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN", + WCD9335_ANA_MBHC_ELECT, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC", + WCD9335_MBHC_PLUG_DETECT_CTL, 0x0F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC", + WCD9335_MBHC_CTL_1, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF", + WCD9335_MBHC_CTL_2, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN", + WCD9335_HPH_OCP_CTL, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x07, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL", + WCD9335_ANA_MBHC_ELECT, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL", + WCD9335_ANA_MICB2, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME", + WCD9335_HPH_CNP_WG_TIME, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN", + WCD9335_ANA_HPH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN", + WCD9335_ANA_HPH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN", + WCD9335_ANA_HPH, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE", + WCD9335_ANA_MBHC_RESULT_3, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", + WCD9335_ANA_MBHC_ZDET, 0x01, 0, 0), + /* + * MBHC FSM status register is only available in Tasha 2.0. + * So, init with 0 later once the version is known, then values + * will be updated. + */ + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", + WCD9335_MBHC_CTL_2, 0x70, 4, 0), +}; + +static const struct wcd_mbhc_intr intr_ids = { + .mbhc_sw_intr = WCD9335_IRQ_MBHC_SW_DET, + .mbhc_btn_press_intr = WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, + .mbhc_btn_release_intr = WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, + .mbhc_hs_ins_intr = WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + .mbhc_hs_rem_intr = WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, + .hph_left_ocp = WCD9335_IRQ_HPH_PA_OCPL_FAULT, + .hph_right_ocp = WCD9335_IRQ_HPH_PA_OCPR_FAULT, +}; + +struct wcd_vbat { + bool is_enabled; + bool adc_config; + /* Variables to cache Vbat ADC output values */ + u16 dcp1; + u16 dcp2; +}; + +struct hpf_work { + struct tasha_priv *tasha; + u8 decimator; + u8 hpf_cut_off_freq; + struct delayed_work dwork; +}; + +#define WCD9335_SPK_ANC_EN_DELAY_MS 350 +static int spk_anc_en_delay = WCD9335_SPK_ANC_EN_DELAY_MS; +module_param(spk_anc_en_delay, int, 0664); +MODULE_PARM_DESC(spk_anc_en_delay, "delay to enable anc in speaker path"); + +struct spk_anc_work { + struct tasha_priv *tasha; + struct delayed_work dwork; +}; + +struct tx_mute_work { + struct tasha_priv *tasha; + u8 decimator; + struct delayed_work dwork; +}; + +struct tasha_priv { + struct device *dev; + struct wcd9xxx *wcd9xxx; + + struct snd_soc_codec *codec; + u32 adc_count; + u32 rx_bias_count; + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + s32 ldo_h_users; + s32 micb_ref[TASHA_MAX_MICBIAS]; + s32 pullup_ref[TASHA_MAX_MICBIAS]; + + u32 anc_slot; + bool anc_func; + + /* Vbat module */ + struct wcd_vbat vbat; + + /* cal info for codec */ + struct fw_info *fw_data; + + /*track tasha interface type*/ + u8 intf_type; + + /* num of slim ports required */ + struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS]; + + /* SoundWire data structure */ + struct tasha_swr_ctrl_data *swr_ctrl_data; + int nr; + + /*compander*/ + int comp_enabled[COMPANDER_MAX]; + + /* Maintain the status of AUX PGA */ + int aux_pga_cnt; + u8 aux_l_gain; + u8 aux_r_gain; + + bool spkr_pa_widget_on; + struct regulator *spkdrv_reg; + struct regulator *spkdrv2_reg; + + bool mbhc_started; + /* class h specific data */ + struct wcd_clsh_cdc_data clsh_d; + + struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg; + + /* + * list used to save/restore registers at start and + * end of impedance measurement + */ + struct list_head reg_save_restore; + + /* handle to cpe core */ + struct wcd_cpe_core *cpe_core; + u32 current_cpe_clk_freq; + enum tasha_sido_voltage sido_voltage; + int sido_ccl_cnt; + + u32 ana_rx_supplies; + /* Multiplication factor used for impedance detection */ + int zdet_gain_mul_fact; + + /* to track the status */ + unsigned long status_mask; + + struct work_struct tasha_add_child_devices_work; + struct wcd_swr_ctrl_platform_data swr_plat_data; + + /* Port values for Rx and Tx codec_dai */ + unsigned int rx_port_value[TASHA_RX_MAX]; + unsigned int tx_port_value; + + unsigned int vi_feed_value; + /* Tasha Interpolator Mode Select for EAR, HPH_L and HPH_R */ + u32 hph_mode; + + u16 prim_int_users[TASHA_NUM_INTERPOLATORS]; + int spl_src_users[SPLINE_SRC_MAX]; + + struct wcd9xxx_resmgr_v2 *resmgr; + struct delayed_work power_gate_work; + struct mutex power_lock; + struct mutex sido_lock; + + /* mbhc module */ + struct wcd_mbhc mbhc; + struct blocking_notifier_head notifier; + struct mutex micb_lock; + + struct clk *wcd_ext_clk; + struct clk *wcd_native_clk; + struct mutex swr_read_lock; + struct mutex swr_write_lock; + struct mutex swr_clk_lock; + int swr_clk_users; + int native_clk_users; + int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high); + + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + int power_active_ref; + + struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX]; + + int (*machine_codec_event_cb)(struct snd_soc_codec *codec, + enum wcd9335_codec_event); + int spkr_gain_offset; + int spkr_mode; + int ear_spkr_gain; + struct hpf_work tx_hpf_work[TASHA_NUM_DECIMATORS]; + struct tx_mute_work tx_mute_dwork[TASHA_NUM_DECIMATORS]; + struct spk_anc_work spk_anc_dwork; + struct mutex codec_mutex; + int hph_l_gain; + int hph_r_gain; + int rx_7_count; + int rx_8_count; + bool clk_mode; + bool clk_internal; + + /* Lock to protect mclk enablement */ + struct mutex mclk_lock; +}; + +static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec, + bool vote); + +static const struct tasha_reg_mask_val tasha_spkr_default[] = { + {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x50}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x50}, +}; + +static const struct tasha_reg_mask_val tasha_spkr_mode1[] = { + {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x00}, + {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x00}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x00}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x00}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x44}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44}, +}; + +/* + * wcd9335_get_codec_info: Get codec specific information + * + * @wcd9xxx: pointer to wcd9xxx structure + * @wcd_type: pointer to wcd9xxx_codec_type structure + * + * Returns 0 for success or negative error code for failure + */ +int wcd9335_get_codec_info(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_codec_type *wcd_type) +{ + u16 id_minor, id_major; + struct regmap *wcd_regmap; + int rc, val, version = 0; + + if (!wcd9xxx || !wcd_type) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n", + __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + rc = regmap_bulk_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, + (u8 *)&id_minor, sizeof(u16)); + if (rc) + return -EINVAL; + + rc = regmap_bulk_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2, + (u8 *)&id_major, sizeof(u16)); + if (rc) + return -EINVAL; + + dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n", + __func__, id_major, id_minor); + + /* Version detection */ + if (id_major == TASHA_MAJOR) { + regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, + &val); + version = ((u8)val & 0x80) >> 7; + } else if (id_major == TASHA2P0_MAJOR) + version = 2; + else + dev_err(wcd9xxx->dev, "%s: wcd9335 version unknown (major 0x%x, minor 0x%x)\n", + __func__, id_major, id_minor); + + /* Fill codec type info */ + wcd_type->id_major = id_major; + wcd_type->id_minor = id_minor; + wcd_type->num_irqs = WCD9335_NUM_IRQS; + wcd_type->version = version; + wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1; + wcd_type->i2c_chip_status = 0x01; + wcd_type->intr_tbl = wcd9335_intr_table; + wcd_type->intr_tbl_size = ARRAY_SIZE(wcd9335_intr_table); + + wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] = + WCD9335_INTR_PIN1_STATUS0; + wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] = + WCD9335_INTR_PIN1_CLEAR0; + wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] = + WCD9335_INTR_PIN1_MASK0; + wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] = + WCD9335_INTR_LEVEL0; + wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] = + WCD9335_INTR_CLR_COMMIT; + + return rc; +} +EXPORT_SYMBOL(wcd9335_get_codec_info); + +/* + * wcd9335_bringdown: Bringdown WCD Codec + * + * @wcd9xxx: Pointer to wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +int wcd9335_bringdown(struct wcd9xxx *wcd9xxx) +{ + if (!wcd9xxx || !wcd9xxx->regmap) + return -EINVAL; + + regmap_write(wcd9xxx->regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x04); + + return 0; +} +EXPORT_SYMBOL(wcd9335_bringdown); + +/* + * wcd9335_bringup: Bringup WCD Codec + * + * @wcd9xxx: Pointer to the wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +int wcd9335_bringup(struct wcd9xxx *wcd9xxx) +{ + int ret = 0; + int val, byte0; + struct regmap *wcd_regmap; + + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n", + __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, &val); + regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, &byte0); + + if ((val < 0) || (byte0 < 0)) { + dev_err(wcd9xxx->dev, "%s: tasha codec version detection fail!\n", + __func__); + return -EINVAL; + } + if ((val & 0x80) && (byte0 == 0x0)) { + dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v1.1\n", + __func__); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_2, 0xFC); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_4, 0x21); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x5); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x7); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x3); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3); + } else if (byte0 == 0x1) { + dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v2.0\n", + __func__); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_TEST_2, 0x00); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_8, 0x6F); + regmap_write(wcd_regmap, WCD9335_BIAS_VBG_FINE_ADJ, 0x65); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x5); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x7); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x3); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3); + } else if ((byte0 == 0) && (!(val & 0x80))) { + dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v1.0\n", + __func__); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_2, 0xFC); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_4, 0x21); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x3); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3); + } else { + dev_err(wcd9xxx->dev, "%s: tasha codec version unknown\n", + __func__); + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL(wcd9335_bringup); + +/** + * tasha_set_spkr_gain_offset - offset the speaker path + * gain with the given offset value. + * + * @codec: codec instance + * @offset: Indicates speaker path gain offset value. + * + * Returns 0 on success or -EINVAL on error. + */ +int tasha_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (!priv) + return -EINVAL; + + priv->spkr_gain_offset = offset; + return 0; +} +EXPORT_SYMBOL(tasha_set_spkr_gain_offset); + +/** + * tasha_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @codec: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int tasha_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + int i; + const struct tasha_reg_mask_val *regs; + int size; + + if (!priv) + return -EINVAL; + + switch (mode) { + case SPKR_MODE_1: + regs = tasha_spkr_mode1; + size = ARRAY_SIZE(tasha_spkr_mode1); + break; + default: + regs = tasha_spkr_default; + size = ARRAY_SIZE(tasha_spkr_default); + break; + } + + priv->spkr_mode = mode; + for (i = 0; i < size; i++) + snd_soc_update_bits(codec, regs[i].reg, + regs[i].mask, regs[i].val); + return 0; +} +EXPORT_SYMBOL(tasha_set_spkr_mode); + +static void tasha_enable_sido_buck(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + snd_soc_update_bits(codec, WCD9335_ANA_RCO, 0x80, 0x80); + snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x02, 0x02); + /* 100us sleep needed after IREF settings */ + usleep_range(100, 110); + snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x04, 0x04); + /* 100us sleep needed after VREF settings */ + usleep_range(100, 110); + tasha->resmgr->sido_input_src = SIDO_SOURCE_RCO_BG; +} + +static void tasha_cdc_sido_ccl_enable(struct tasha_priv *tasha, bool ccl_flag) +{ + struct snd_soc_codec *codec = tasha->codec; + + if (!codec) + return; + + if (!TASHA_IS_2_0(tasha->wcd9xxx)) { + dev_dbg(codec->dev, "%s: tasha version < 2p0, return\n", + __func__); + return; + } + dev_dbg(codec->dev, "%s: sido_ccl_cnt=%d, ccl_flag:%d\n", + __func__, tasha->sido_ccl_cnt, ccl_flag); + if (ccl_flag) { + if (++tasha->sido_ccl_cnt == 1) + snd_soc_update_bits(codec, + WCD9335_SIDO_SIDO_CCL_10, 0xFF, 0x6E); + } else { + if (tasha->sido_ccl_cnt == 0) { + dev_dbg(codec->dev, "%s: sido_ccl already disabled\n", + __func__); + return; + } + if (--tasha->sido_ccl_cnt == 0) + snd_soc_update_bits(codec, + WCD9335_SIDO_SIDO_CCL_10, 0xFF, 0x02); + } +} + +static bool tasha_cdc_is_svs_enabled(struct tasha_priv *tasha) +{ + if (TASHA_IS_2_0(tasha->wcd9xxx) && + svs_scaling_enabled) + return true; + + return false; +} + +static int tasha_cdc_req_mclk_enable(struct tasha_priv *tasha, + bool enable) +{ + int ret = 0; + + mutex_lock(&tasha->mclk_lock); + if (enable) { + tasha_cdc_sido_ccl_enable(tasha, true); + ret = clk_prepare_enable(tasha->wcd_ext_clk); + if (ret) { + dev_err(tasha->dev, "%s: ext clk enable failed\n", + __func__); + goto unlock_mutex; + } + /* get BG */ + wcd_resmgr_enable_master_bias(tasha->resmgr); + /* get MCLK */ + wcd_resmgr_enable_clk_block(tasha->resmgr, WCD_CLK_MCLK); + } else { + /* put MCLK */ + wcd_resmgr_disable_clk_block(tasha->resmgr, WCD_CLK_MCLK); + /* put BG */ + wcd_resmgr_disable_master_bias(tasha->resmgr); + clk_disable_unprepare(tasha->wcd_ext_clk); + tasha_cdc_sido_ccl_enable(tasha, false); + } +unlock_mutex: + mutex_unlock(&tasha->mclk_lock); + return ret; +} + +static int tasha_cdc_check_sido_value(enum tasha_sido_voltage req_mv) +{ + if ((req_mv != SIDO_VOLTAGE_SVS_MV) && + (req_mv != SIDO_VOLTAGE_NOMINAL_MV)) + return -EINVAL; + + return 0; +} + +static void tasha_codec_apply_sido_voltage( + struct tasha_priv *tasha, + enum tasha_sido_voltage req_mv) +{ + u32 vout_d_val; + struct snd_soc_codec *codec = tasha->codec; + int ret; + + if (!codec) + return; + + if (!tasha_cdc_is_svs_enabled(tasha)) + return; + + if ((sido_buck_svs_voltage != SIDO_VOLTAGE_SVS_MV) && + (sido_buck_svs_voltage != SIDO_VOLTAGE_NOMINAL_MV)) + sido_buck_svs_voltage = SIDO_VOLTAGE_SVS_MV; + + ret = tasha_cdc_check_sido_value(req_mv); + if (ret < 0) { + dev_dbg(codec->dev, "%s: requested mv=%d not in range\n", + __func__, req_mv); + return; + } + if (req_mv == tasha->sido_voltage) { + dev_dbg(codec->dev, "%s: Already at requested mv=%d\n", + __func__, req_mv); + return; + } + if (req_mv == sido_buck_svs_voltage) { + if (test_bit(AUDIO_NOMINAL, &tasha->status_mask) || + test_bit(CPE_NOMINAL, &tasha->status_mask)) { + dev_dbg(codec->dev, + "%s: nominal client running, status_mask=%lu\n", + __func__, tasha->status_mask); + return; + } + } + /* compute the vout_d step value */ + vout_d_val = CALCULATE_VOUT_D(req_mv); + snd_soc_write(codec, WCD9335_ANA_BUCK_VOUT_D, vout_d_val & 0xFF); + snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x80, 0x80); + + /* 1 msec sleep required after SIDO Vout_D voltage change */ + usleep_range(1000, 1100); + tasha->sido_voltage = req_mv; + dev_dbg(codec->dev, + "%s: updated SIDO buck Vout_D to %d, vout_d step = %u\n", + __func__, tasha->sido_voltage, vout_d_val); + + snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, + 0x80, 0x00); +} + +static int tasha_codec_update_sido_voltage( + struct tasha_priv *tasha, + enum tasha_sido_voltage req_mv) +{ + int ret = 0; + + if (!tasha_cdc_is_svs_enabled(tasha)) + return ret; + + mutex_lock(&tasha->sido_lock); + /* enable mclk before setting SIDO voltage */ + ret = tasha_cdc_req_mclk_enable(tasha, true); + if (ret) { + dev_err(tasha->dev, "%s: ext clk enable failed\n", + __func__); + goto err; + } + tasha_codec_apply_sido_voltage(tasha, req_mv); + tasha_cdc_req_mclk_enable(tasha, false); + +err: + mutex_unlock(&tasha->sido_lock); + return ret; +} + +int tasha_enable_efuse_sensing(struct snd_soc_codec *codec) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + tasha_cdc_mclk_enable(codec, true, false); + + if (!TASHA_IS_2_0(priv->wcd9xxx)) + snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, + 0x1E, 0x02); + snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, + 0x01, 0x01); + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + if (!(snd_soc_read(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS) & 0x01)) + WARN(1, "%s: Efuse sense is not complete\n", __func__); + + if (TASHA_IS_2_0(priv->wcd9xxx)) { + if (!(snd_soc_read(codec, + WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0) & 0x40)) + snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, + 0x04, 0x00); + tasha_enable_sido_buck(codec); + } + + tasha_cdc_mclk_enable(codec, false, false); + + return 0; +} +EXPORT_SYMBOL(tasha_enable_efuse_sensing); + +void *tasha_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (config_type) { + case AFE_SLIMBUS_SLAVE_CONFIG: + return &priv->slimbus_slave_cfg; + case AFE_CDC_REGISTERS_CONFIG: + return &tasha_audio_reg_cfg; + case AFE_SLIMBUS_SLAVE_PORT_CONFIG: + return &tasha_slimbus_slave_port_cfg; + case AFE_AANC_VERSION: + return &tasha_cdc_aanc_version; + case AFE_CLIP_BANK_SEL: + return NULL; + case AFE_CDC_CLIP_REGISTERS_CONFIG: + return NULL; + case AFE_CDC_REGISTER_PAGE_CONFIG: + return &tasha_cdc_reg_page_cfg; + default: + dev_err(codec->dev, "%s: Unknown config_type 0x%x\n", + __func__, config_type); + return NULL; + } +} +EXPORT_SYMBOL(tasha_get_afe_config); + +/* + * tasha_event_register: Registers a machine driver callback + * function with codec private data for post ADSP sub-system + * restart (SSR). This callback function will be called from + * codec driver once codec comes out of reset after ADSP SSR. + * + * @machine_event_cb: callback function from machine driver + * @codec: Codec instance + * + * Return: none + */ +void tasha_event_register( + int (*machine_event_cb)(struct snd_soc_codec *codec, + enum wcd9335_codec_event), + struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (tasha) + tasha->machine_codec_event_cb = machine_event_cb; + else + dev_dbg(codec->dev, "%s: Invalid tasha_priv data\n", __func__); +} +EXPORT_SYMBOL(tasha_event_register); + +static int tasha_mbhc_request_irq(struct snd_soc_codec *codec, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + return wcd9xxx_request_irq(core_res, irq, handler, name, data); +} + +static void tasha_mbhc_irq_control(struct snd_soc_codec *codec, + int irq, bool enable) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + if (enable) + wcd9xxx_enable_irq(core_res, irq); + else + wcd9xxx_disable_irq(core_res, irq); +} + +static int tasha_mbhc_free_irq(struct snd_soc_codec *codec, + int irq, void *data) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, irq, data); + return 0; +} + +static void tasha_mbhc_clk_setup(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1, + 0x80, 0x80); + else + snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1, + 0x80, 0x00); +} + +static int tasha_mbhc_btn_to_num(struct snd_soc_codec *codec) +{ + return snd_soc_read(codec, WCD9335_ANA_MBHC_RESULT_3) & 0x7; +} + +static void tasha_mbhc_mbhc_bias_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_ELECT, + 0x01, 0x01); + else + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_ELECT, + 0x01, 0x00); +} + +static void tasha_mbhc_program_btn_thr(struct snd_soc_codec *codec, + s16 *btn_low, s16 *btn_high, + int num_btn, bool is_micbias) +{ + int i; + int vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(codec->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + /* + * Tasha just needs one set of thresholds for button detection + * due to micbias voltage ramp to pullup upon button press. So + * btn_low and is_micbias are ignored and always program button + * thresholds using btn_high. + */ + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN0 + i, + 0xFC, vth << 2); + dev_dbg(codec->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool tasha_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + if (lock) + return wcd9xxx_lock_sleep(core_res); + else { + wcd9xxx_unlock_sleep(core_res); + return 0; + } +} + +static int tasha_mbhc_register_notifier(struct wcd_mbhc *mbhc, + struct notifier_block *nblock, + bool enable) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (enable) + return blocking_notifier_chain_register(&tasha->notifier, + nblock); + else + return blocking_notifier_chain_unregister(&tasha->notifier, + nblock); +} + +static bool tasha_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num) +{ + u8 val; + + if (micb_num == MIC_BIAS_2) { + val = (snd_soc_read(mbhc->codec, WCD9335_ANA_MICB2) >> 6); + if (val == 0x01) + return true; + } + return false; +} + +static bool tasha_mbhc_hph_pa_on_status(struct snd_soc_codec *codec) +{ + return (snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) ? true : false; +} + +static void tasha_mbhc_hph_l_pull_up_control(struct snd_soc_codec *codec, + enum mbhc_hs_pullup_iref pull_up_cur) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (!tasha) + return; + + /* Default pull up current to 2uA */ + if (pull_up_cur < I_OFF || pull_up_cur > I_3P0_UA || + pull_up_cur == I_DEFAULT) + pull_up_cur = I_2P0_UA; + + dev_dbg(codec->dev, "%s: HS pull up current:%d\n", + __func__, pull_up_cur); + + if (TASHA_IS_2_0(tasha->wcd9xxx)) + snd_soc_update_bits(codec, WCD9335_MBHC_PLUG_DETECT_CTL, + 0xC0, pull_up_cur << 6); + else + snd_soc_update_bits(codec, WCD9335_MBHC_PLUG_DETECT_CTL, + 0xC0, 0x40); +} + +static int tasha_enable_ext_mb_source(struct wcd_mbhc *mbhc, + bool turn_on) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + struct on_demand_supply *supply; + + if (!tasha) + return -EINVAL; + + supply = &tasha->on_demand_list[ON_DEMAND_MICBIAS]; + if (!supply->supply) { + dev_dbg(codec->dev, "%s: warning supply not present ond for %s\n", + __func__, "onDemand Micbias"); + return ret; + } + + dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on, + supply->ondemand_supply_count); + + if (turn_on) { + if (!(supply->ondemand_supply_count)) { + ret = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + } + supply->ondemand_supply_count++; + } else { + if (supply->ondemand_supply_count > 0) + supply->ondemand_supply_count--; + if (!(supply->ondemand_supply_count)) { + ret = snd_soc_dapm_disable_pin( + snd_soc_codec_get_dapm(codec), + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + } + } + + if (ret) + dev_err(codec->dev, "%s: Failed to %s external micbias source\n", + __func__, turn_on ? "enable" : "disabled"); + else + dev_dbg(codec->dev, "%s: %s external micbias source\n", + __func__, turn_on ? "Enabled" : "Disabled"); + + return ret; +} + +static int tasha_micbias_control(struct snd_soc_codec *codec, + int micb_num, + int req, bool is_dapm) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int micb_index = micb_num - 1; + u16 micb_reg; + int pre_off_event = 0, post_off_event = 0; + int post_on_event = 0, post_dapm_off = 0; + int post_dapm_on = 0; + + if ((micb_index < 0) || (micb_index > TASHA_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD9335_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD9335_ANA_MICB2; + pre_off_event = WCD_EVENT_PRE_MICBIAS_2_OFF; + post_off_event = WCD_EVENT_POST_MICBIAS_2_OFF; + post_on_event = WCD_EVENT_POST_MICBIAS_2_ON; + post_dapm_on = WCD_EVENT_POST_DAPM_MICBIAS_2_ON; + post_dapm_off = WCD_EVENT_POST_DAPM_MICBIAS_2_OFF; + break; + case MIC_BIAS_3: + micb_reg = WCD9335_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD9335_ANA_MICB4; + break; + default: + dev_err(codec->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + mutex_lock(&tasha->micb_lock); + + switch (req) { + case MICB_PULLUP_ENABLE: + tasha->pullup_ref[micb_index]++; + if ((tasha->pullup_ref[micb_index] == 1) && + (tasha->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + break; + case MICB_PULLUP_DISABLE: + if (tasha->pullup_ref[micb_index] > 0) + tasha->pullup_ref[micb_index]--; + if ((tasha->pullup_ref[micb_index] == 0) && + (tasha->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + break; + case MICB_ENABLE: + tasha->micb_ref[micb_index]++; + if (tasha->micb_ref[micb_index] == 1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + if (post_on_event) + blocking_notifier_call_chain(&tasha->notifier, + post_on_event, &tasha->mbhc); + } + if (is_dapm && post_dapm_on) + blocking_notifier_call_chain(&tasha->notifier, + post_dapm_on, &tasha->mbhc); + break; + case MICB_DISABLE: + if (tasha->micb_ref[micb_index] > 0) + tasha->micb_ref[micb_index]--; + if ((tasha->micb_ref[micb_index] == 0) && + (tasha->pullup_ref[micb_index] > 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + else if ((tasha->micb_ref[micb_index] == 0) && + (tasha->pullup_ref[micb_index] == 0)) { + if (pre_off_event) + blocking_notifier_call_chain(&tasha->notifier, + pre_off_event, &tasha->mbhc); + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + if (post_off_event) + blocking_notifier_call_chain(&tasha->notifier, + post_off_event, &tasha->mbhc); + } + if (is_dapm && post_dapm_off) + blocking_notifier_call_chain(&tasha->notifier, + post_dapm_off, &tasha->mbhc); + break; + }; + + dev_dbg(codec->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n", + __func__, micb_num, tasha->micb_ref[micb_index], + tasha->pullup_ref[micb_index]); + + mutex_unlock(&tasha->micb_lock); + + return 0; +} + +static int tasha_mbhc_request_micbias(struct snd_soc_codec *codec, + int micb_num, int req) +{ + int ret; + + /* + * If micbias is requested, make sure that there + * is vote to enable mclk + */ + if (req == MICB_ENABLE) + tasha_cdc_mclk_enable(codec, true, false); + + ret = tasha_micbias_control(codec, micb_num, req, false); + + /* + * Release vote for mclk while requesting for + * micbias disable + */ + if (req == MICB_DISABLE) + tasha_cdc_mclk_enable(codec, false, false); + + return ret; +} + +static void tasha_mbhc_micb_ramp_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP, + 0x1C, 0x0C); + snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP, + 0x1C, 0x00); + } +} + +static struct firmware_cal *tasha_get_hwdep_fw_cal(struct wcd_mbhc *mbhc, + enum wcd_cal_type type) +{ + struct tasha_priv *tasha; + struct firmware_cal *hwdep_cal; + struct snd_soc_codec *codec = mbhc->codec; + + if (!codec) { + pr_err("%s: NULL codec pointer\n", __func__); + return NULL; + } + tasha = snd_soc_codec_get_drvdata(codec); + hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, type); + if (!hwdep_cal) + dev_err(codec->dev, "%s: cal not sent by %d\n", + __func__, type); + + return hwdep_cal; +} + +static int tasha_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, + int micb_num) +{ + int cur_vout_ctl, req_vout_ctl; + int micb_reg, micb_val, micb_en; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD9335_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD9335_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD9335_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD9335_ANA_MICB4; + break; + default: + return -EINVAL; + } + + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_val = snd_soc_read(codec, micb_reg); + micb_en = (micb_val & 0xC0) >> 6; + cur_vout_ctl = micb_val & 0x3F; + + req_vout_ctl = wcd9335_get_micb_vout_ctl_val(req_volt); + if (IS_ERR_VALUE(req_vout_ctl)) + return -EINVAL; + if (cur_vout_ctl == req_vout_ctl) + return 0; + + dev_dbg(codec->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), + req_volt, micb_en); + + if (micb_en == 0x1) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + + snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl); + + if (micb_en == 0x1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } + + return 0; +} + +static int tasha_mbhc_micb_ctrl_threshold_mic(struct snd_soc_codec *codec, + int micb_num, bool req_en) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + int rc, micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (pdata->micbias.micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : pdata->micbias.micb2_mv; + + mutex_lock(&tasha->micb_lock); + rc = tasha_mbhc_micb_adjust_voltage(codec, micb_mv, MIC_BIAS_2); + mutex_unlock(&tasha->micb_lock); + + return rc; +} + +static inline void tasha_mbhc_get_result_params(struct wcd9xxx *wcd9xxx, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + int i; + int val, val1; + s16 c1; + s32 x1, d1; + int32_t denom; + int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(wcd9xxx->regmap, WCD9335_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < TASHA_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(wcd9xxx->regmap, WCD9335_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(wcd9xxx->regmap, WCD9335_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(wcd9xxx->regmap, WCD9335_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = TASHA_MBHC_GET_X1(val); + c1 = TASHA_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if ((c1 < 2) && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + dev_dbg(wcd9xxx->dev, + "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (TASHA_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = TASHA_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(wcd9xxx->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + regmap_bulk_read(wcd9xxx->regmap, + WCD9335_ANA_MBHC_RESULT_1, (u8 *)&val, 2); + x1 = TASHA_MBHC_GET_X1(val); + i++; + if (i == TASHA_ZDET_NUM_MEASUREMENTS) + break; + } +} + +/* + * tasha_mbhc_zdet_gpio_ctrl: Register callback function for + * controlling the switch on hifi amps. Default switch state + * will put a 51ohm load in parallel to the hph load. So, + * impedance detection function will pull the gpio high + * to make the switch open. + * + * @zdet_gpio_cb: callback function from machine driver + * @codec: Codec instance + * + * Return: none + */ +void tasha_mbhc_zdet_gpio_ctrl( + int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high), + struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha->zdet_gpio_cb = zdet_gpio_cb; +} +EXPORT_SYMBOL(tasha_mbhc_zdet_gpio_ctrl); + +static void tasha_mbhc_zdet_ramp(struct snd_soc_codec *codec, + struct tasha_mbhc_zdet_param *zdet_param, + int32_t *zl, int32_t *zr, s16 *d1_a) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int32_t zdet = 0; + + snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_ANA_CTL, 0x70, + zdet_param->ldo_ctl << 4); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN5, 0xFC, + zdet_param->btn5); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN6, 0xFC, + zdet_param->btn6); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN7, 0xFC, + zdet_param->btn7); + snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_ANA_CTL, 0x0F, + zdet_param->noff); + snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x0F, + zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ZDET, 0x80, 0x80); + dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + tasha_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ZDET, 0x40, 0x40); + dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + tasha_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static inline void tasha_wcd_mbhc_qfuse_cal(struct snd_soc_codec *codec, + int32_t *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (TASHA_ZDET_VAL_400/1000)) + q1 = snd_soc_read(codec, + WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r)); + else + q1 = snd_soc_read(codec, + WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void tasha_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + s16 reg0, reg1, reg2, reg3, reg4; + int32_t z1L, z1R, z1Ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + bool is_change = false; + struct tasha_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct tasha_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + if (!TASHA_IS_2_0(wcd9xxx)) { + dev_dbg(codec->dev, "%s: Z-det is not supported for this codec version\n", + __func__); + *zl = 0; + *zr = 0; + return; + } + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (tasha->zdet_gpio_cb) + is_change = tasha->zdet_gpio_cb(codec, true); + + reg0 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN5); + reg1 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN6); + reg2 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN7); + reg3 = snd_soc_read(codec, WCD9335_MBHC_CTL_1); + reg4 = snd_soc_read(codec, WCD9335_MBHC_ZDET_ANA_CTL); + + if (snd_soc_read(codec, WCD9335_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_MECH, 0x80, 0x00); + + /* Enable AZ */ + snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1, 0x0C, 0x04); + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_MECH, 0x01, 0x00); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + + if (!TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) + goto left_ch_impedance; + + /* second ramp for left ch */ + if (z1L < TASHA_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1L > TASHA_ZDET_VAL_400) && (z1L <= TASHA_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1L > TASHA_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + +left_ch_impedance: + if ((z1L == TASHA_ZDET_FLOATING_IMPEDANCE) || + (z1L > TASHA_ZDET_VAL_100K)) { + *zl = TASHA_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1L/1000; + tasha_wcd_mbhc_qfuse_cal(codec, zl, 0); + } + dev_dbg(codec->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* start of right impedance ramp and calculation */ + tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + if (TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { + if (((z1R > TASHA_ZDET_VAL_1200) && + (zdet_param_ptr->noff == 0x6)) || + ((*zl) != TASHA_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* second ramp for right ch */ + if (z1R < TASHA_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1R > TASHA_ZDET_VAL_400) && + (z1R <= TASHA_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1R > TASHA_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + } +right_ch_impedance: + if ((z1R == TASHA_ZDET_FLOATING_IMPEDANCE) || + (z1R > TASHA_ZDET_VAL_100K)) { + *zr = TASHA_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1R/1000; + tasha_wcd_mbhc_qfuse_cal(codec, zr, 1); + } + dev_dbg(codec->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* mono/stereo detection */ + if ((*zl == TASHA_ZDET_FLOATING_IMPEDANCE) && + (*zr == TASHA_ZDET_FLOATING_IMPEDANCE)) { + dev_dbg(codec->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == TASHA_ZDET_FLOATING_IMPEDANCE) || + (*zr == TASHA_ZDET_FLOATING_IMPEDANCE) || + ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) || + ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) { + dev_dbg(codec->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + goto zdet_complete; + } + snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, 0x02, 0x02); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x40, 0x01); + if (*zl < (TASHA_ZDET_VAL_32/1000)) + tasha_mbhc_zdet_ramp(codec, &zdet_param[0], &z1Ls, NULL, d1); + else + tasha_mbhc_zdet_ramp(codec, &zdet_param[1], &z1Ls, NULL, d1); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x40, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, 0x02, 0x00); + z1Ls /= 1000; + tasha_wcd_mbhc_qfuse_cal(codec, &z1Ls, 0); + /* parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls); + z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl)); + if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) { + dev_dbg(codec->dev, "%s: stereo plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } else { + dev_dbg(codec->dev, "%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } + +zdet_complete: + snd_soc_write(codec, WCD9335_ANA_MBHC_BTN5, reg0); + snd_soc_write(codec, WCD9335_ANA_MBHC_BTN6, reg1); + snd_soc_write(codec, WCD9335_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_write(codec, WCD9335_MBHC_ZDET_ANA_CTL, reg4); + snd_soc_write(codec, WCD9335_MBHC_CTL_1, reg3); + if (is_fsm_disable) + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ELECT, 0x80, 0x80); + if (tasha->zdet_gpio_cb && is_change) + tasha->zdet_gpio_cb(codec, false); +} + +static void tasha_mbhc_gnd_det_ctrl(struct snd_soc_codec *codec, bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH, + 0x40, 0x40); + } else { + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH, + 0x02, 0x00); + } +} + +static void tasha_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (enable) { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, + 0x40, 0x40); + if (TASHA_IS_2_0(tasha->wcd9xxx)) + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, + 0x10, 0x10); + } else { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, + 0x40, 0x00); + if (TASHA_IS_2_0(tasha->wcd9xxx)) + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, + 0x10, 0x00); + } +} + +static void tasha_mbhc_moisture_config(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + if (TASHA_MBHC_MOISTURE_VREF == V_OFF) + return; + + /* Donot enable moisture detection if jack type is NC */ + if (!mbhc->hphl_swh) { + dev_dbg(codec->dev, "%s: disable moisture detection for NC\n", + __func__); + return; + } + + snd_soc_update_bits(codec, WCD9335_MBHC_CTL_2, + 0x0C, TASHA_MBHC_MOISTURE_VREF << 2); + tasha_mbhc_hph_l_pull_up_control(codec, TASHA_MBHC_MOISTURE_IREF); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .request_irq = tasha_mbhc_request_irq, + .irq_control = tasha_mbhc_irq_control, + .free_irq = tasha_mbhc_free_irq, + .clk_setup = tasha_mbhc_clk_setup, + .map_btn_code_to_num = tasha_mbhc_btn_to_num, + .enable_mb_source = tasha_enable_ext_mb_source, + .mbhc_bias = tasha_mbhc_mbhc_bias_control, + .set_btn_thr = tasha_mbhc_program_btn_thr, + .lock_sleep = tasha_mbhc_lock_sleep, + .register_notifier = tasha_mbhc_register_notifier, + .micbias_enable_status = tasha_mbhc_micb_en_status, + .hph_pa_on_status = tasha_mbhc_hph_pa_on_status, + .hph_pull_up_control = tasha_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = tasha_mbhc_request_micbias, + .mbhc_micb_ramp_control = tasha_mbhc_micb_ramp_control, + .get_hwdep_fw_cal = tasha_get_hwdep_fw_cal, + .mbhc_micb_ctrl_thr_mic = tasha_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = tasha_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = tasha_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = tasha_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = tasha_mbhc_moisture_config, +}; + +static int tasha_get_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha->anc_slot; + return 0; +} + +static int tasha_put_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha->anc_slot = ucontrol->value.integer.value[0]; + return 0; +} + +static int tasha_get_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = (tasha->anc_func == true ? 1 : 0); + return 0; +} + +static int tasha_put_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + mutex_lock(&tasha->codec_mutex); + tasha->anc_func = (!ucontrol->value.integer.value[0] ? false : true); + + dev_dbg(codec->dev, "%s: anc_func %x", __func__, tasha->anc_func); + + if (tasha->anc_func == true) { + snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT2 PA"); + snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT2"); + snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT1 PA"); + snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT1"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_disable_pin(dapm, "LINEOUT2"); + snd_soc_dapm_disable_pin(dapm, "LINEOUT2 PA"); + snd_soc_dapm_disable_pin(dapm, "LINEOUT1"); + snd_soc_dapm_disable_pin(dapm, "LINEOUT1 PA"); + snd_soc_dapm_disable_pin(dapm, "HPHR"); + snd_soc_dapm_disable_pin(dapm, "HPHL"); + snd_soc_dapm_disable_pin(dapm, "HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "EAR PA"); + snd_soc_dapm_disable_pin(dapm, "EAR"); + } else { + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_enable_pin(dapm, "LINEOUT2"); + snd_soc_dapm_enable_pin(dapm, "LINEOUT2 PA"); + snd_soc_dapm_enable_pin(dapm, "LINEOUT1"); + snd_soc_dapm_enable_pin(dapm, "LINEOUT1 PA"); + snd_soc_dapm_enable_pin(dapm, "HPHR"); + snd_soc_dapm_enable_pin(dapm, "HPHL"); + snd_soc_dapm_enable_pin(dapm, "HPHR PA"); + snd_soc_dapm_enable_pin(dapm, "HPHL PA"); + snd_soc_dapm_enable_pin(dapm, "EAR PA"); + snd_soc_dapm_enable_pin(dapm, "EAR"); + } + mutex_unlock(&tasha->codec_mutex); + snd_soc_dapm_sync(dapm); + return 0; +} + +static int tasha_get_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = tasha->clk_mode; + dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode); + + return 0; +} + +static int tasha_put_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha->clk_mode = ucontrol->value.enumerated.item[0]; + dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode); + + return 0; +} + +static int tasha_get_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + /* IIR filter band registers are at integer multiples of 16 */ + u16 iir_reg = WCD9335_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int tasha_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl, zr; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(&priv->mbhc, &zl, &zr); + dev_dbg(codec->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + tasha_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + tasha_hph_impedance_get, NULL), +}; + +static int tasha_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + struct wcd_mbhc *mbhc; + + if (!priv) { + dev_dbg(codec->dev, "%s: wcd9335 private data is NULL\n", + __func__); + return 0; + } + + mbhc = &priv->mbhc; + if (!mbhc) { + dev_dbg(codec->dev, "%s: mbhc not initialized\n", __func__); + return 0; + } + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type); + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + tasha_get_hph_type, NULL), +}; + +static int tasha_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha_p->vi_feed_value; + + return 0; +} + +static int tasha_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = tasha_p->wcd9xxx; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n", + __func__, enable, port_id, dai_id); + + tasha_p->vi_feed_value = ucontrol->value.integer.value[0]; + + mutex_lock(&tasha_p->codec_mutex); + if (enable) { + if (port_id == TASHA_TX14 && !test_bit(VI_SENSE_1, + &tasha_p->status_mask)) { + list_add_tail(&core->tx_chs[TASHA_TX14].list, + &tasha_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_1, &tasha_p->status_mask); + } + if (port_id == TASHA_TX15 && !test_bit(VI_SENSE_2, + &tasha_p->status_mask)) { + list_add_tail(&core->tx_chs[TASHA_TX15].list, + &tasha_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_2, &tasha_p->status_mask); + } + } else { + if (port_id == TASHA_TX14 && test_bit(VI_SENSE_1, + &tasha_p->status_mask)) { + list_del_init(&core->tx_chs[TASHA_TX14].list); + clear_bit(VI_SENSE_1, &tasha_p->status_mask); + } + if (port_id == TASHA_TX15 && test_bit(VI_SENSE_2, + &tasha_p->status_mask)) { + list_del_init(&core->tx_chs[TASHA_TX15].list); + clear_bit(VI_SENSE_2, &tasha_p->status_mask); + } + } + mutex_unlock(&tasha_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +/* virtual port entries */ +static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha_p->tx_port_value; + return 0; +} + +static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + u32 vtable; + + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, + widget->name, ucontrol->id.name, tasha_p->tx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tasha_p->codec_mutex); + + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + if (dai_id != AIF1_CAP) { + dev_err(codec->dev, "%s: invalid AIF for I2C mode\n", + __func__); + mutex_unlock(&tasha_p->codec_mutex); + return -EINVAL; + } + vtable = vport_slim_check_table[dai_id]; + } else { + if (dai_id >= ARRAY_SIZE(vport_i2s_check_table)) { + dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n", + __func__, dai_id); + return -EINVAL; + } + vtable = vport_i2s_check_table[dai_id]; + } + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set */ + if (enable && !(tasha_p->tx_port_value & 1 << port_id)) { + + if (wcd9xxx_tx_vport_validation(vtable, port_id, + tasha_p->dai, NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n", + __func__, port_id); + mutex_unlock(&tasha_p->codec_mutex); + return 0; + } + tasha_p->tx_port_value |= 1 << port_id; + list_add_tail(&core->tx_chs[port_id].list, + &tasha_p->dai[dai_id].wcd9xxx_ch_list + ); + } else if (!enable && (tasha_p->tx_port_value & + 1 << port_id)) { + tasha_p->tx_port_value &= ~(1 << port_id); + list_del_init(&core->tx_chs[port_id].list); + } else { + if (enable) + dev_dbg(codec->dev, "%s: TX%u port is used by\n" + "this virtual port\n", + __func__, port_id); + else + dev_dbg(codec->dev, "%s: TX%u port is not used by\n" + "this virtual port\n", + __func__, port_id); + /* avoid update power function */ + mutex_unlock(&tasha_p->codec_mutex); + return 0; + } + break; + case AIF4_MAD_TX: + case AIF5_CPE_TX: + break; + default: + pr_err("Unknown AIF %d\n", dai_id); + mutex_unlock(&tasha_p->codec_mutex); + return -EINVAL; + } + pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__, + widget->name, widget->sname, tasha_p->tx_port_value, + widget->shift); + + mutex_unlock(&tasha_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int slim_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = + tasha_p->rx_port_value[widget->shift]; + return 0; +} + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB", "AIF_MIX1_PB" +}; + +static int slim_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + unsigned int rx_port_value; + u32 port_id = widget->shift; + + tasha_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + rx_port_value = tasha_p->rx_port_value[port_id]; + + pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__, + widget->name, ucontrol->id.name, rx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tasha_p->codec_mutex); + + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + if (rx_port_value > 2) { + dev_err(codec->dev, "%s: invalid AIF for I2C mode\n", + __func__); + goto err; + } + } + /* value need to match the Virtual port and AIF number */ + switch (rx_port_value) { + case 0: + list_del_init(&core->rx_chs[port_id].list); + break; + case 1: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF1_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF1_PB].wcd9xxx_ch_list); + break; + case 2: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF2_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF2_PB].wcd9xxx_ch_list); + break; + case 3: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF3_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF3_PB].wcd9xxx_ch_list); + break; + case 4: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF4_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF4_PB].wcd9xxx_ch_list); + break; + case 5: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF_MIX1_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF_MIX1_PB].wcd9xxx_ch_list); + break; + default: + pr_err("Unknown AIF %d\n", rx_port_value); + goto err; + } +rtn: + mutex_unlock(&tasha_p->codec_mutex); + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + + return 0; +err: + mutex_unlock(&tasha_p->codec_mutex); + return -EINVAL; +} + +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 snd_kcontrol_new slim_rx_mux[TASHA_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 aif4_vi_mixer[] = { + SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, TASHA_TX14, 1, 0, + tasha_vi_feed_mixer_get, tasha_vi_feed_mixer_put), + SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, TASHA_TX15, 1, 0, + tasha_vi_feed_mixer_get, tasha_vi_feed_mixer_put), +}; + +static const struct snd_kcontrol_new aif1_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif4_mad_mixer[] = { + SOC_SINGLE_EXT("SLIM TX12", SND_SOC_NOPM, TASHA_TX12, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, 0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + +}; + +static const struct snd_kcontrol_new rx_int1_spline_mix_switch[] = { + SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int2_spline_mix_switch[] = { + SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int3_spline_mix_switch[] = { + SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int4_spline_mix_switch[] = { + SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int5_spline_mix_switch[] = { + SOC_DAPM_SINGLE("LO3 Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int6_spline_mix_switch[] = { + SOC_DAPM_SINGLE("LO4 Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int7_spline_mix_switch[] = { + SOC_DAPM_SINGLE("SPKRL Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int8_spline_mix_switch[] = { + SOC_DAPM_SINGLE("SPKRR Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int5_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("LO3 VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int6_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("LO4 VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int7_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("SPKRL VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int8_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("SPKRR VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new cpe_in_mix_switch[] = { + SOC_DAPM_SINGLE("MAD_BYPASS", SND_SOC_NOPM, 0, 1, 0) +}; + + + +static int tasha_put_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + bool iir_band_en_status; + int value = ucontrol->value.integer.value[0]; + u16 iir_reg = WCD9335_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, iir_reg, (1 << band_idx), + (value << band_idx)); + + iir_band_en_status = ((snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0); + pr_debug("%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, iir_band_en_status); + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx)); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 8); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 16); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) & 0x3F) << 24); + + return value; +} + +static int tasha_get_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 24) & 0x3F); +} + +static void tasha_codec_enable_int_port(struct wcd9xxx_codec_dai_data *dai, + struct snd_soc_codec *codec) +{ + struct wcd9xxx_ch *ch; + int port_num = 0; + unsigned short reg = 0; + u8 val = 0; + struct tasha_priv *tasha_p; + + if (!dai || !codec) { + pr_err("%s: Invalid params\n", __func__); + return; + } + + tasha_p = snd_soc_codec_get_drvdata(codec); + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + if (ch->port >= TASHA_RX_PORT_START_NUMBER) { + port_num = ch->port - TASHA_RX_PORT_START_NUMBER; + reg = TASHA_SLIM_PGD_PORT_INT_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(tasha_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write( + tasha_p->wcd9xxx, reg, val); + val = wcd9xxx_interface_reg_read( + tasha_p->wcd9xxx, reg); + } + } else { + port_num = ch->port; + reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(tasha_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write(tasha_p->wcd9xxx, + reg, val); + val = wcd9xxx_interface_reg_read( + tasha_p->wcd9xxx, reg); + } + } + } +} + +static int tasha_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai, + bool up) +{ + int ret = 0; + struct wcd9xxx_ch *ch; + + if (up) { + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + ret = wcd9xxx_get_slave_port(ch->ch_num); + if (ret < 0) { + pr_err("%s: Invalid slave port ID: %d\n", + __func__, ret); + ret = -EINVAL; + } else { + set_bit(ret, &dai->ch_mask); + } + } + } else { + ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0), + msecs_to_jiffies( + TASHA_SLIM_CLOSE_TIMEOUT)); + if (!ret) { + pr_err("%s: Slim close tx/rx wait timeout, ch_mask:0x%lx\n", + __func__, dai->ch_mask); + ret = -ETIMEDOUT; + } else { + ret = 0; + } + } + return ret; +} + +static int tasha_codec_enable_slimrx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + int ret = 0; + struct wcd9xxx_codec_dai_data *dai; + + core = dev_get_drvdata(codec->dev->parent); + + dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d\n" + "stream name %s event %d\n", + __func__, codec->component.name, + codec->component.num_dai, w->sname, event); + + /* Execute the callback only if interface type is slimbus */ + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) + return 0; + + dai = &tasha_p->dai[w->shift]; + dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n", + __func__, w->name, w->shift, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tasha_codec_enable_int_port(dai, codec); + (void) tasha_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_PRE_PMD: + if (!test_bit(SB_CLK_GEAR, &tasha_p->status_mask)) { + tasha_codec_vote_max_bw(codec, true); + set_bit(SB_CLK_GEAR, &tasha_p->status_mask); + } + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n", + __func__, ret); + + if (!dai->bus_down_in_recovery) + ret = tasha_codec_enable_slim_chmask(dai, false); + else + dev_dbg(codec->dev, + "%s: bus in recovery skip enable slim_chmask", + __func__); + ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->grph); + break; + } + return ret; +} + +static int tasha_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core = NULL; + struct snd_soc_codec *codec = NULL; + struct tasha_priv *tasha_p = NULL; + int ret = 0; + struct wcd9xxx_codec_dai_data *dai = NULL; + + if (!w) { + pr_err("%s invalid params\n", __func__); + return -EINVAL; + } + codec = snd_soc_dapm_to_codec(w->dapm); + tasha_p = snd_soc_codec_get_drvdata(codec); + core = tasha_p->wcd9xxx; + + dev_dbg(codec->dev, "%s: num_dai %d stream name %s\n", + __func__, codec->component.num_dai, w->sname); + + /* Execute the callback only if interface type is slimbus */ + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + dev_err(codec->dev, "%s Interface is not correct", __func__); + return 0; + } + + dev_dbg(codec->dev, "%s(): w->name %s event %d w->shift %d\n", + __func__, w->name, event, w->shift); + if (w->shift != AIF4_VIFEED) { + pr_err("%s Error in enabling the tx path\n", __func__); + ret = -EINVAL; + goto out_vi; + } + dai = &tasha_p->dai[w->shift]; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (test_bit(VI_SENSE_1, &tasha_p->status_mask)) { + dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + if (test_bit(VI_SENSE_2, &tasha_p->status_mask)) { + pr_debug("%s: spkr2 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + dai->bus_down_in_recovery = false; + tasha_codec_enable_int_port(dai, codec); + (void) tasha_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (ret) + dev_err(codec->dev, "%s error in close_slim_sch_tx %d\n", + __func__, ret); + if (!dai->bus_down_in_recovery) + ret = tasha_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect TX port, ret = %d\n", + __func__, ret); + } + if (test_bit(VI_SENSE_1, &tasha_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + if (test_bit(VI_SENSE_2, &tasha_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + break; + } +out_vi: + return ret; +} + +/* + * __tasha_codec_enable_slimtx: Enable the slimbus slave port + * for TX path + * @codec: Handle to the codec for which the slave port is to be + * enabled. + * @dai_data: The dai specific data for dai which is enabled. + */ +static int __tasha_codec_enable_slimtx(struct snd_soc_codec *codec, + int event, struct wcd9xxx_codec_dai_data *dai) +{ + struct wcd9xxx *core; + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + /* Execute the callback only if interface type is slimbus */ + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) + return 0; + + dev_dbg(codec->dev, + "%s: event = %d\n", __func__, event); + core = dev_get_drvdata(codec->dev->parent); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tasha_codec_enable_int_port(dai, codec); + (void) tasha_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (!dai->bus_down_in_recovery) + ret = tasha_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + pr_debug("%s: Disconnect TX port, ret = %d\n", + __func__, ret); + } + + break; + } + + return ret; +} + +static int tasha_codec_enable_slimtx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + + dev_dbg(codec->dev, + "%s: w->name %s, w->shift = %d, num_dai %d stream name %s\n", + __func__, w->name, w->shift, + codec->component.num_dai, w->sname); + + dai = &tasha_p->dai[w->shift]; + return __tasha_codec_enable_slimtx(codec, event, dai); +} + +static void tasha_codec_cpe_pp_set_cfg(struct snd_soc_codec *codec, int event) +{ + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + u8 bit_width, rate, buf_period; + + dai = &tasha_p->dai[AIF4_MAD_TX]; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + switch (dai->bit_width) { + case 32: + bit_width = 0xF; + break; + case 24: + bit_width = 0xE; + break; + case 20: + bit_width = 0xD; + break; + case 16: + default: + bit_width = 0x0; + break; + } + snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x0F, + bit_width); + + switch (dai->rate) { + case 384000: + rate = 0x30; + break; + case 192000: + rate = 0x20; + break; + case 48000: + rate = 0x10; + break; + case 16000: + default: + rate = 0x00; + break; + } + snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x70, + rate); + + buf_period = (dai->rate * (dai->bit_width/8)) / (16*1000); + snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD, + 0xFF, buf_period); + dev_dbg(codec->dev, "%s: PP buffer period= 0x%x\n", + __func__, buf_period); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_write(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x3C); + snd_soc_write(codec, WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD, 0x60); + break; + + default: + break; + } +} + +/* + * tasha_codec_get_mad_port_id: Callback function that will be invoked + * to get the port ID for MAD. + * @codec: Handle to the codec + * @port_id: cpe port_id needs to enable + */ +static int tasha_codec_get_mad_port_id(struct snd_soc_codec *codec, + u16 *port_id) +{ + struct tasha_priv *tasha_p; + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx_ch *ch; + + if (!port_id || !codec) + return -EINVAL; + + tasha_p = snd_soc_codec_get_drvdata(codec); + if (!tasha_p) + return -EINVAL; + + dai = &tasha_p->dai[AIF4_MAD_TX]; + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + if (ch->port == TASHA_TX12) + *port_id = WCD_CPE_AFE_OUT_PORT_2; + else if (ch->port == TASHA_TX13) + *port_id = WCD_CPE_AFE_OUT_PORT_4; + else { + dev_err(codec->dev, "%s: invalid mad_port = %d\n", + __func__, ch->port); + return -EINVAL; + } + } + dev_dbg(codec->dev, "%s: port_id = %d\n", __func__, *port_id); + + return 0; +} + +/* + * tasha_codec_enable_slimtx_mad: Callback function that will be invoked + * to setup the slave port for MAD. + * @codec: Handle to the codec + * @event: Indicates whether to enable or disable the slave port + */ +static int tasha_codec_enable_slimtx_mad(struct snd_soc_codec *codec, + u8 event) +{ + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx_ch *ch; + int dapm_event = SND_SOC_DAPM_POST_PMU; + u16 port = 0; + int ret = 0; + + dai = &tasha_p->dai[AIF4_MAD_TX]; + + if (event == 0) + dapm_event = SND_SOC_DAPM_POST_PMD; + + dev_dbg(codec->dev, + "%s: mad_channel, event = 0x%x\n", + __func__, event); + + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + dev_dbg(codec->dev, "%s: mad_port = %d, event = 0x%x\n", + __func__, ch->port, event); + if (ch->port == TASHA_TX13) { + tasha_codec_cpe_pp_set_cfg(codec, dapm_event); + port = TASHA_TX13; + break; + } + } + + ret = __tasha_codec_enable_slimtx(codec, dapm_event, dai); + + if (port == TASHA_TX13) { + switch (dapm_event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, + 0x20, 0x00); + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, + 0x03, 0x02); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, + 0x80, 0x80); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, + 0x20, 0x20); + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, + 0x03, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, + 0x80, 0x00); + break; + } + } + + return ret; +} + +static int tasha_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + /* + * Mask top bit it is reserved + * Updates addr automatically for each B2 write + */ + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[0]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[1]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[2]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[3]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[4]); + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static int tasha_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha->comp_enabled[comp]; + return 0; +} + +static int tasha_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + pr_debug("%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, tasha->comp_enabled[comp], value); + tasha->comp_enabled[comp] = value; + + /* Any specific register configuration for compander */ + switch (comp) { + case COMPANDER_1: + /* Set Gain Source Select based on compander enable/disable */ + snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_2: + snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_3: + break; + case COMPANDER_4: + break; + case COMPANDER_5: + snd_soc_update_bits(codec, WCD9335_SE_LO_LO3_GAIN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_6: + snd_soc_update_bits(codec, WCD9335_SE_LO_LO4_GAIN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_7: + break; + case COMPANDER_8: + break; + default: + /* + * if compander is not enabled for any interpolator, + * it does not cause any audio failure, so do not + * return error in this case, but just print a log + */ + dev_warn(codec->dev, "%s: unknown compander: %d\n", + __func__, comp); + }; + return 0; +} + +static void tasha_codec_init_flyback(struct snd_soc_codec *codec) +{ + snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0xC0, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0xC0, 0x00); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_FLYB_BUFF, 0x0F, 0x00); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_FLYB_BUFF, 0xF0, 0x00); +} + +static int tasha_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tasha->rx_bias_count++; + if (tasha->rx_bias_count == 1) { + if (TASHA_IS_2_0(tasha->wcd9xxx)) + tasha_codec_init_flyback(codec); + snd_soc_update_bits(codec, WCD9335_ANA_RX_SUPPLIES, + 0x01, 0x01); + } + break; + case SND_SOC_DAPM_POST_PMD: + tasha->rx_bias_count--; + if (!tasha->rx_bias_count) + snd_soc_update_bits(codec, WCD9335_ANA_RX_SUPPLIES, + 0x01, 0x00); + break; + }; + dev_dbg(codec->dev, "%s: Current RX BIAS user count: %d\n", __func__, + tasha->rx_bias_count); + + return 0; +} + +static void tasha_realign_anc_coeff(struct snd_soc_codec *codec, + u16 reg1, u16 reg2) +{ + u8 val1, val2, tmpval1, tmpval2; + + snd_soc_write(codec, reg1, 0x00); + tmpval1 = snd_soc_read(codec, reg2); + tmpval2 = snd_soc_read(codec, reg2); + snd_soc_write(codec, reg1, 0x00); + snd_soc_write(codec, reg2, 0xFF); + snd_soc_write(codec, reg1, 0x01); + snd_soc_write(codec, reg2, 0xFF); + + snd_soc_write(codec, reg1, 0x00); + val1 = snd_soc_read(codec, reg2); + val2 = snd_soc_read(codec, reg2); + + if (val1 == 0x0F && val2 == 0xFF) { + dev_dbg(codec->dev, "%s: ANC0 co-eff index re-aligned\n", + __func__); + snd_soc_read(codec, reg2); + snd_soc_write(codec, reg1, 0x00); + snd_soc_write(codec, reg2, tmpval2); + snd_soc_write(codec, reg1, 0x01); + snd_soc_write(codec, reg2, tmpval1); + } else if (val1 == 0xFF && val2 == 0x0F) { + dev_dbg(codec->dev, "%s: ANC1 co-eff index already aligned\n", + __func__); + snd_soc_write(codec, reg1, 0x00); + snd_soc_write(codec, reg2, tmpval1); + snd_soc_write(codec, reg1, 0x01); + snd_soc_write(codec, reg2, tmpval2); + } else { + dev_err(codec->dev, "%s: ANC0 co-eff index not aligned\n", + __func__); + } +} + +static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + const char *filename; + const struct firmware *fw; + int i; + int ret = 0; + int num_anc_slots; + struct wcd9xxx_anc_header *anc_head; + struct firmware_cal *hwdep_cal = NULL; + u32 anc_writes_size = 0; + u32 anc_cal_size = 0; + int anc_size_remaining; + u32 *anc_ptr; + u16 reg; + u8 mask, val; + size_t cal_size; + const void *data; + + if (!tasha->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_ANC_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + filename = "wcd9335/wcd9335_anc.bin"; + ret = request_firmware(&fw, filename, codec->dev); + if (ret != 0) { + dev_err(codec->dev, + "Failed to acquire ANC data: %d\n", ret); + return -ENODEV; + } + if (!fw) { + dev_err(codec->dev, "failed to get anc fw"); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, + "%s: using request_firmware calibration\n", __func__); + } + if (cal_size < sizeof(struct wcd9xxx_anc_header)) { + dev_err(codec->dev, "Not enough data\n"); + ret = -ENOMEM; + goto err; + } + /* First number is the number of register writes */ + anc_head = (struct wcd9xxx_anc_header *)(data); + anc_ptr = (u32 *)(data + + sizeof(struct wcd9xxx_anc_header)); + anc_size_remaining = cal_size - + sizeof(struct wcd9xxx_anc_header); + num_anc_slots = anc_head->num_anc_slots; + + if (tasha->anc_slot >= num_anc_slots) { + dev_err(codec->dev, "Invalid ANC slot selected\n"); + ret = -EINVAL; + goto err; + } + for (i = 0; i < num_anc_slots; i++) { + if (anc_size_remaining < TASHA_PACKED_REG_SIZE) { + dev_err(codec->dev, + "Invalid register format\n"); + ret = -EINVAL; + goto err; + } + anc_writes_size = (u32)(*anc_ptr); + anc_size_remaining -= sizeof(u32); + anc_ptr += 1; + + if (anc_writes_size * TASHA_PACKED_REG_SIZE + > anc_size_remaining) { + dev_err(codec->dev, + "Invalid register format\n"); + ret = -EINVAL; + goto err; + } + + if (tasha->anc_slot == i) + break; + + anc_size_remaining -= (anc_writes_size * + TASHA_PACKED_REG_SIZE); + anc_ptr += anc_writes_size; + } + if (i == num_anc_slots) { + dev_err(codec->dev, "Selected ANC slot not present\n"); + ret = -EINVAL; + goto err; + } + + i = 0; + anc_cal_size = anc_writes_size; + + if (!strcmp(w->name, "RX INT0 DAC") || + !strcmp(w->name, "ANC SPK1 PA")) + tasha_realign_anc_coeff(codec, + WCD9335_CDC_ANC0_IIR_COEFF_1_CTL, + WCD9335_CDC_ANC0_IIR_COEFF_2_CTL); + + if (!strcmp(w->name, "RX INT1 DAC") || + !strcmp(w->name, "RX INT3 DAC")) { + tasha_realign_anc_coeff(codec, + WCD9335_CDC_ANC0_IIR_COEFF_1_CTL, + WCD9335_CDC_ANC0_IIR_COEFF_2_CTL); + anc_writes_size = anc_cal_size / 2; + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x39, 0x39); + } else if (!strcmp(w->name, "RX INT2 DAC") || + !strcmp(w->name, "RX INT4 DAC")) { + tasha_realign_anc_coeff(codec, + WCD9335_CDC_ANC1_IIR_COEFF_1_CTL, + WCD9335_CDC_ANC1_IIR_COEFF_2_CTL); + i = anc_cal_size / 2; + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x39, 0x39); + } + + for (; i < anc_writes_size; i++) { + TASHA_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val); + snd_soc_write(codec, reg, (val & mask)); + } + if (!strcmp(w->name, "RX INT1 DAC") || + !strcmp(w->name, "RX INT3 DAC")) { + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x08, 0x08); + } else if (!strcmp(w->name, "RX INT2 DAC") || + !strcmp(w->name, "RX INT4 DAC")) { + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x08, 0x08); + } + + if (!hwdep_cal) + release_firmware(fw); + break; + case SND_SOC_DAPM_POST_PMU: + /* Remove ANC Rx from reset */ + snd_soc_update_bits(codec, WCD9335_CDC_ANC0_CLK_RESET_CTL, + 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_ANC1_CLK_RESET_CTL, + 0x08, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + if (!strcmp(w->name, "ANC HPHL PA") || + !strcmp(w->name, "ANC EAR PA") || + !strcmp(w->name, "ANC SPK1 PA") || + !strcmp(w->name, "ANC LINEOUT1 PA")) { + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_MODE_1_CTL, 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_MODE_1_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x38); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x07, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x00); + } else if (!strcmp(w->name, "ANC HPHR PA") || + !strcmp(w->name, "ANC LINEOUT2 PA")) { + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_MODE_1_CTL, 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_MODE_1_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x38, 0x38); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x07, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x38, 0x00); + } + break; + } + + return 0; +err: + if (!hwdep_cal) + release_firmware(fw); + return ret; +} + +static void tasha_codec_clear_anc_tx_hold(struct tasha_priv *tasha) +{ + if (test_and_clear_bit(ANC_MIC_AMIC1, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC1, false); + if (test_and_clear_bit(ANC_MIC_AMIC2, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC2, false); + if (test_and_clear_bit(ANC_MIC_AMIC3, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC3, false); + if (test_and_clear_bit(ANC_MIC_AMIC4, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC4, false); + if (test_and_clear_bit(ANC_MIC_AMIC5, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC5, false); + if (test_and_clear_bit(ANC_MIC_AMIC6, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC6, false); +} + +static void tasha_codec_hph_post_pa_config(struct tasha_priv *tasha, + int mode, int event) +{ + u8 scale_val = 0; + + if (!TASHA_IS_2_0(tasha->wcd9xxx)) + return; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + switch (mode) { + case CLS_H_HIFI: + scale_val = 0x3; + break; + case CLS_H_LOHIFI: + scale_val = 0x1; + break; + } + if (tasha->anc_func) { + /* Clear Tx FE HOLD if both PAs are enabled */ + if ((snd_soc_read(tasha->codec, WCD9335_ANA_HPH) & + 0xC0) == 0xC0) { + tasha_codec_clear_anc_tx_hold(tasha); + } + } + break; + case SND_SOC_DAPM_PRE_PMD: + scale_val = 0x6; + break; + } + + if (scale_val) + snd_soc_update_bits(tasha->codec, WCD9335_HPH_PA_CTL1, 0x0E, + scale_val << 1); + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (tasha->comp_enabled[COMPANDER_1] || + tasha->comp_enabled[COMPANDER_2]) { + snd_soc_update_bits(tasha->codec, WCD9335_HPH_L_EN, + 0x20, 0x00); + snd_soc_update_bits(tasha->codec, WCD9335_HPH_R_EN, + 0x20, 0x00); + snd_soc_update_bits(tasha->codec, WCD9335_HPH_AUTO_CHOP, + 0x20, 0x20); + } + snd_soc_update_bits(tasha->codec, WCD9335_HPH_L_EN, 0x1F, + tasha->hph_l_gain); + snd_soc_update_bits(tasha->codec, WCD9335_HPH_R_EN, 0x1F, + tasha->hph_r_gain); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(tasha->codec, WCD9335_HPH_AUTO_CHOP, 0x20, + 0x00); + } +} + +static void tasha_codec_override(struct snd_soc_codec *codec, + int mode, + int event) +{ + if (mode == CLS_AB) { + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!(snd_soc_read(codec, + WCD9335_CDC_RX2_RX_PATH_CTL) & 0x10) && + (!(snd_soc_read(codec, + WCD9335_CDC_RX1_RX_PATH_CTL) & 0x10))) + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x02); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x00); + break; + } + } +} + +static int tasha_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int hph_mode = tasha->hph_mode; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if ((!(strcmp(w->name, "ANC HPHR PA"))) && + (test_bit(HPH_PA_DELAY, &tasha->status_mask))) { + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0); + } + set_bit(HPH_PA_DELAY, &tasha->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (!(strcmp(w->name, "ANC HPHR PA"))) { + if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) + * then do nothing for POST_PMU and let left + * channel handle everything. + */ + break; + } + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement + */ + if (test_bit(HPH_PA_DELAY, &tasha->status_mask)) { + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &tasha->status_mask); + } + tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + snd_soc_update_bits(codec, WCD9335_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + + if (!(strcmp(w->name, "ANC HPHR PA"))) { + /* Do everything needed for left channel */ + snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + /* Remove ANC Rx from reset */ + ret = tasha_codec_enable_anc(w, kcontrol, event); + } + tasha_codec_override(codec, hph_mode, event); + break; + + case SND_SOC_DAPM_PRE_PMD: + blocking_notifier_call_chain(&tasha->notifier, + WCD_EVENT_PRE_HPHR_PA_OFF, + &tasha->mbhc); + tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + if (!(strcmp(w->name, "ANC HPHR PA"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x40, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + tasha_codec_override(codec, hph_mode, event); + blocking_notifier_call_chain(&tasha->notifier, + WCD_EVENT_POST_HPHR_PA_OFF, + &tasha->mbhc); + + if (!(strcmp(w->name, "ANC HPHR PA"))) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_CFG0, 0x10, 0x00); + } + break; + }; + + return ret; +} + +static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int hph_mode = tasha->hph_mode; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if ((!(strcmp(w->name, "ANC HPHL PA"))) && + (test_bit(HPH_PA_DELAY, &tasha->status_mask))) { + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0); + } + set_bit(HPH_PA_DELAY, &tasha->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (!(strcmp(w->name, "ANC HPHL PA"))) { + if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) + * then do nothing for POST_PMU and let right + * channel handle everything. + */ + break; + } + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement + */ + if (test_bit(HPH_PA_DELAY, &tasha->status_mask)) { + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &tasha->status_mask); + } + + tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + + if (!(strcmp(w->name, "ANC HPHL PA"))) { + /* Do everything needed for right channel */ + snd_soc_update_bits(codec, WCD9335_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + + /* Remove ANC Rx from reset */ + ret = tasha_codec_enable_anc(w, kcontrol, event); + } + tasha_codec_override(codec, hph_mode, event); + break; + case SND_SOC_DAPM_PRE_PMD: + blocking_notifier_call_chain(&tasha->notifier, + WCD_EVENT_PRE_HPHL_PA_OFF, + &tasha->mbhc); + tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + if (!(strcmp(w->name, "ANC HPHL PA"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x80, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + tasha_codec_override(codec, hph_mode, event); + blocking_notifier_call_chain(&tasha->notifier, + WCD_EVENT_POST_HPHL_PA_OFF, + &tasha->mbhc); + + if (!(strcmp(w->name, "ANC HPHL PA"))) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_CFG0, 0x10, 0x00); + } + break; + }; + + return ret; +} + +static int tasha_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 lineout_vol_reg = 0, lineout_mix_vol_reg = 0; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (w->reg == WCD9335_ANA_LO_1_2) { + if (w->shift == 7) { + lineout_vol_reg = WCD9335_CDC_RX3_RX_PATH_CTL; + lineout_mix_vol_reg = WCD9335_CDC_RX3_RX_PATH_MIX_CTL; + } else if (w->shift == 6) { + lineout_vol_reg = WCD9335_CDC_RX4_RX_PATH_CTL; + lineout_mix_vol_reg = WCD9335_CDC_RX4_RX_PATH_MIX_CTL; + } + } else if (w->reg == WCD9335_ANA_LO_3_4) { + if (w->shift == 7) { + lineout_vol_reg = WCD9335_CDC_RX5_RX_PATH_CTL; + lineout_mix_vol_reg = WCD9335_CDC_RX5_RX_PATH_MIX_CTL; + } else if (w->shift == 6) { + lineout_vol_reg = WCD9335_CDC_RX6_RX_PATH_CTL; + lineout_mix_vol_reg = WCD9335_CDC_RX6_RX_PATH_MIX_CTL; + } + } else { + dev_err(codec->dev, "%s: Error enabling lineout PA\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, lineout_vol_reg, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, lineout_mix_vol_reg)) & 0x10) + snd_soc_update_bits(codec, + lineout_mix_vol_reg, + 0x10, 0x00); + if (!(strcmp(w->name, "ANC LINEOUT1 PA")) || + !(strcmp(w->name, "ANC LINEOUT2 PA"))) + ret = tasha_codec_enable_anc(w, kcontrol, event); + tasha_codec_override(codec, CLS_AB, event); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + tasha_codec_override(codec, CLS_AB, event); + if (!(strcmp(w->name, "ANC LINEOUT1 PA")) || + !(strcmp(w->name, "ANC LINEOUT2 PA"))) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + if (!(strcmp(w->name, "ANC LINEOUT1 PA"))) + snd_soc_update_bits(codec, + WCD9335_CDC_RX3_RX_PATH_CFG0, 0x10, 0x10); + else + snd_soc_update_bits(codec, + WCD9335_CDC_RX4_RX_PATH_CFG0, 0x10, 0x10); + } + break; + }; + + return ret; +} + +static void tasha_spk_anc_update_callback(struct work_struct *work) +{ + struct spk_anc_work *spk_anc_dwork; + struct tasha_priv *tasha; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + + delayed_work = to_delayed_work(work); + spk_anc_dwork = container_of(delayed_work, struct spk_anc_work, dwork); + tasha = spk_anc_dwork->tasha; + codec = tasha->codec; + + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0, 0x10, 0x10); +} + +static int tasha_codec_enable_spk_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d %d\n", __func__, w->name, event, + tasha->anc_func); + + if (!tasha->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tasha_codec_enable_anc(w, kcontrol, event); + schedule_delayed_work(&tasha->spk_anc_dwork.dwork, + msecs_to_jiffies(spk_anc_en_delay)); + break; + case SND_SOC_DAPM_POST_PMD: + cancel_delayed_work_sync(&tasha->spk_anc_dwork.dwork); + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0, + 0x10, 0x00); + ret = tasha_codec_enable_anc(w, kcontrol, event); + break; + } + return ret; +} + +static int tasha_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, WCD9335_CDC_RX0_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD9335_CDC_RX0_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX0_RX_PATH_MIX_CTL, + 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + + if (!(strcmp(w->name, "ANC EAR PA"))) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD9335_CDC_RX0_RX_PATH_CFG0, 0x10, 0x00); + } + break; + }; + + return ret; +} + +static void tasha_codec_hph_mode_gain_opt(struct snd_soc_codec *codec, + u8 gain) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u8 hph_l_en, hph_r_en; + u8 l_val, r_val; + u8 hph_pa_status; + bool is_hphl_pa, is_hphr_pa; + + hph_pa_status = snd_soc_read(codec, WCD9335_ANA_HPH); + is_hphl_pa = hph_pa_status >> 7; + is_hphr_pa = (hph_pa_status & 0x40) >> 6; + + hph_l_en = snd_soc_read(codec, WCD9335_HPH_L_EN); + hph_r_en = snd_soc_read(codec, WCD9335_HPH_R_EN); + + l_val = (hph_l_en & 0xC0) | 0x20 | gain; + r_val = (hph_r_en & 0xC0) | 0x20 | gain; + + /* + * Set HPH_L & HPH_R gain source selection to REGISTER + * for better click and pop only if corresponding PAs are + * not enabled. Also cache the values of the HPHL/R + * PA gains to be applied after PAs are enabled + */ + if ((l_val != hph_l_en) && !is_hphl_pa) { + snd_soc_write(codec, WCD9335_HPH_L_EN, l_val); + tasha->hph_l_gain = hph_l_en & 0x1F; + } + + if ((r_val != hph_r_en) && !is_hphr_pa) { + snd_soc_write(codec, WCD9335_HPH_R_EN, r_val); + tasha->hph_r_gain = hph_r_en & 0x1F; + } +} + +static void tasha_codec_hph_lohifi_config(struct snd_soc_codec *codec, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_PA, 0x0F, 0x06); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, + 0xF0, 0x40); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C); + tasha_codec_hph_mode_gain_opt(codec, 0x11); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02); + snd_soc_write(codec, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8A); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_PA, 0x0F, 0x0A); + } +} + +static void tasha_codec_hph_lp_config(struct snd_soc_codec *codec, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C); + tasha_codec_hph_mode_gain_opt(codec, 0x10); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x04, 0x04); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x20, 0x20); + snd_soc_update_bits(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x07, + 0x01); + snd_soc_update_bits(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x70, + 0x10); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO, + 0x0F, 0x01); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO, + 0xF0, 0x10); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_write(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO, 0x88); + snd_soc_write(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x33); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x20, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x04, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02); + snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0xC0, 0x80); + snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0xC0, 0x80); + } +} + +static void tasha_codec_hph_hifi_config(struct snd_soc_codec *codec, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C); + tasha_codec_hph_mode_gain_opt(codec, 0x11); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02); + } +} + +static void tasha_codec_hph_mode_config(struct snd_soc_codec *codec, + int event, int mode) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (!TASHA_IS_2_0(tasha->wcd9xxx)) + return; + + switch (mode) { + case CLS_H_LP: + tasha_codec_hph_lp_config(codec, event); + break; + case CLS_H_LOHIFI: + tasha_codec_hph_lohifi_config(codec, event); + break; + case CLS_H_HIFI: + tasha_codec_hph_hifi_config(codec, event); + break; + } +} + +static int tasha_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int hph_mode = tasha->hph_mode; + u8 dem_inp; + int ret = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tasha->anc_func) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + /* 40 msec delay is needed to avoid click and pop */ + msleep(40); + } + + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, WCD9335_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)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + tasha_codec_hph_mode_config(codec, event, hph_mode); + + if (tasha->anc_func) + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_CFG0, 0x10, 0x10); + + break; + case SND_SOC_DAPM_POST_PMU: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + if ((hph_mode == CLS_H_LP) && + (TASHA_IS_1_1(wcd9xxx))) { + snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, + 0x03, 0x03); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if ((hph_mode == CLS_H_LP) && + (TASHA_IS_1_1(wcd9xxx))) { + snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, + 0x03, 0x00); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + if (!(wcd_clsh_get_clsh_state(&tasha->clsh_d) & + WCD_CLSH_STATE_HPHL)) + tasha_codec_hph_mode_config(codec, event, hph_mode); + + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + break; + }; + + return ret; +} + +static int tasha_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int hph_mode = tasha->hph_mode; + u8 dem_inp; + int ret = 0; + uint32_t impedl = 0, impedr = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tasha->anc_func) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + /* 40 msec delay is needed to avoid click and pop */ + msleep(40); + } + + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, WCD9335_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)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + tasha_codec_hph_mode_config(codec, event, hph_mode); + + if (tasha->anc_func) + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_CFG0, 0x10, 0x10); + + ret = wcd_mbhc_get_impedance(&tasha->mbhc, + &impedl, &impedr); + if (!ret) { + wcd_clsh_imped_config(codec, impedl, false); + set_bit(CLASSH_CONFIG, &tasha->status_mask); + } else { + dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n", + __func__, ret); + ret = 0; + } + + + break; + case SND_SOC_DAPM_POST_PMU: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + if ((hph_mode == CLS_H_LP) && + (TASHA_IS_1_1(wcd9xxx))) { + snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, + 0x03, 0x03); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if ((hph_mode == CLS_H_LP) && + (TASHA_IS_1_1(wcd9xxx))) { + snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, + 0x03, 0x00); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + if (!(wcd_clsh_get_clsh_state(&tasha->clsh_d) & + WCD_CLSH_STATE_HPHR)) + tasha_codec_hph_mode_config(codec, event, hph_mode); + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + if (test_bit(CLASSH_CONFIG, &tasha->status_mask)) { + wcd_clsh_imped_config(codec, impedl, true); + clear_bit(CLASSH_CONFIG, &tasha->status_mask); + } else + dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n", + __func__, ret); + + + break; + }; + + return ret; +} + +static int tasha_codec_lineout_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tasha->anc_func && + (!strcmp(w->name, "RX INT3 DAC") || + !strcmp(w->name, "RX INT4 DAC"))) + ret = tasha_codec_enable_anc(w, kcontrol, event); + + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_LO, + CLS_AB); + + if (tasha->anc_func) { + if (!strcmp(w->name, "RX INT3 DAC")) + snd_soc_update_bits(codec, + WCD9335_CDC_RX3_RX_PATH_CFG0, 0x10, 0x10); + else if (!strcmp(w->name, "RX INT4 DAC")) + snd_soc_update_bits(codec, + WCD9335_CDC_RX4_RX_PATH_CFG0, 0x10, 0x10); + } + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_LO, + CLS_AB); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget tasha_dapm_i2s_widgets[] = { + SND_SOC_DAPM_SUPPLY("RX_I2S_CTL", WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TX_I2S_CTL", WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0, 0, NULL, 0), +}; + +static int tasha_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tasha->anc_func) + ret = tasha_codec_enable_anc(w, kcontrol, event); + + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, + CLS_H_NORMAL); + if (tasha->anc_func) + snd_soc_update_bits(codec, + WCD9335_CDC_RX0_RX_PATH_CFG0, 0x10, 0x10); + + break; + case SND_SOC_DAPM_POST_PMU: + break; + case SND_SOC_DAPM_PRE_PMD: + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, + CLS_H_NORMAL); + break; + }; + + return ret; +} + +static int tasha_codec_spk_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg, reg_mix; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (!strcmp(w->name, "RX INT7 CHAIN")) { + boost_path_ctl = WCD9335_CDC_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = WCD9335_CDC_RX7_RX_PATH_CFG1; + reg = WCD9335_CDC_RX7_RX_PATH_CTL; + reg_mix = WCD9335_CDC_RX7_RX_PATH_MIX_CTL; + } else if (!strcmp(w->name, "RX INT8 CHAIN")) { + boost_path_ctl = WCD9335_CDC_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = WCD9335_CDC_RX8_RX_PATH_CFG1; + reg = WCD9335_CDC_RX8_RX_PATH_CTL; + reg_mix = WCD9335_CDC_RX8_RX_PATH_MIX_CTL; + } else { + dev_err(codec->dev, "%s: unknown widget: %s\n", + __func__, w->name); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10); + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01); + snd_soc_update_bits(codec, reg, 0x10, 0x00); + if ((snd_soc_read(codec, reg_mix)) & 0x10) + snd_soc_update_bits(codec, reg_mix, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00); + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00); + break; + }; + + return 0; +} + +static u16 tasha_interp_get_primary_reg(u16 reg, u16 *ind) +{ + u16 prim_int_reg = 0; + + switch (reg) { + case WCD9335_CDC_RX0_RX_PATH_CTL: + case WCD9335_CDC_RX0_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX0_RX_PATH_CTL; + *ind = 0; + break; + case WCD9335_CDC_RX1_RX_PATH_CTL: + case WCD9335_CDC_RX1_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX1_RX_PATH_CTL; + *ind = 1; + break; + case WCD9335_CDC_RX2_RX_PATH_CTL: + case WCD9335_CDC_RX2_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX2_RX_PATH_CTL; + *ind = 2; + break; + case WCD9335_CDC_RX3_RX_PATH_CTL: + case WCD9335_CDC_RX3_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX3_RX_PATH_CTL; + *ind = 3; + break; + case WCD9335_CDC_RX4_RX_PATH_CTL: + case WCD9335_CDC_RX4_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX4_RX_PATH_CTL; + *ind = 4; + break; + case WCD9335_CDC_RX5_RX_PATH_CTL: + case WCD9335_CDC_RX5_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX5_RX_PATH_CTL; + *ind = 5; + break; + case WCD9335_CDC_RX6_RX_PATH_CTL: + case WCD9335_CDC_RX6_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX6_RX_PATH_CTL; + *ind = 6; + break; + case WCD9335_CDC_RX7_RX_PATH_CTL: + case WCD9335_CDC_RX7_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX7_RX_PATH_CTL; + *ind = 7; + break; + case WCD9335_CDC_RX8_RX_PATH_CTL: + case WCD9335_CDC_RX8_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX8_RX_PATH_CTL; + *ind = 8; + break; + }; + + return prim_int_reg; +} + +static void tasha_codec_hd2_control(struct snd_soc_codec *codec, + u16 prim_int_reg, int event) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + if (!TASHA_IS_2_0(tasha->wcd9xxx)) + return; + + if (prim_int_reg == WCD9335_CDC_RX1_RX_PATH_CTL) { + hd2_scale_reg = WCD9335_CDC_RX1_RX_PATH_SEC3; + hd2_enable_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + } + if (prim_int_reg == WCD9335_CDC_RX2_RX_PATH_CTL) { + hd2_scale_reg = WCD9335_CDC_RX2_RX_PATH_SEC3; + hd2_enable_reg = WCD9335_CDC_RX2_RX_PATH_CFG0; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01); + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); + } +} + +static int tasha_codec_enable_prim_interpolator( + struct snd_soc_codec *codec, + u16 reg, int event) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 prim_int_reg; + u16 ind = 0; + + prim_int_reg = tasha_interp_get_primary_reg(reg, &ind); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tasha->prim_int_users[ind]++; + if (tasha->prim_int_users[ind] == 1) { + snd_soc_update_bits(codec, prim_int_reg, + 0x10, 0x10); + tasha_codec_hd2_control(codec, prim_int_reg, event); + snd_soc_update_bits(codec, prim_int_reg, + 1 << 0x5, 1 << 0x5); + } + if ((reg != prim_int_reg) && + ((snd_soc_read(codec, prim_int_reg)) & 0x10)) + snd_soc_update_bits(codec, reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + tasha->prim_int_users[ind]--; + if (tasha->prim_int_users[ind] == 0) { + snd_soc_update_bits(codec, prim_int_reg, + 1 << 0x5, 0 << 0x5); + snd_soc_update_bits(codec, prim_int_reg, + 0x40, 0x40); + snd_soc_update_bits(codec, prim_int_reg, + 0x40, 0x00); + tasha_codec_hd2_control(codec, prim_int_reg, event); + } + break; + }; + + dev_dbg(codec->dev, "%s: primary interpolator: INT%d, users: %d\n", + __func__, ind, tasha->prim_int_users[ind]); + return 0; +} + +static int tasha_codec_enable_spline_src(struct snd_soc_codec *codec, + int src_num, + int event) +{ + u16 src_paired_reg; + struct tasha_priv *tasha; + u16 rx_path_cfg_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + u16 rx_path_ctl_reg = WCD9335_CDC_RX1_RX_PATH_CTL; + int *src_users, count, spl_src = SPLINE_SRC0; + u16 src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + + tasha = snd_soc_codec_get_drvdata(codec); + + switch (src_num) { + case SRC_IN_HPHL: + rx_path_cfg_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX1_RX_PATH_CTL; + spl_src = SPLINE_SRC0; + break; + case SRC_IN_LO1: + rx_path_cfg_reg = WCD9335_CDC_RX3_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX3_RX_PATH_CTL; + spl_src = SPLINE_SRC0; + break; + case SRC_IN_HPHR: + rx_path_cfg_reg = WCD9335_CDC_RX2_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX2_RX_PATH_CTL; + spl_src = SPLINE_SRC1; + break; + case SRC_IN_LO2: + rx_path_cfg_reg = WCD9335_CDC_RX4_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX4_RX_PATH_CTL; + spl_src = SPLINE_SRC1; + break; + case SRC_IN_SPKRL: + rx_path_cfg_reg = WCD9335_CDC_RX7_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX7_RX_PATH_CTL; + spl_src = SPLINE_SRC2; + break; + case SRC_IN_LO3: + rx_path_cfg_reg = WCD9335_CDC_RX5_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX5_RX_PATH_CTL; + spl_src = SPLINE_SRC2; + break; + case SRC_IN_SPKRR: + rx_path_cfg_reg = WCD9335_CDC_RX8_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX8_RX_PATH_CTL; + spl_src = SPLINE_SRC3; + break; + case SRC_IN_LO4: + rx_path_cfg_reg = WCD9335_CDC_RX6_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX6_RX_PATH_CTL; + spl_src = SPLINE_SRC3; + break; + }; + + src_users = &tasha->spl_src_users[spl_src]; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + count = *src_users; + count++; + if (count == 1) { + if ((snd_soc_read(codec, src_clk_reg) & 0x02) || + (snd_soc_read(codec, src_paired_reg) & 0x02)) { + snd_soc_update_bits(codec, src_clk_reg, 0x02, + 0x00); + snd_soc_update_bits(codec, src_paired_reg, + 0x02, 0x00); + } + snd_soc_update_bits(codec, src_clk_reg, 0x01, 0x01); + snd_soc_update_bits(codec, rx_path_cfg_reg, 0x80, + 0x80); + } + *src_users = count; + break; + case SND_SOC_DAPM_POST_PMD: + count = *src_users; + count--; + if (count == 0) { + snd_soc_update_bits(codec, rx_path_cfg_reg, 0x80, + 0x00); + snd_soc_update_bits(codec, src_clk_reg, 0x03, 0x02); + /* default sample rate */ + snd_soc_update_bits(codec, rx_path_ctl_reg, 0x0f, + 0x04); + } + *src_users = count; + break; + }; + + dev_dbg(codec->dev, "%s: Spline SRC%d, users: %d\n", + __func__, spl_src, *src_users); + return 0; +} + +static int tasha_codec_enable_spline_resampler(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + u8 src_in; + + src_in = snd_soc_read(codec, WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0); + if (!(src_in & 0xFF)) { + dev_err(codec->dev, "%s: Spline SRC%u input not selected\n", + __func__, w->shift); + return -EINVAL; + } + + switch (w->shift) { + case SPLINE_SRC0: + ret = tasha_codec_enable_spline_src(codec, + ((src_in & 0x03) == 1) ? SRC_IN_HPHL : SRC_IN_LO1, + event); + break; + case SPLINE_SRC1: + ret = tasha_codec_enable_spline_src(codec, + ((src_in & 0x0C) == 4) ? SRC_IN_HPHR : SRC_IN_LO2, + event); + break; + case SPLINE_SRC2: + ret = tasha_codec_enable_spline_src(codec, + ((src_in & 0x30) == 0x10) ? SRC_IN_LO3 : SRC_IN_SPKRL, + event); + break; + case SPLINE_SRC3: + ret = tasha_codec_enable_spline_src(codec, + ((src_in & 0xC0) == 0x40) ? SRC_IN_LO4 : SRC_IN_SPKRR, + event); + break; + default: + dev_err(codec->dev, "%s: Invalid spline src:%u\n", __func__, + w->shift); + ret = -EINVAL; + }; + + return ret; +} + +static int tasha_codec_enable_swr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha; + int i, ch_cnt; + + tasha = snd_soc_codec_get_drvdata(codec); + + if (!tasha->nr) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) && + !tasha->rx_7_count) + tasha->rx_7_count++; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + !tasha->rx_8_count) + tasha->rx_8_count++; + ch_cnt = tasha->rx_7_count + tasha->rx_8_count; + + for (i = 0; i < tasha->nr; i++) { + swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev, + SWR_DEVICE_UP, NULL); + swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + } + break; + case SND_SOC_DAPM_POST_PMD: + if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) && + tasha->rx_7_count) + tasha->rx_7_count--; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + tasha->rx_8_count) + tasha->rx_8_count--; + ch_cnt = tasha->rx_7_count + tasha->rx_8_count; + + for (i = 0; i < tasha->nr; i++) + swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + + break; + } + dev_dbg(tasha->dev, "%s: current swr ch cnt: %d\n", + __func__, tasha->rx_7_count + tasha->rx_8_count); + + return 0; +} + +static int tasha_codec_config_ear_spkr_gain(struct snd_soc_codec *codec, + int event, int gain_reg) +{ + int comp_gain_offset, val; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + switch (tasha->spkr_mode) { + /* Compander gain in SPKR_MODE1 case is 12 dB */ + case SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (tasha->comp_enabled[COMPANDER_7] && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL) && + (tasha->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + tasha->ear_spkr_gain - 1; + snd_soc_write(codec, gain_reg, val); + + dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n", + __func__, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX7 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (tasha->comp_enabled[COMPANDER_7] && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL) && + (tasha->ear_spkr_gain != 0)) { + snd_soc_write(codec, gain_reg, 0x0); + + dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n", + __func__); + } + break; + } + + return 0; +} + +static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 gain_reg; + int offset_val = 0; + int val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + switch (w->reg) { + case WCD9335_CDC_RX0_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX0_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX1_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX1_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX2_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX2_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX3_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX3_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX4_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX4_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX5_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX5_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX6_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX6_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX7_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX7_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX8_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX8_RX_VOL_MIX_CTL; + break; + default: + dev_err(codec->dev, "%s: No gain register avail for %s\n", + __func__, w->name); + return 0; + }; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (tasha->comp_enabled[COMPANDER_7] || + tasha->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD9335_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (tasha->comp_enabled[COMPANDER_7] || + tasha->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD9335_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + + return 0; +} + +static int __tasha_cdc_native_clk_enable(struct tasha_priv *tasha, + bool enable) +{ + int ret = 0; + struct snd_soc_codec *codec = tasha->codec; + + if (!tasha->wcd_native_clk) { + dev_err(tasha->dev, "%s: wcd native clock is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(tasha->dev, "%s: native_clk_enable = %u\n", __func__, enable); + + if (enable) { + ret = clk_prepare_enable(tasha->wcd_native_clk); + if (ret) { + dev_err(tasha->dev, "%s: native clk enable failed\n", + __func__); + goto err; + } + if (++tasha->native_clk_users == 1) { + snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL, + 0x80, 0x80); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_GATE, + 0x04, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x02); + } + } else { + if (tasha->native_clk_users && + (--tasha->native_clk_users == 0)) { + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_GATE, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL, + 0x10, 0x00); + } + clk_disable_unprepare(tasha->wcd_native_clk); + } + + dev_dbg(codec->dev, "%s: native_clk_users: %d\n", __func__, + tasha->native_clk_users); +err: + return ret; +} + +static int tasha_codec_get_native_fifo_sync_mask(struct snd_soc_codec *codec, + int interp_n) +{ + int mask = 0; + u16 reg; + u8 val1, val2, inp0 = 0; + u8 inp1 = 0, inp2 = 0; + + reg = WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0 + (2 * interp_n) - 2; + + val1 = snd_soc_read(codec, reg); + val2 = snd_soc_read(codec, reg + 1); + + inp0 = val1 & 0x0F; + inp1 = (val1 >> 4) & 0x0F; + inp2 = (val2 >> 4) & 0x0F; + + if (IS_VALID_NATIVE_FIFO_PORT(inp0)) + mask |= (1 << (inp0 - 5)); + if (IS_VALID_NATIVE_FIFO_PORT(inp1)) + mask |= (1 << (inp1 - 5)); + if (IS_VALID_NATIVE_FIFO_PORT(inp2)) + mask |= (1 << (inp2 - 5)); + + dev_dbg(codec->dev, "%s: native fifo mask: 0x%x\n", __func__, mask); + if (!mask) + dev_err(codec->dev, "native fifo err,int:%d,inp0:%d,inp1:%d,inp2:%d\n", + interp_n, inp0, inp1, inp2); + return mask; +} + +static int tasha_enable_native_supply(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int mask; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 interp_reg; + + dev_dbg(codec->dev, "%s: event: %d, shift:%d\n", __func__, event, + w->shift); + + if (w->shift < INTERP_HPHL || w->shift > INTERP_LO2) + return -EINVAL; + + interp_reg = WCD9335_CDC_RX1_RX_PATH_CTL + 20 * (w->shift - 1); + + mask = tasha_codec_get_native_fifo_sync_mask(codec, w->shift); + if (!mask) + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Adjust interpolator rate to 44P1_NATIVE */ + snd_soc_update_bits(codec, interp_reg, 0x0F, 0x09); + __tasha_cdc_native_clk_enable(tasha, true); + snd_soc_update_bits(codec, WCD9335_DATA_HUB_NATIVE_FIFO_SYNC, + mask, mask); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WCD9335_DATA_HUB_NATIVE_FIFO_SYNC, + mask, 0x0); + __tasha_cdc_native_clk_enable(tasha, false); + /* Adjust interpolator rate to default */ + snd_soc_update_bits(codec, interp_reg, 0x0F, 0x04); + break; + } + + return 0; +} + +static int tasha_codec_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (!(strcmp(w->name, "RX INT0 INTERP"))) { + reg = WCD9335_CDC_RX0_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX0_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT1 INTERP"))) { + reg = WCD9335_CDC_RX1_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX1_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT2 INTERP"))) { + reg = WCD9335_CDC_RX2_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX2_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT3 INTERP"))) { + reg = WCD9335_CDC_RX3_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX3_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT4 INTERP"))) { + reg = WCD9335_CDC_RX4_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX4_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT5 INTERP"))) { + reg = WCD9335_CDC_RX5_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX5_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT6 INTERP"))) { + reg = WCD9335_CDC_RX6_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX6_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT7 INTERP"))) { + reg = WCD9335_CDC_RX7_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX7_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT8 INTERP"))) { + reg = WCD9335_CDC_RX8_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX8_RX_VOL_CTL; + } else { + dev_err(codec->dev, "%s: Interpolator reg not found\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (!test_bit(SB_CLK_GEAR, &tasha->status_mask)) { + tasha_codec_vote_max_bw(codec, true); + set_bit(SB_CLK_GEAR, &tasha->status_mask); + } + /* Reset if needed */ + tasha_codec_enable_prim_interpolator(codec, reg, event); + break; + case SND_SOC_DAPM_POST_PMU: + tasha_config_compander(codec, w->shift, event); + /* apply gain after int clk is enabled */ + if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (tasha->comp_enabled[COMPANDER_7] || + tasha->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9335_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + tasha_config_compander(codec, w->shift, event); + tasha_codec_enable_prim_interpolator(codec, reg, event); + if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (tasha->comp_enabled[COMPANDER_7] || + tasha->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9335_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + + return 0; +} + +static int tasha_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: /* fall through */ + case SND_SOC_DAPM_PRE_PMD: + if (strnstr(w->name, "IIR0", sizeof("IIR0"))) { + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)); + } else { + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)); + } + break; + } + return 0; +} + +static int tasha_codec_enable_on_demand_supply( + struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct on_demand_supply *supply; + + if (w->shift >= ON_DEMAND_SUPPLIES_MAX) { + dev_err(codec->dev, "%s: error index > MAX Demand supplies", + __func__); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, "%s: supply: %s event: %d\n", + __func__, on_demand_supply_name[w->shift], event); + + supply = &tasha->on_demand_list[w->shift]; + WARN_ONCE(!supply->supply, "%s isn't defined\n", + on_demand_supply_name[w->shift]); + if (!supply->supply) { + dev_err(codec->dev, "%s: err supply not present ond for %d", + __func__, w->shift); + goto out; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regulator_enable(supply->supply); + if (ret) + dev_err(codec->dev, "%s: Failed to enable %s\n", + __func__, + on_demand_supply_name[w->shift]); + break; + case SND_SOC_DAPM_POST_PMD: + ret = regulator_disable(supply->supply); + if (ret) + dev_err(codec->dev, "%s: Failed to disable %s\n", + __func__, + on_demand_supply_name[w->shift]); + break; + default: + break; + }; + +out: + return ret; +} + +static int tasha_codec_find_amic_input(struct snd_soc_codec *codec, + 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 > WCD9335_MAX_VALID_ADC_MUX || + adc_mux_n == WCD9335_INVALID_ADC_MUX) + return 0; + + /* Check whether adc mux input is AMIC or DMIC */ + if (adc_mux_n < 4) { + adc_mux_in_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + 2 * adc_mux_n; + amic_mux_sel_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + mask = 0x03; + shift = 0; + } else { + adc_mux_in_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + amic_mux_sel_reg = adc_mux_in_reg; + mask = 0xC0; + shift = 6; + } + is_amic = (((snd_soc_read(codec, adc_mux_in_reg) & mask) >> shift) + == 1); + if (!is_amic) + return 0; + + return snd_soc_read(codec, amic_mux_sel_reg) & 0x07; +} + +static void tasha_codec_set_tx_hold(struct snd_soc_codec *codec, + u16 amic_reg, bool set) +{ + u8 mask = 0x20; + u8 val; + + if (amic_reg == WCD9335_ANA_AMIC1 || + amic_reg == WCD9335_ANA_AMIC3 || + amic_reg == WCD9335_ANA_AMIC5) + mask = 0x40; + + val = set ? mask : 0x00; + + switch (amic_reg) { + case WCD9335_ANA_AMIC1: + case WCD9335_ANA_AMIC2: + snd_soc_update_bits(codec, WCD9335_ANA_AMIC2, mask, val); + break; + case WCD9335_ANA_AMIC3: + case WCD9335_ANA_AMIC4: + snd_soc_update_bits(codec, WCD9335_ANA_AMIC4, mask, val); + break; + case WCD9335_ANA_AMIC5: + case WCD9335_ANA_AMIC6: + snd_soc_update_bits(codec, WCD9335_ANA_AMIC6, mask, val); + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic_reg); + break; + } +} + +static int tasha_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int adc_mux_n = w->shift; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int amic_n; + + dev_dbg(codec->dev, "%s: event: %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + amic_n = tasha_codec_find_amic_input(codec, adc_mux_n); + if (amic_n) { + /* + * Prevent ANC Rx pop by leaving Tx FE in HOLD + * state until PA is up. Track AMIC being used + * so we can release the HOLD later. + */ + set_bit(ANC_MIC_AMIC1 + amic_n - 1, + &tasha->status_mask); + } + break; + default: + break; + } + + return 0; +} + +static u16 tasha_codec_get_amic_pwlvl_reg(struct snd_soc_codec *codec, int amic) +{ + u16 pwr_level_reg = 0; + + switch (amic) { + case 1: + case 2: + pwr_level_reg = WCD9335_ANA_AMIC1; + break; + + case 3: + case 4: + pwr_level_reg = WCD9335_ANA_AMIC3; + break; + + case 5: + case 6: + pwr_level_reg = WCD9335_ANA_AMIC5; + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic); + break; + } + + return pwr_level_reg; +} + +#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 + +static void tasha_tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct tasha_priv *tasha; + struct snd_soc_codec *codec; + u16 dec_cfg_reg, amic_reg; + u8 hpf_cut_off_freq; + int amic_n; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + tasha = hpf_work->tasha; + codec = tasha->codec; + hpf_cut_off_freq = hpf_work->hpf_cut_off_freq; + + dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator; + + dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, hpf_cut_off_freq); + + amic_n = tasha_codec_find_amic_input(codec, hpf_work->decimator); + if (amic_n) { + amic_reg = WCD9335_ANA_AMIC1 + amic_n - 1; + tasha_codec_set_tx_hold(codec, amic_reg, false); + } + tasha_codec_vote_max_bw(codec, true); + snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + tasha_codec_vote_max_bw(codec, false); +} + +static void tasha_tx_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork; + struct tasha_priv *tasha; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + u16 tx_vol_ctl_reg, hpf_gate_reg; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + tasha = tx_mute_dwork->tasha; + codec = tasha->codec; + + tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + + 16 * tx_mute_dwork->decimator; + hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 + + 16 * tx_mute_dwork->decimator; + snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x01); + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); +} + +static int tasha_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(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_cut_off_freq; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + 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(codec->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(codec->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, wname); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__, + w->name, decimator); + + tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + 16 * decimator; + hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; + dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = WCD9335_CDC_TX0_TX_VOL_CTL + 16 * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + amic_n = tasha_codec_find_amic_input(codec, decimator); + if (amic_n) + pwr_level_reg = tasha_codec_get_amic_pwlvl_reg(codec, + amic_n); + + if (pwr_level_reg) { + switch ((snd_soc_read(codec, pwr_level_reg) & + WCD9335_AMIC_PWR_LVL_MASK) >> + WCD9335_AMIC_PWR_LVL_SHIFT) { + case WCD9335_AMIC_PWR_LEVEL_LP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_LP); + break; + + case WCD9335_AMIC_PWR_LEVEL_HP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_HP); + break; + case WCD9335_AMIC_PWR_LEVEL_DEFAULT: + default: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_DF); + break; + } + } + hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + tasha->tx_hpf_work[decimator].hpf_cut_off_freq = + hpf_cut_off_freq; + + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + /* Enable TX PGA Mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + /* Enable APC */ + snd_soc_update_bits(codec, dec_cfg_reg, 0x08, 0x08); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x00); + + if (decimator == 0) { + snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x83); + snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0xA3); + snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x83); + snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x03); + } + /* schedule work queue to Remove Mute */ + schedule_delayed_work(&tasha->tx_mute_dwork[decimator].dwork, + msecs_to_jiffies(tx_unmute_delay)); + if (tasha->tx_hpf_work[decimator].hpf_cut_off_freq != + CF_MIN_3DB_150HZ) + schedule_delayed_work( + &tasha->tx_hpf_work[decimator].dwork, + msecs_to_jiffies(300)); + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_cut_off_freq = + tasha->tx_hpf_work[decimator].hpf_cut_off_freq; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + snd_soc_update_bits(codec, dec_cfg_reg, 0x08, 0x00); + if (cancel_delayed_work_sync( + &tasha->tx_hpf_work[decimator].dwork)) { + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + tasha_codec_vote_max_bw(codec, true); + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + tasha_codec_vote_max_bw(codec, false); + } + } + cancel_delayed_work_sync( + &tasha->tx_mute_dwork[decimator].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + break; + }; +out: + kfree(wname); + return ret; +} + +static u32 tasha_get_dmic_sample_rate(struct snd_soc_codec *codec, + unsigned int dmic, struct wcd9xxx_pdata *pdata) +{ + 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 < WCD9335_MAX_VALID_ADC_MUX) { + if (adc_mux_index < 4) { + adc_mux_ctl_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + (adc_mux_index * 2); + adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) & + 0x78) >> 3) - 1; + } else if (adc_mux_index < 9) { + adc_mux_ctl_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + ((adc_mux_index - 4) * 1); + adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) & + 0x38) >> 3) - 1; + } else if (adc_mux_index == 9) { + ++adc_mux_index; + continue; + } + if (adc_mux_sel == dmic) + dec_found = true; + else + ++adc_mux_index; + } + + if (dec_found == true && adc_mux_index <= 8) { + tx_fs_reg = WCD9335_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index); + tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F; + dmic_fs = tx_stream_fs <= 4 ? WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ : + WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + + /* + * Check for ECPP path selection and DEC1 not connected to + * any other audio path to apply ECPP DMIC sample rate + */ + if ((adc_mux_index == 1) && + ((snd_soc_read(codec, WCD9335_CPE_SS_US_EC_MUX_CFG) + & 0x0F) == 0x0A) && + ((snd_soc_read(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0) + & 0x0C) == 0x00)) { + dmic_fs = pdata->ecpp_dmic_sample_rate; + } + } else { + dmic_fs = pdata->dmic_sample_rate; + } + + return dmic_fs; +} + +static u8 tasha_get_dmic_clk_val(struct snd_soc_codec *codec, + u32 mclk_rate, u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val; + + dev_dbg(codec->dev, + "%s: mclk_rate = %d, dmic_sample_rate = %d\n", + __func__, mclk_rate, dmic_clk_rate); + + /* Default value to return in case of error */ + if (mclk_rate == TASHA_MCLK_CLK_9P6MHZ) + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; + else + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; + + if (dmic_clk_rate == 0) { + dev_err(codec->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 = WCD9335_DMIC_CLK_DIV_2; + break; + case 3: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; + break; + case 4: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_4; + break; + case 6: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_6; + break; + case 8: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_8; + break; + case 16: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_16; + break; + default: + dev_err(codec->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 tasha_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event:%d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tasha_codec_set_tx_hold(codec, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static int tasha_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + 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(codec->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(tasha->dmic_0_1_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(tasha->dmic_2_3_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(tasha->dmic_4_5_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC2_CTL; + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + }; + dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dmic_sample_rate = tasha_get_dmic_sample_rate(codec, dmic, + pdata); + dmic_rate_val = + tasha_get_dmic_clk_val(codec, + pdata->mclk_rate, + dmic_sample_rate); + + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + dmic_rate_val = + tasha_get_dmic_clk_val(codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + } + break; + }; + + return 0; +} + +static int __tasha_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int micb_num; + + dev_dbg(codec->dev, "%s: wname: %s, event: %d\n", + __func__, w->name, event); + + 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 + */ + tasha_micbias_control(codec, 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: + tasha_micbias_control(codec, micb_num, MICB_DISABLE, true); + break; + }; + + return 0; +} + +static int tasha_codec_ldo_h_control(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + tasha->ldo_h_users++; + + if (tasha->ldo_h_users == 1) + snd_soc_update_bits(codec, WCD9335_LDOH_MODE, + 0x80, 0x80); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + tasha->ldo_h_users--; + + if (tasha->ldo_h_users < 0) + tasha->ldo_h_users = 0; + + if (tasha->ldo_h_users == 0) + snd_soc_update_bits(codec, WCD9335_LDOH_MODE, + 0x80, 0x00); + } + + return 0; +} + +static int tasha_codec_force_enable_ldo_h(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_resmgr_enable_master_bias(tasha->resmgr); + tasha_codec_ldo_h_control(w, event); + break; + case SND_SOC_DAPM_POST_PMD: + tasha_codec_ldo_h_control(w, event); + wcd_resmgr_disable_master_bias(tasha->resmgr); + break; + } + + return 0; +} + +static int tasha_codec_force_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_resmgr_enable_master_bias(tasha->resmgr); + tasha_cdc_mclk_enable(codec, true, true); + ret = __tasha_codec_enable_micbias(w, SND_SOC_DAPM_PRE_PMU); + /* Wait for 1ms for better cnp */ + usleep_range(1000, 1100); + tasha_cdc_mclk_enable(codec, false, true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = __tasha_codec_enable_micbias(w, SND_SOC_DAPM_POST_PMD); + wcd_resmgr_disable_master_bias(tasha->resmgr); + break; + } + + return ret; +} + +static int tasha_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return __tasha_codec_enable_micbias(w, event); +} + +static int tasha_codec_enable_standalone_ldo_h(struct snd_soc_codec *codec, + bool enable) +{ + int rc; + + if (enable) + rc = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + DAPM_LDO_H_STANDALONE); + else + rc = snd_soc_dapm_disable_pin( + snd_soc_codec_get_dapm(codec), + DAPM_LDO_H_STANDALONE); + + if (!rc) + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + else + dev_err(codec->dev, "%s: ldo_h force %s pin failed\n", + __func__, (enable ? "enable" : "disable")); + + return rc; +} + +/* + * tasha_codec_enable_standalone_micbias - enable micbias standalone + * @codec: pointer to codec instance + * @micb_num: number of micbias to be enabled + * @enable: true to enable micbias or false to disable + * + * This function is used to enable micbias (1, 2, 3 or 4) during + * standalone independent of whether TX use-case is running or not + * + * Return: error code in case of failure or 0 for success + */ +int tasha_codec_enable_standalone_micbias(struct snd_soc_codec *codec, + int micb_num, + bool enable) +{ + const char * const micb_names[] = { + DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE, + DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE + }; + int micb_index = micb_num - 1; + int rc; + + if (!codec) { + pr_err("%s: Codec memory is NULL\n", __func__); + return -EINVAL; + } + + if ((micb_index < 0) || (micb_index > TASHA_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + + if (enable) + rc = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + else + rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + + if (!rc) + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + else + dev_err(codec->dev, "%s: micbias%d force %s pin failed\n", + __func__, micb_num, (enable ? "enable" : "disable")); + + return rc; +} +EXPORT_SYMBOL(tasha_codec_enable_standalone_micbias); + +static const char *const tasha_anc_func_text[] = {"OFF", "ON"}; +static const struct soc_enum tasha_anc_func_enum = + SOC_ENUM_SINGLE_EXT(2, tasha_anc_func_text); + +static const char *const tasha_clkmode_text[] = {"EXTERNAL", "INTERNAL"}; +static SOC_ENUM_SINGLE_EXT_DECL(tasha_clkmode_enum, tasha_clkmode_text); + +/* 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 struct soc_enum cf_dec0_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX0_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX1_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec2_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX2_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec3_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX3_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec4_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX4_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec5_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX5_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec6_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX6_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec7_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX7_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec8_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX8_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_int0_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int1_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int2_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int3_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int4_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int5_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int5_2_enum, WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int6_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int6_2_enum, WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int7_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int8_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct snd_soc_dapm_route audio_i2s_map[] = { + {"SLIM RX0 MUX", NULL, "RX_I2S_CTL"}, + {"SLIM RX1 MUX", NULL, "RX_I2S_CTL"}, + {"SLIM RX2 MUX", NULL, "RX_I2S_CTL"}, + {"SLIM RX3 MUX", NULL, "RX_I2S_CTL"}, + + {"SLIM TX6 MUX", NULL, "TX_I2S_CTL"}, + {"SLIM TX7 MUX", NULL, "TX_I2S_CTL"}, + {"SLIM TX8 MUX", NULL, "TX_I2S_CTL"}, + {"SLIM TX11 MUX", NULL, "TX_I2S_CTL"}, +}; + +static const struct snd_soc_dapm_route audio_map[] = { + + /* MAD */ + {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"}, + {"MAD_SEL MUX", "MSM", "MADINPUT"}, + {"MADONOFF", "Switch", "MAD_SEL MUX"}, + {"MAD_BROADCAST", "Switch", "MAD_SEL MUX"}, + {"TX13 INP MUX", "CPE_TX_PP", "MADONOFF"}, + + /* CPE HW MAD bypass */ + {"CPE IN Mixer", "MAD_BYPASS", "SLIM TX1 MUX"}, + + {"AIF4_MAD Mixer", "SLIM TX1", "CPE IN Mixer"}, + {"AIF4_MAD Mixer", "SLIM TX12", "MADONOFF"}, + {"AIF4_MAD Mixer", "SLIM TX13", "TX13 INP MUX"}, + {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, + {"AIF4 MAD", NULL, "AIF4"}, + + {"EC BUF MUX INP", "DEC1", "ADC MUX1"}, + {"AIF5 CPE", NULL, "EC BUF MUX INP"}, + + /* SLIMBUS Connections */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + + /* VI Feedback */ + {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"}, + {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"}, + {"AIF4 VI", NULL, "AIF4_VI Mixer"}, + + /* SLIM_MIXER("AIF1_CAP Mixer"),*/ + {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX13", "TX13 INP MUX"}, + /* SLIM_MIXER("AIF2_CAP Mixer"),*/ + {"AIF2_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX13", "TX13 INP MUX"}, + /* SLIM_MIXER("AIF3_CAP Mixer"),*/ + {"AIF3_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX13", "TX13 INP MUX"}, + + {"SLIM TX0 MUX", "DEC0", "ADC MUX0"}, + {"SLIM TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, + {"SLIM TX0 MUX", "DEC0_192", "ADC US MUX0"}, + + {"SLIM TX1 MUX", "DEC1", "ADC MUX1"}, + {"SLIM TX1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"}, + {"SLIM TX1 MUX", "DEC1_192", "ADC US MUX1"}, + + {"SLIM TX2 MUX", "DEC2", "ADC MUX2"}, + {"SLIM TX2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"}, + {"SLIM TX2 MUX", "DEC2_192", "ADC US MUX2"}, + + {"SLIM TX3 MUX", "DEC3", "ADC MUX3"}, + {"SLIM TX3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"}, + {"SLIM TX3 MUX", "DEC3_192", "ADC US MUX3"}, + + {"SLIM TX4 MUX", "DEC4", "ADC MUX4"}, + {"SLIM TX4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"}, + {"SLIM TX4 MUX", "DEC4_192", "ADC US MUX4"}, + + {"SLIM TX5 MUX", "DEC5", "ADC MUX5"}, + {"SLIM TX5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + {"SLIM TX5 MUX", "DEC5_192", "ADC US MUX5"}, + + {"SLIM TX6 MUX", "DEC6", "ADC MUX6"}, + {"SLIM TX6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"}, + {"SLIM TX6 MUX", "DEC6_192", "ADC US MUX6"}, + + {"SLIM TX7 MUX", "DEC7", "ADC MUX7"}, + {"SLIM TX7 MUX", "RX_MIX_TX7", "RX MIX TX7 MUX"}, + {"SLIM TX7 MUX", "DEC7_192", "ADC US MUX7"}, + + {"SLIM TX8 MUX", "DEC8", "ADC MUX8"}, + {"SLIM TX8 MUX", "RX_MIX_TX8", "RX MIX TX8 MUX"}, + {"SLIM TX8 MUX", "DEC8_192", "ADC US MUX8"}, + + {"SLIM TX9 MUX", "DEC7", "ADC MUX7"}, + {"SLIM TX9 MUX", "DEC7_192", "ADC US MUX7"}, + {"SLIM TX10 MUX", "DEC6", "ADC MUX6"}, + {"SLIM TX10 MUX", "DEC6_192", "ADC US MUX6"}, + + {"SLIM TX11 MUX", "DEC_0_5", "SLIM TX11 INP1 MUX"}, + {"SLIM TX11 MUX", "DEC_9_12", "SLIM TX11 INP1 MUX"}, + {"SLIM TX11 INP1 MUX", "DEC0", "ADC MUX0"}, + {"SLIM TX11 INP1 MUX", "DEC1", "ADC MUX1"}, + {"SLIM TX11 INP1 MUX", "DEC2", "ADC MUX2"}, + {"SLIM TX11 INP1 MUX", "DEC3", "ADC MUX3"}, + {"SLIM TX11 INP1 MUX", "DEC4", "ADC MUX4"}, + {"SLIM TX11 INP1 MUX", "DEC5", "ADC MUX5"}, + {"SLIM TX11 INP1 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + + {"TX13 INP MUX", "MAD_BRDCST", "MAD_BROADCAST"}, + {"TX13 INP MUX", "CDC_DEC_5", "SLIM TX13 MUX"}, + {"SLIM TX13 MUX", "DEC5", "ADC MUX5"}, + + {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX0 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX0 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX0 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX1 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX1 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX1 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX2 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX2 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX2 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX3 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX3 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX3 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX3 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX4 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX4 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX4 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX4 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX5 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX5 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX5 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX5 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX6 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX6 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX6 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX6 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX7 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX7 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX7 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX7 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX8 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX8 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX8 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX8 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"ADC US MUX0", "US_Switch", "ADC MUX0"}, + {"ADC US MUX1", "US_Switch", "ADC MUX1"}, + {"ADC US MUX2", "US_Switch", "ADC MUX2"}, + {"ADC US MUX3", "US_Switch", "ADC MUX3"}, + {"ADC US MUX4", "US_Switch", "ADC MUX4"}, + {"ADC US MUX5", "US_Switch", "ADC MUX5"}, + {"ADC US MUX6", "US_Switch", "ADC MUX6"}, + {"ADC US MUX7", "US_Switch", "ADC MUX7"}, + {"ADC US MUX8", "US_Switch", "ADC MUX8"}, + {"ADC MUX0", "DMIC", "DMIC MUX0"}, + {"ADC MUX0", "AMIC", "AMIC MUX0"}, + {"ADC MUX1", "DMIC", "DMIC MUX1"}, + {"ADC MUX1", "AMIC", "AMIC MUX1"}, + {"ADC MUX2", "DMIC", "DMIC MUX2"}, + {"ADC MUX2", "AMIC", "AMIC MUX2"}, + {"ADC MUX3", "DMIC", "DMIC MUX3"}, + {"ADC MUX3", "AMIC", "AMIC MUX3"}, + {"ADC MUX4", "DMIC", "DMIC MUX4"}, + {"ADC MUX4", "AMIC", "AMIC MUX4"}, + {"ADC MUX5", "DMIC", "DMIC MUX5"}, + {"ADC MUX5", "AMIC", "AMIC MUX5"}, + {"ADC MUX6", "DMIC", "DMIC MUX6"}, + {"ADC MUX6", "AMIC", "AMIC MUX6"}, + {"ADC MUX7", "DMIC", "DMIC MUX7"}, + {"ADC MUX7", "AMIC", "AMIC MUX7"}, + {"ADC MUX8", "DMIC", "DMIC MUX8"}, + {"ADC MUX8", "AMIC", "AMIC MUX8"}, + {"ADC MUX10", "DMIC", "DMIC MUX10"}, + {"ADC MUX10", "AMIC", "AMIC MUX10"}, + {"ADC MUX11", "DMIC", "DMIC MUX11"}, + {"ADC MUX11", "AMIC", "AMIC MUX11"}, + {"ADC MUX12", "DMIC", "DMIC MUX12"}, + {"ADC MUX12", "AMIC", "AMIC MUX12"}, + {"ADC MUX13", "DMIC", "DMIC MUX13"}, + {"ADC MUX13", "AMIC", "AMIC MUX13"}, + + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX13"}, + + {"DMIC MUX0", "DMIC0", "DMIC0"}, + {"DMIC MUX0", "DMIC1", "DMIC1"}, + {"DMIC MUX0", "DMIC2", "DMIC2"}, + {"DMIC MUX0", "DMIC3", "DMIC3"}, + {"DMIC MUX0", "DMIC4", "DMIC4"}, + {"DMIC MUX0", "DMIC5", "DMIC5"}, + {"AMIC MUX0", "ADC1", "ADC1"}, + {"AMIC MUX0", "ADC2", "ADC2"}, + {"AMIC MUX0", "ADC3", "ADC3"}, + {"AMIC MUX0", "ADC4", "ADC4"}, + {"AMIC MUX0", "ADC5", "ADC5"}, + {"AMIC MUX0", "ADC6", "ADC6"}, + + {"DMIC MUX1", "DMIC0", "DMIC0"}, + {"DMIC MUX1", "DMIC1", "DMIC1"}, + {"DMIC MUX1", "DMIC2", "DMIC2"}, + {"DMIC MUX1", "DMIC3", "DMIC3"}, + {"DMIC MUX1", "DMIC4", "DMIC4"}, + {"DMIC MUX1", "DMIC5", "DMIC5"}, + {"AMIC MUX1", "ADC1", "ADC1"}, + {"AMIC MUX1", "ADC2", "ADC2"}, + {"AMIC MUX1", "ADC3", "ADC3"}, + {"AMIC MUX1", "ADC4", "ADC4"}, + {"AMIC MUX1", "ADC5", "ADC5"}, + {"AMIC MUX1", "ADC6", "ADC6"}, + + {"DMIC MUX2", "DMIC0", "DMIC0"}, + {"DMIC MUX2", "DMIC1", "DMIC1"}, + {"DMIC MUX2", "DMIC2", "DMIC2"}, + {"DMIC MUX2", "DMIC3", "DMIC3"}, + {"DMIC MUX2", "DMIC4", "DMIC4"}, + {"DMIC MUX2", "DMIC5", "DMIC5"}, + {"AMIC MUX2", "ADC1", "ADC1"}, + {"AMIC MUX2", "ADC2", "ADC2"}, + {"AMIC MUX2", "ADC3", "ADC3"}, + {"AMIC MUX2", "ADC4", "ADC4"}, + {"AMIC MUX2", "ADC5", "ADC5"}, + {"AMIC MUX2", "ADC6", "ADC6"}, + + {"DMIC MUX3", "DMIC0", "DMIC0"}, + {"DMIC MUX3", "DMIC1", "DMIC1"}, + {"DMIC MUX3", "DMIC2", "DMIC2"}, + {"DMIC MUX3", "DMIC3", "DMIC3"}, + {"DMIC MUX3", "DMIC4", "DMIC4"}, + {"DMIC MUX3", "DMIC5", "DMIC5"}, + {"AMIC MUX3", "ADC1", "ADC1"}, + {"AMIC MUX3", "ADC2", "ADC2"}, + {"AMIC MUX3", "ADC3", "ADC3"}, + {"AMIC MUX3", "ADC4", "ADC4"}, + {"AMIC MUX3", "ADC5", "ADC5"}, + {"AMIC MUX3", "ADC6", "ADC6"}, + + {"DMIC MUX4", "DMIC0", "DMIC0"}, + {"DMIC MUX4", "DMIC1", "DMIC1"}, + {"DMIC MUX4", "DMIC2", "DMIC2"}, + {"DMIC MUX4", "DMIC3", "DMIC3"}, + {"DMIC MUX4", "DMIC4", "DMIC4"}, + {"DMIC MUX4", "DMIC5", "DMIC5"}, + {"AMIC MUX4", "ADC1", "ADC1"}, + {"AMIC MUX4", "ADC2", "ADC2"}, + {"AMIC MUX4", "ADC3", "ADC3"}, + {"AMIC MUX4", "ADC4", "ADC4"}, + {"AMIC MUX4", "ADC5", "ADC5"}, + {"AMIC MUX4", "ADC6", "ADC6"}, + + {"DMIC MUX5", "DMIC0", "DMIC0"}, + {"DMIC MUX5", "DMIC1", "DMIC1"}, + {"DMIC MUX5", "DMIC2", "DMIC2"}, + {"DMIC MUX5", "DMIC3", "DMIC3"}, + {"DMIC MUX5", "DMIC4", "DMIC4"}, + {"DMIC MUX5", "DMIC5", "DMIC5"}, + {"AMIC MUX5", "ADC1", "ADC1"}, + {"AMIC MUX5", "ADC2", "ADC2"}, + {"AMIC MUX5", "ADC3", "ADC3"}, + {"AMIC MUX5", "ADC4", "ADC4"}, + {"AMIC MUX5", "ADC5", "ADC5"}, + {"AMIC MUX5", "ADC6", "ADC6"}, + + {"DMIC MUX6", "DMIC0", "DMIC0"}, + {"DMIC MUX6", "DMIC1", "DMIC1"}, + {"DMIC MUX6", "DMIC2", "DMIC2"}, + {"DMIC MUX6", "DMIC3", "DMIC3"}, + {"DMIC MUX6", "DMIC4", "DMIC4"}, + {"DMIC MUX6", "DMIC5", "DMIC5"}, + {"AMIC MUX6", "ADC1", "ADC1"}, + {"AMIC MUX6", "ADC2", "ADC2"}, + {"AMIC MUX6", "ADC3", "ADC3"}, + {"AMIC MUX6", "ADC4", "ADC4"}, + {"AMIC MUX6", "ADC5", "ADC5"}, + {"AMIC MUX6", "ADC6", "ADC6"}, + + {"DMIC MUX7", "DMIC0", "DMIC0"}, + {"DMIC MUX7", "DMIC1", "DMIC1"}, + {"DMIC MUX7", "DMIC2", "DMIC2"}, + {"DMIC MUX7", "DMIC3", "DMIC3"}, + {"DMIC MUX7", "DMIC4", "DMIC4"}, + {"DMIC MUX7", "DMIC5", "DMIC5"}, + {"AMIC MUX7", "ADC1", "ADC1"}, + {"AMIC MUX7", "ADC2", "ADC2"}, + {"AMIC MUX7", "ADC3", "ADC3"}, + {"AMIC MUX7", "ADC4", "ADC4"}, + {"AMIC MUX7", "ADC5", "ADC5"}, + {"AMIC MUX7", "ADC6", "ADC6"}, + + {"DMIC MUX8", "DMIC0", "DMIC0"}, + {"DMIC MUX8", "DMIC1", "DMIC1"}, + {"DMIC MUX8", "DMIC2", "DMIC2"}, + {"DMIC MUX8", "DMIC3", "DMIC3"}, + {"DMIC MUX8", "DMIC4", "DMIC4"}, + {"DMIC MUX8", "DMIC5", "DMIC5"}, + {"AMIC MUX8", "ADC1", "ADC1"}, + {"AMIC MUX8", "ADC2", "ADC2"}, + {"AMIC MUX8", "ADC3", "ADC3"}, + {"AMIC MUX8", "ADC4", "ADC4"}, + {"AMIC MUX8", "ADC5", "ADC5"}, + {"AMIC MUX8", "ADC6", "ADC6"}, + + {"DMIC MUX10", "DMIC0", "DMIC0"}, + {"DMIC MUX10", "DMIC1", "DMIC1"}, + {"DMIC MUX10", "DMIC2", "DMIC2"}, + {"DMIC MUX10", "DMIC3", "DMIC3"}, + {"DMIC MUX10", "DMIC4", "DMIC4"}, + {"DMIC MUX10", "DMIC5", "DMIC5"}, + {"AMIC MUX10", "ADC1", "ADC1"}, + {"AMIC MUX10", "ADC2", "ADC2"}, + {"AMIC MUX10", "ADC3", "ADC3"}, + {"AMIC MUX10", "ADC4", "ADC4"}, + {"AMIC MUX10", "ADC5", "ADC5"}, + {"AMIC MUX10", "ADC6", "ADC6"}, + + {"DMIC MUX11", "DMIC0", "DMIC0"}, + {"DMIC MUX11", "DMIC1", "DMIC1"}, + {"DMIC MUX11", "DMIC2", "DMIC2"}, + {"DMIC MUX11", "DMIC3", "DMIC3"}, + {"DMIC MUX11", "DMIC4", "DMIC4"}, + {"DMIC MUX11", "DMIC5", "DMIC5"}, + {"AMIC MUX11", "ADC1", "ADC1"}, + {"AMIC MUX11", "ADC2", "ADC2"}, + {"AMIC MUX11", "ADC3", "ADC3"}, + {"AMIC MUX11", "ADC4", "ADC4"}, + {"AMIC MUX11", "ADC5", "ADC5"}, + {"AMIC MUX11", "ADC6", "ADC6"}, + + {"DMIC MUX12", "DMIC0", "DMIC0"}, + {"DMIC MUX12", "DMIC1", "DMIC1"}, + {"DMIC MUX12", "DMIC2", "DMIC2"}, + {"DMIC MUX12", "DMIC3", "DMIC3"}, + {"DMIC MUX12", "DMIC4", "DMIC4"}, + {"DMIC MUX12", "DMIC5", "DMIC5"}, + {"AMIC MUX12", "ADC1", "ADC1"}, + {"AMIC MUX12", "ADC2", "ADC2"}, + {"AMIC MUX12", "ADC3", "ADC3"}, + {"AMIC MUX12", "ADC4", "ADC4"}, + {"AMIC MUX12", "ADC5", "ADC5"}, + {"AMIC MUX12", "ADC6", "ADC6"}, + + {"DMIC MUX13", "DMIC0", "DMIC0"}, + {"DMIC MUX13", "DMIC1", "DMIC1"}, + {"DMIC MUX13", "DMIC2", "DMIC2"}, + {"DMIC MUX13", "DMIC3", "DMIC3"}, + {"DMIC MUX13", "DMIC4", "DMIC4"}, + {"DMIC MUX13", "DMIC5", "DMIC5"}, + {"AMIC MUX13", "ADC1", "ADC1"}, + {"AMIC MUX13", "ADC2", "ADC2"}, + {"AMIC MUX13", "ADC3", "ADC3"}, + {"AMIC MUX13", "ADC4", "ADC4"}, + {"AMIC MUX13", "ADC5", "ADC5"}, + {"AMIC MUX13", "ADC6", "ADC6"}, + /* ADC Connections */ + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "AMIC2"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "AMIC4"}, + {"ADC5", NULL, "AMIC5"}, + {"ADC6", NULL, "AMIC6"}, + + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP0"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP1"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP2"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP0"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP1"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP2"}, + {"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP0"}, + {"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP1"}, + {"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP2"}, + {"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP0"}, + {"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP1"}, + {"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP2"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP0"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP1"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP2"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP0"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP1"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP2"}, + + {"RX INT0 SEC MIX", NULL, "RX INT0_1 MIX1"}, + {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"}, + {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"}, + {"RX INT0 INTERP", NULL, "RX INT0 MIX2"}, + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 INTERP"}, + {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"}, + {"RX INT0 DAC", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "RX INT0 DAC"}, + {"EAR", NULL, "EAR PA"}, + + {"SPL SRC0 MUX", "SRC_IN_HPHL", "RX INT1_1 MIX1"}, + {"RX INT1 SPLINE MIX", NULL, "RX INT1_1 MIX1"}, + {"RX INT1 SPLINE MIX", "HPHL Switch", "SPL SRC0 MUX"}, + {"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"}, + {"RX INT1 SPLINE MIX", NULL, "RX INT1_1 NATIVE MUX"}, + {"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"}, + {"RX INT1 SEC MIX", NULL, "RX INT1 SPLINE MIX"}, + {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"}, + {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"}, + {"RX INT1 INTERP", NULL, "RX INT1 MIX2"}, + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 INTERP"}, + {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"}, + {"RX INT1 DAC", NULL, "RX_BIAS"}, + {"HPHL PA", NULL, "RX INT1 DAC"}, + {"HPHL", NULL, "HPHL PA"}, + + {"SPL SRC1 MUX", "SRC_IN_HPHR", "RX INT2_1 MIX1"}, + {"RX INT2 SPLINE MIX", NULL, "RX INT2_1 MIX1"}, + {"RX INT2 SPLINE MIX", "HPHR Switch", "SPL SRC1 MUX"}, + {"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"}, + {"RX INT2 SPLINE MIX", NULL, "RX INT2_1 NATIVE MUX"}, + {"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"}, + {"RX INT2 SEC MIX", NULL, "RX INT2 SPLINE MIX"}, + {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"}, + {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"}, + {"RX INT2 INTERP", NULL, "RX INT2 MIX2"}, + {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 INTERP"}, + {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"}, + {"RX INT2 DAC", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "RX INT2 DAC"}, + {"HPHR", NULL, "HPHR PA"}, + + {"SPL SRC0 MUX", "SRC_IN_LO1", "RX INT3_1 MIX1"}, + {"RX INT3 SPLINE MIX", NULL, "RX INT3_1 MIX1"}, + {"RX INT3 SPLINE MIX", "LO1 Switch", "SPL SRC0 MUX"}, + {"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"}, + {"RX INT3 SPLINE MIX", NULL, "RX INT3_1 NATIVE MUX"}, + {"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"}, + {"RX INT3 SEC MIX", NULL, "RX INT3 SPLINE MIX"}, + {"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"}, + {"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"}, + {"RX INT3 INTERP", NULL, "RX INT3 MIX2"}, + {"RX INT3 DAC", NULL, "RX INT3 INTERP"}, + {"RX INT3 DAC", NULL, "RX_BIAS"}, + {"LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"LINEOUT1", NULL, "LINEOUT1 PA"}, + + {"SPL SRC1 MUX", "SRC_IN_LO2", "RX INT4_1 MIX1"}, + {"RX INT4 SPLINE MIX", NULL, "RX INT4_1 MIX1"}, + {"RX INT4 SPLINE MIX", "LO2 Switch", "SPL SRC1 MUX"}, + {"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"}, + {"RX INT4 SPLINE MIX", NULL, "RX INT4_1 NATIVE MUX"}, + {"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"}, + {"RX INT4 SEC MIX", NULL, "RX INT4 SPLINE MIX"}, + {"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"}, + {"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"}, + {"RX INT4 INTERP", NULL, "RX INT4 MIX2"}, + {"RX INT4 DAC", NULL, "RX INT4 INTERP"}, + {"RX INT4 DAC", NULL, "RX_BIAS"}, + {"LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"LINEOUT2", NULL, "LINEOUT2 PA"}, + + {"SPL SRC2 MUX", "SRC_IN_LO3", "RX INT5_1 MIX1"}, + {"RX INT5 SPLINE MIX", NULL, "RX INT5_1 MIX1"}, + {"RX INT5 SPLINE MIX", "LO3 Switch", "SPL SRC2 MUX"}, + {"RX INT5 SEC MIX", NULL, "RX INT5 SPLINE MIX"}, + {"RX INT5 MIX2", NULL, "RX INT5 SEC MIX"}, + {"RX INT5 INTERP", NULL, "RX INT5 MIX2"}, + + {"RX INT5 VBAT", "LO3 VBAT Enable", "RX INT5 INTERP"}, + {"RX INT5 DAC", NULL, "RX INT5 VBAT"}, + + {"RX INT5 DAC", NULL, "RX INT5 INTERP"}, + {"RX INT5 DAC", NULL, "RX_BIAS"}, + {"LINEOUT3 PA", NULL, "RX INT5 DAC"}, + {"LINEOUT3", NULL, "LINEOUT3 PA"}, + + {"SPL SRC3 MUX", "SRC_IN_LO4", "RX INT6_1 MIX1"}, + {"RX INT6 SPLINE MIX", NULL, "RX INT6_1 MIX1"}, + {"RX INT6 SPLINE MIX", "LO4 Switch", "SPL SRC3 MUX"}, + {"RX INT6 SEC MIX", NULL, "RX INT6 SPLINE MIX"}, + {"RX INT6 MIX2", NULL, "RX INT6 SEC MIX"}, + {"RX INT6 INTERP", NULL, "RX INT6 MIX2"}, + + {"RX INT6 VBAT", "LO4 VBAT Enable", "RX INT6 INTERP"}, + {"RX INT6 DAC", NULL, "RX INT6 VBAT"}, + + {"RX INT6 DAC", NULL, "RX INT6 INTERP"}, + {"RX INT6 DAC", NULL, "RX_BIAS"}, + {"LINEOUT4 PA", NULL, "RX INT6 DAC"}, + {"LINEOUT4", NULL, "LINEOUT4 PA"}, + + {"SPL SRC2 MUX", "SRC_IN_SPKRL", "RX INT7_1 MIX1"}, + {"RX INT7 SPLINE MIX", NULL, "RX INT7_1 MIX1"}, + {"RX INT7 SPLINE MIX", "SPKRL Switch", "SPL SRC2 MUX"}, + {"RX INT7 SEC MIX", NULL, "RX INT7 SPLINE MIX"}, + {"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"}, + {"RX INT7 MIX2", NULL, "RX INT7 MIX2 INP"}, + + {"RX INT7 INTERP", NULL, "RX INT7 MIX2"}, + + {"RX INT7 VBAT", "SPKRL VBAT Enable", "RX INT7 INTERP"}, + {"RX INT7 CHAIN", NULL, "RX INT7 VBAT"}, + + {"RX INT7 CHAIN", NULL, "RX INT7 INTERP"}, + {"RX INT7 CHAIN", NULL, "RX_BIAS"}, + {"SPK1 OUT", NULL, "RX INT7 CHAIN"}, + + {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"}, + {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"}, + {"SPK1 OUT", NULL, "ANC SPK1 PA"}, + + {"SPL SRC3 MUX", "SRC_IN_SPKRR", "RX INT8_1 MIX1"}, + {"RX INT8 SPLINE MIX", NULL, "RX INT8_1 MIX1"}, + {"RX INT8 SPLINE MIX", "SPKRR Switch", "SPL SRC3 MUX"}, + {"RX INT8 SEC MIX", NULL, "RX INT8 SPLINE MIX"}, + {"RX INT8 INTERP", NULL, "RX INT8 SEC MIX"}, + + {"RX INT8 VBAT", "SPKRR VBAT Enable", "RX INT8 INTERP"}, + {"RX INT8 CHAIN", NULL, "RX INT8 VBAT"}, + + {"RX INT8 CHAIN", NULL, "RX INT8 INTERP"}, + {"RX INT8 CHAIN", NULL, "RX_BIAS"}, + {"SPK2 OUT", NULL, "RX INT8 CHAIN"}, + + {"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_HPHL", "RX INT1 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_LO1", "RX INT3 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_HPHR", "RX INT2 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_LO2", "RX INT4 MIX2"}, + + {"ANC HPHL Enable", "Switch", "ADC MUX10"}, + {"ANC HPHL Enable", "Switch", "ADC MUX11"}, + {"RX INT1 MIX2", NULL, "ANC HPHL Enable"}, + + {"ANC HPHR Enable", "Switch", "ADC MUX12"}, + {"ANC HPHR Enable", "Switch", "ADC MUX13"}, + {"RX INT2 MIX2", NULL, "ANC HPHR Enable"}, + + {"ANC EAR Enable", "Switch", "ADC MUX10"}, + {"ANC EAR Enable", "Switch", "ADC MUX11"}, + {"RX INT0 MIX2", NULL, "ANC EAR Enable"}, + + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"}, + {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"}, + + {"ANC LINEOUT1 Enable", "Switch", "ADC MUX10"}, + {"ANC LINEOUT1 Enable", "Switch", "ADC MUX11"}, + {"RX INT3 MIX2", NULL, "ANC LINEOUT1 Enable"}, + + {"ANC LINEOUT2 Enable", "Switch", "ADC MUX12"}, + {"ANC LINEOUT2 Enable", "Switch", "ADC MUX13"}, + {"RX INT4 MIX2", NULL, "ANC LINEOUT2 Enable"}, + + {"ANC EAR PA", NULL, "RX INT0 DAC"}, + {"ANC EAR", NULL, "ANC EAR PA"}, + {"ANC HPHL PA", NULL, "RX INT1 DAC"}, + {"ANC HPHL", NULL, "ANC HPHL PA"}, + {"ANC HPHR PA", NULL, "RX INT2 DAC"}, + {"ANC HPHR", NULL, "ANC HPHR PA"}, + {"ANC LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"ANC LINEOUT1", NULL, "ANC LINEOUT1 PA"}, + {"ANC LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"ANC LINEOUT2", NULL, "ANC LINEOUT2 PA"}, + + /* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/ + {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"}, + /* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/ + {"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"}, + /* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/ + {"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"}, + /* SLIM_MUX("AIF4_PB", "AIF4 PB"),*/ + {"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"}, + + /* SLIM_MUX("AIF_MIX1_PB", "AIF MIX1 PB"),*/ + {"SLIM RX0 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX1 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX2 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX3 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX4 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX5 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX6 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX7 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + + {"SLIM RX0", NULL, "SLIM RX0 MUX"}, + {"SLIM RX1", NULL, "SLIM RX1 MUX"}, + {"SLIM RX2", NULL, "SLIM RX2 MUX"}, + {"SLIM RX3", NULL, "SLIM RX3 MUX"}, + {"SLIM RX4", NULL, "SLIM RX4 MUX"}, + {"SLIM RX5", NULL, "SLIM RX5 MUX"}, + {"SLIM RX6", NULL, "SLIM RX6 MUX"}, + {"SLIM RX7", NULL, "SLIM RX7 MUX"}, + + {"RX INT0_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT0_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT0_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT0_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT0_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT0_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT0_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT0_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT0_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT0_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT0_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT0_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT0_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT0_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT0_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT0_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT0_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT0_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT0_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT0_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT0_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT0_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"}, + + /* MIXing path INT0 */ + {"RX INT0_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT0_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT0_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT0_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT0_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT0_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT0_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT0_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_2 MUX"}, + + /* MIXing path INT1 */ + {"RX INT1_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT1_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT1_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT1_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT1_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT1_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT1_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT1_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_2 MUX"}, + + /* MIXing path INT2 */ + {"RX INT2_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT2_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT2_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT2_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT2_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT2_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT2_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT2_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_2 MUX"}, + + /* MIXing path INT3 */ + {"RX INT3_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT3_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT3_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT3_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT3_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT3_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT3_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT3_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT3 SEC MIX", NULL, "RX INT3_2 MUX"}, + + /* MIXing path INT4 */ + {"RX INT4_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT4_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT4_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT4_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT4_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT4_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT4_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT4_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_2 MUX"}, + + /* MIXing path INT5 */ + {"RX INT5_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT5_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT5_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT5_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT5_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT5_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT5_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT5_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT5 SEC MIX", NULL, "RX INT5_2 MUX"}, + + /* MIXing path INT6 */ + {"RX INT6_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT6_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT6_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT6_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT6_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT6_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT6_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT6_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT6 SEC MIX", NULL, "RX INT6_2 MUX"}, + + /* MIXing path INT7 */ + {"RX INT7_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT7_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT7_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT7_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT7_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT7_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT7_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT7_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_2 MUX"}, + + /* MIXing path INT8 */ + {"RX INT8_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT8_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT8_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT8_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT8_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT8_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT8_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT8_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_2 MUX"}, + + {"RX INT1_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT1_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT1_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT1_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT1_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT1_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT1_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT1_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT1_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT1_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT1_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT1_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT1_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT1_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT1_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT1_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT1_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT1_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT1_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT1_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT1_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT1_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT2_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT2_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT2_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT2_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT2_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT2_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT2_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT2_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT2_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT2_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT2_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT2_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT2_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT2_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT2_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT2_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT2_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT2_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT2_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT2_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT2_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT3_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT3_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT3_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT3_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT3_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT3_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT3_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT3_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT3_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT3_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT3_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT3_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT3_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT3_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT3_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT3_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT3_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT3_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT3_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT3_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT3_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT3_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT3_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT3_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT3_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT3_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT3_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT4_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT4_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT4_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT4_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT4_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT4_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT4_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT4_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT4_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT4_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT4_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT4_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT4_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT4_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT4_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT4_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT4_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT4_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT4_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT4_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT4_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT4_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT4_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT4_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT4_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT4_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT4_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT5_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT5_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT5_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT5_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT5_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT5_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT5_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT5_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT5_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT5_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT5_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT5_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT5_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT5_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT5_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT5_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT5_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT5_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT5_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT5_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT5_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT5_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT5_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT5_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT5_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT5_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT5_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT5_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT5_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT5_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT6_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT6_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT6_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT6_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT6_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT6_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT6_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT6_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT6_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT6_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT6_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT6_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT6_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT6_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT6_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT6_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT6_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT6_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT6_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT6_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT6_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT6_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT6_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT6_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT6_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT6_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT6_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT6_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT6_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT6_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT7_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT7_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT7_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT7_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT7_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT7_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT7_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT7_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT7_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT7_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT7_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT7_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT7_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT7_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT7_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT7_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT7_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT7_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT7_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT7_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT7_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT7_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT7_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT7_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT7_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT7_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT7_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT8_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT8_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT8_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT8_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT8_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT8_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT8_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT8_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT8_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT8_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT8_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT8_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT8_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT8_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT8_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT8_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT8_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT8_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT8_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT8_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT8_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT8_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT8_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT8_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT8_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT8_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT8_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP2", "IIR1", "IIR1"}, + + /* SRC0, SRC1 inputs to Sidetone RX Mixer + * on RX0, RX1, RX2, RX3, RX4 and RX7 chains + */ + {"IIR0", NULL, "IIR0 INP0 MUX"}, + {"IIR0 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP0 MUX", "RX0", "SLIM RX0"}, + {"IIR0 INP0 MUX", "RX1", "SLIM RX1"}, + {"IIR0 INP0 MUX", "RX2", "SLIM RX2"}, + {"IIR0 INP0 MUX", "RX3", "SLIM RX3"}, + {"IIR0 INP0 MUX", "RX4", "SLIM RX4"}, + {"IIR0 INP0 MUX", "RX5", "SLIM RX5"}, + {"IIR0 INP0 MUX", "RX6", "SLIM RX6"}, + {"IIR0 INP0 MUX", "RX7", "SLIM RX7"}, + {"IIR0", NULL, "IIR0 INP1 MUX"}, + {"IIR0 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP1 MUX", "RX0", "SLIM RX0"}, + {"IIR0 INP1 MUX", "RX1", "SLIM RX1"}, + {"IIR0 INP1 MUX", "RX2", "SLIM RX2"}, + {"IIR0 INP1 MUX", "RX3", "SLIM RX3"}, + {"IIR0 INP1 MUX", "RX4", "SLIM RX4"}, + {"IIR0 INP1 MUX", "RX5", "SLIM RX5"}, + {"IIR0 INP1 MUX", "RX6", "SLIM RX6"}, + {"IIR0 INP1 MUX", "RX7", "SLIM RX7"}, + {"IIR0", NULL, "IIR0 INP2 MUX"}, + {"IIR0 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP2 MUX", "RX0", "SLIM RX0"}, + {"IIR0 INP2 MUX", "RX1", "SLIM RX1"}, + {"IIR0 INP2 MUX", "RX2", "SLIM RX2"}, + {"IIR0 INP2 MUX", "RX3", "SLIM RX3"}, + {"IIR0 INP2 MUX", "RX4", "SLIM RX4"}, + {"IIR0 INP2 MUX", "RX5", "SLIM RX5"}, + {"IIR0 INP2 MUX", "RX6", "SLIM RX6"}, + {"IIR0 INP2 MUX", "RX7", "SLIM RX7"}, + {"IIR0", NULL, "IIR0 INP3 MUX"}, + {"IIR0 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP3 MUX", "RX0", "SLIM RX0"}, + {"IIR0 INP3 MUX", "RX1", "SLIM RX1"}, + {"IIR0 INP3 MUX", "RX2", "SLIM RX2"}, + {"IIR0 INP3 MUX", "RX3", "SLIM RX3"}, + {"IIR0 INP3 MUX", "RX4", "SLIM RX4"}, + {"IIR0 INP3 MUX", "RX5", "SLIM RX5"}, + {"IIR0 INP3 MUX", "RX6", "SLIM RX6"}, + {"IIR0 INP3 MUX", "RX7", "SLIM RX7"}, + + {"IIR1", NULL, "IIR1 INP0 MUX"}, + {"IIR1 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP0 MUX", "RX0", "SLIM RX0"}, + {"IIR1 INP0 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP0 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP0 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP0 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP0 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP0 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP0 MUX", "RX7", "SLIM RX7"}, + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP1 MUX", "RX0", "SLIM RX0"}, + {"IIR1 INP1 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP1 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP1 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP1 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP1 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP1 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP1 MUX", "RX7", "SLIM RX7"}, + {"IIR1", NULL, "IIR1 INP2 MUX"}, + {"IIR1 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP2 MUX", "RX0", "SLIM RX0"}, + {"IIR1 INP2 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP2 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP2 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP2 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP2 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP2 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP2 MUX", "RX7", "SLIM RX7"}, + {"IIR1", NULL, "IIR1 INP3 MUX"}, + {"IIR1 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP3 MUX", "RX0", "SLIM RX0"}, + {"IIR1 INP3 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP3 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP3 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP3 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP3 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP3 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP3 MUX", "RX7", "SLIM RX7"}, + + {"SRC0", NULL, "IIR0"}, + {"SRC1", NULL, "IIR1"}, + {"RX INT0 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT0 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT1 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT1 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT2 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT2 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT3 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT3 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT4 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT4 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT7 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT7 MIX2 INP", "SRC1", "SRC1"}, +}; + +static int tasha_amic_pwr_lvl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 amic_reg; + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC3; + if (!strcmp(kcontrol->id.name, "AMIC_5_6 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC5; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, amic_reg) & WCD9335_AMIC_PWR_LVL_MASK) >> + WCD9335_AMIC_PWR_LVL_SHIFT; + + return 0; +} + +static int tasha_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u32 mode_val; + u16 amic_reg; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", + __func__, mode_val); + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC3; + if (!strcmp(kcontrol->id.name, "AMIC_5_6 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC5; + + snd_soc_update_bits(codec, amic_reg, WCD9335_AMIC_PWR_LVL_MASK, + mode_val << WCD9335_AMIC_PWR_LVL_SHIFT); + + return 0; +} + +static int tasha_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha->hph_mode; + return 0; +} + +static int tasha_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", + __func__, mode_val); + + if (mode_val == 0) { + dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H HiFi\n", + __func__); + mode_val = CLS_H_HIFI; + } + tasha->hph_mode = mode_val; + return 0; +} + +static const char *const tasha_conn_mad_text[] = { + "NOTUSED1", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6", + "NOTUSED2", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", + "DMIC5", "NOTUSED3", "NOTUSED4" +}; + +static const struct soc_enum tasha_conn_mad_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_conn_mad_text), + tasha_conn_mad_text); + +static int tasha_enable_ldo_h_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u8 val = 0; + + if (codec) + val = snd_soc_read(codec, WCD9335_LDOH_MODE) & 0x80; + + ucontrol->value.integer.value[0] = !!val; + + return 0; +} + +static int tasha_enable_ldo_h_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int value = ucontrol->value.integer.value[0]; + bool enable; + + enable = !!value; + if (codec) + tasha_codec_enable_standalone_ldo_h(codec, enable); + + return 0; +} + +static int tasha_mad_input_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 tasha_mad_input; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + tasha_mad_input = snd_soc_read(codec, + WCD9335_SOC_MAD_INP_SEL) & 0x0F; + ucontrol->value.integer.value[0] = tasha_mad_input; + + dev_dbg(codec->dev, + "%s: tasha_mad_input = %s\n", __func__, + tasha_conn_mad_text[tasha_mad_input]); + return 0; +} + +static int tasha_mad_input_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 tasha_mad_input; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + char mad_amic_input_widget[6]; + const char *mad_input_widget; + const char *source_widget = NULL; + u32 adc, i, mic_bias_found = 0; + int ret = 0; + char *mad_input; + + tasha_mad_input = ucontrol->value.integer.value[0]; + + if (!strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED1") || + !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED2") || + !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED3") || + !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED4")) { + dev_err(codec->dev, + "%s: Unsupported tasha_mad_input = %s\n", + __func__, tasha_conn_mad_text[tasha_mad_input]); + return -EINVAL; + } + + if (strnstr(tasha_conn_mad_text[tasha_mad_input], + "ADC", sizeof("ADC"))) { + mad_input = strpbrk(tasha_conn_mad_text[tasha_mad_input], + "123456"); + if (!mad_input) { + dev_err(codec->dev, "%s: Invalid MAD input %s\n", + __func__, + tasha_conn_mad_text[tasha_mad_input]); + return -EINVAL; + } + ret = kstrtouint(mad_input, 10, &adc); + if ((ret < 0) || (adc > 6)) { + dev_err(codec->dev, + "%s: Invalid ADC = %s\n", __func__, + tasha_conn_mad_text[tasha_mad_input]); + ret = -EINVAL; + } + + snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc); + + mad_input_widget = mad_amic_input_widget; + } else { + /* DMIC type input widget*/ + mad_input_widget = tasha_conn_mad_text[tasha_mad_input]; + } + + dev_dbg(codec->dev, + "%s: tasha input widget = %s\n", __func__, + mad_input_widget); + + for (i = 0; i < card->num_of_dapm_routes; i++) { + if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) { + source_widget = card->of_dapm_routes[i].source; + if (!source_widget) { + dev_err(codec->dev, + "%s: invalid source widget\n", + __func__); + return -EINVAL; + } + + if (strnstr(source_widget, + "MIC BIAS1", sizeof("MIC BIAS1"))) { + mic_bias_found = 1; + break; + } else if (strnstr(source_widget, + "MIC BIAS2", sizeof("MIC BIAS2"))) { + mic_bias_found = 2; + break; + } else if (strnstr(source_widget, + "MIC BIAS3", sizeof("MIC BIAS3"))) { + mic_bias_found = 3; + break; + } else if (strnstr(source_widget, + "MIC BIAS4", sizeof("MIC BIAS4"))) { + mic_bias_found = 4; + break; + } + } + } + + if (!mic_bias_found) { + dev_err(codec->dev, + "%s: mic bias source not found for input = %s\n", + __func__, mad_input_widget); + return -EINVAL; + } + + dev_dbg(codec->dev, + "%s: mic_bias found = %d\n", __func__, + mic_bias_found); + + snd_soc_update_bits(codec, WCD9335_SOC_MAD_INP_SEL, + 0x0F, tasha_mad_input); + snd_soc_update_bits(codec, WCD9335_ANA_MAD_SETUP, + 0x07, mic_bias_found); + + return 0; +} + +static int tasha_pinctl_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 ctl_reg; + u8 reg_val, pinctl_position; + + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + switch (pinctl_position >> 3) { + case 0: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_0; + break; + case 1: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_1; + break; + case 2: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_2; + break; + case 3: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_3; + break; + default: + dev_err(codec->dev, "%s: Invalid pinctl position = %d\n", + __func__, pinctl_position); + return -EINVAL; + } + + reg_val = snd_soc_read(codec, ctl_reg); + reg_val = (reg_val >> (pinctl_position & 0x07)) & 0x1; + ucontrol->value.integer.value[0] = reg_val; + + return 0; +} + +static int tasha_pinctl_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 ctl_reg, cfg_reg; + u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask; + + /* 1- high or low; 0- high Z */ + pinctl_mode = ucontrol->value.integer.value[0]; + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + switch (pinctl_position >> 3) { + case 0: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_0; + break; + case 1: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_1; + break; + case 2: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_2; + break; + case 3: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_3; + break; + default: + dev_err(codec->dev, "%s: Invalid pinctl position = %d\n", + __func__, pinctl_position); + return -EINVAL; + } + + ctl_val = pinctl_mode << (pinctl_position & 0x07); + mask = 1 << (pinctl_position & 0x07); + snd_soc_update_bits(codec, ctl_reg, mask, ctl_val); + + cfg_reg = WCD9335_TLMM_BIST_MODE_PINCFG + pinctl_position; + if (!pinctl_mode) { + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + cfg_val = 0x4; + else + cfg_val = 0xC; + } else { + cfg_val = 0; + } + snd_soc_update_bits(codec, cfg_reg, 0x07, cfg_val); + + dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n", + __func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val); + + return 0; +} + +static void wcd_vbat_adc_out_config_2_0(struct wcd_vbat *vbat, + struct snd_soc_codec *codec) +{ + u8 val1, val2; + + /* + * Measure dcp1 by using "ALT" branch of band gap + * voltage(Vbg) and use it in FAST mode + */ + snd_soc_update_bits(codec, WCD9335_BIAS_CTL, 0x82, 0x82); + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01, 0x01); + snd_soc_update_bits(codec, WCD9335_ANA_VBADC, 0x80, 0x80); + snd_soc_update_bits(codec, WCD9335_VBADC_SUBBLOCK_EN, 0x20, 0x00); + + snd_soc_update_bits(codec, WCD9335_VBADC_FE_CTRL, 0x20, 0x20); + /* Wait 100 usec after calibration select as Vbg */ + usleep_range(100, 110); + + snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x40); + val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB); + val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB); + snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x00); + + vbat->dcp1 = (((val1 & 0xFF) << 3) | (val2 & 0x07)); + + snd_soc_update_bits(codec, WCD9335_BIAS_CTL, 0x40, 0x40); + /* Wait 100 usec after selecting Vbg as 1.05V */ + usleep_range(100, 110); + + snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x40); + val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB); + val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB); + snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x00); + + vbat->dcp2 = (((val1 & 0xFF) << 3) | (val2 & 0x07)); + + dev_dbg(codec->dev, "%s: dcp1:0x%x, dcp2:0x%x\n", + __func__, vbat->dcp1, vbat->dcp2); + + snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28); + /* Wait 100 usec after selecting Vbg as 0.85V */ + usleep_range(100, 110); + + snd_soc_update_bits(codec, WCD9335_VBADC_FE_CTRL, 0x20, 0x00); + snd_soc_update_bits(codec, WCD9335_VBADC_SUBBLOCK_EN, 0x20, 0x20); + snd_soc_update_bits(codec, WCD9335_ANA_VBADC, 0x80, 0x00); + + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01, 0x00); +} + +static void wcd_vbat_adc_out_config_1_x(struct wcd_vbat *vbat, + struct snd_soc_codec *codec) +{ + u8 val1, val2; + + /* + * Measure dcp1 by applying band gap voltage(Vbg) + * of 0.85V + */ + snd_soc_write(codec, WCD9335_ANA_BIAS, 0x20); + snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28); + snd_soc_write(codec, WCD9335_BIAS_VBG_FINE_ADJ, 0x05); + snd_soc_write(codec, WCD9335_ANA_BIAS, 0xA0); + /* Wait 2 sec after enabling band gap bias */ + usleep_range(2000000, 2000100); + + snd_soc_write(codec, WCD9335_ANA_CLK_TOP, 0x82); + snd_soc_write(codec, WCD9335_ANA_CLK_TOP, 0x87); + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x10); + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_CFG, 0x0D); + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01); + + snd_soc_write(codec, WCD9335_ANA_VBADC, 0x80); + snd_soc_write(codec, WCD9335_VBADC_SUBBLOCK_EN, 0xDE); + snd_soc_write(codec, WCD9335_VBADC_FE_CTRL, 0x3C); + /* Wait 1 msec after calibration select as Vbg */ + usleep_range(1000, 1100); + + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0xC0); + val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB); + val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB); + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80); + + vbat->dcp1 = (((val1 & 0xFF) << 3) | (val2 & 0x07)); + + /* + * Measure dcp2 by applying band gap voltage(Vbg) + * of 1.05V + */ + snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80); + snd_soc_write(codec, WCD9335_ANA_BIAS, 0xC0); + snd_soc_write(codec, WCD9335_BIAS_CTL, 0x68); + /* Wait 2 msec after selecting Vbg as 1.05V */ + usleep_range(2000, 2100); + + snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80); + /* Wait 1 sec after enabling band gap bias */ + usleep_range(1000000, 1000100); + + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0xC0); + val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB); + val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB); + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80); + + vbat->dcp2 = (((val1 & 0xFF) << 3) | (val2 & 0x07)); + + dev_dbg(codec->dev, "%s: dcp1:0x%x, dcp2:0x%x\n", + __func__, vbat->dcp1, vbat->dcp2); + + /* Reset the Vbat ADC configuration */ + snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80); + snd_soc_write(codec, WCD9335_ANA_BIAS, 0xC0); + + snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28); + /* Wait 2 msec after selecting Vbg as 0.85V */ + usleep_range(2000, 2100); + + snd_soc_write(codec, WCD9335_ANA_BIAS, 0xA0); + /* Wait 1 sec after enabling band gap bias */ + usleep_range(1000000, 1000100); + + snd_soc_write(codec, WCD9335_VBADC_FE_CTRL, 0x1C); + snd_soc_write(codec, WCD9335_VBADC_SUBBLOCK_EN, 0xFE); + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80); + snd_soc_write(codec, WCD9335_ANA_VBADC, 0x00); + + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x00); + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x00); + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_CFG, 0x0A); +} + +static void wcd_vbat_adc_out_config(struct wcd_vbat *vbat, + struct snd_soc_codec *codec) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!vbat->adc_config) { + tasha_cdc_mclk_enable(codec, true, false); + + if (TASHA_IS_2_0(wcd9xxx)) + wcd_vbat_adc_out_config_2_0(vbat, codec); + else + wcd_vbat_adc_out_config_1_x(vbat, codec); + + tasha_cdc_mclk_enable(codec, false, false); + vbat->adc_config = true; + } +} + +static int tasha_update_vbat_reg_config(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct firmware_cal *hwdep_cal = NULL; + struct vbat_monitor_reg *vbat_reg_ptr = NULL; + const void *data; + size_t cal_size, vbat_size_remaining; + int ret = 0, i; + u32 vbat_writes_size = 0; + u16 reg; + u8 mask, val, old_val; + + hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_VBAT_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + dev_err(codec->dev, "%s: Vbat cal not received\n", + __func__); + ret = -EINVAL; + goto done; + } + + if (cal_size < sizeof(*vbat_reg_ptr)) { + dev_err(codec->dev, + "%s: Incorrect size %zd for Vbat Cal, expected %zd\n", + __func__, cal_size, sizeof(*vbat_reg_ptr)); + ret = -EINVAL; + goto done; + } + + vbat_reg_ptr = (struct vbat_monitor_reg *) (data); + + if (!vbat_reg_ptr) { + dev_err(codec->dev, + "%s: Invalid calibration data for Vbat\n", + __func__); + ret = -EINVAL; + goto done; + } + + vbat_writes_size = vbat_reg_ptr->size; + vbat_size_remaining = cal_size - sizeof(u32); + dev_dbg(codec->dev, "%s: vbat_writes_sz: %d, vbat_sz_remaining: %zd\n", + __func__, vbat_writes_size, vbat_size_remaining); + + if ((vbat_writes_size * TASHA_PACKED_REG_SIZE) + > vbat_size_remaining) { + pr_err("%s: Incorrect Vbat calibration data\n", __func__); + ret = -EINVAL; + goto done; + } + + for (i = 0 ; i < vbat_writes_size; i++) { + TASHA_CODEC_UNPACK_ENTRY(vbat_reg_ptr->writes[i], + reg, mask, val); + old_val = snd_soc_read(codec, reg); + snd_soc_write(codec, reg, (old_val & ~mask) | (val & mask)); + } + +done: + return ret; +} + +static int tasha_vbat_adc_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + wcd_vbat_adc_out_config(&tasha->vbat, codec); + + ucontrol->value.integer.value[0] = tasha->vbat.dcp1; + ucontrol->value.integer.value[1] = tasha->vbat.dcp2; + + dev_dbg(codec->dev, + "%s: Vbat ADC output values, Dcp1 : %lu, Dcp2: %lu\n", + __func__, ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]); + + return 0; +} + +static const char * const tasha_vbat_gsm_mode_text[] = { + "OFF", "ON"}; + +static const struct soc_enum tasha_vbat_gsm_mode_enum = + SOC_ENUM_SINGLE_EXT(2, tasha_vbat_gsm_mode_text); + +static int tasha_vbat_gsm_mode_func_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ucontrol->value.integer.value[0] = + ((snd_soc_read(codec, WCD9335_CDC_VBAT_VBAT_CFG) & 0x04) ? + 1 : 0); + + dev_dbg(codec->dev, "%s: value: %lu\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int tasha_vbat_gsm_mode_func_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: value: %lu\n", __func__, + ucontrol->value.integer.value[0]); + + /* Set Vbat register configuration for GSM mode bit based on value */ + if (ucontrol->value.integer.value[0]) + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_CFG, + 0x04, 0x04); + else + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_CFG, + 0x04, 0x00); + + return 0; +} + +static int tasha_codec_vbat_enable_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 vbat_path_ctl, vbat_cfg, vbat_path_cfg; + + vbat_path_ctl = WCD9335_CDC_VBAT_VBAT_PATH_CTL; + vbat_cfg = WCD9335_CDC_VBAT_VBAT_CFG; + vbat_path_cfg = WCD9335_CDC_RX8_RX_PATH_CFG1; + + if (!strcmp(w->name, "RX INT8 VBAT")) + vbat_path_cfg = WCD9335_CDC_RX8_RX_PATH_CFG1; + else if (!strcmp(w->name, "RX INT7 VBAT")) + vbat_path_cfg = WCD9335_CDC_RX7_RX_PATH_CFG1; + else if (!strcmp(w->name, "RX INT6 VBAT")) + vbat_path_cfg = WCD9335_CDC_RX6_RX_PATH_CFG1; + else if (!strcmp(w->name, "RX INT5 VBAT")) + vbat_path_cfg = WCD9335_CDC_RX5_RX_PATH_CFG1; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tasha_update_vbat_reg_config(codec); + if (ret) { + dev_dbg(codec->dev, + "%s : VBAT isn't calibrated, So not enabling it\n", + __func__); + return 0; + } + snd_soc_write(codec, WCD9335_ANA_VBADC, 0x80); + snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x02); + snd_soc_update_bits(codec, vbat_path_ctl, 0x10, 0x10); + snd_soc_update_bits(codec, vbat_cfg, 0x01, 0x01); + tasha->vbat.is_enabled = true; + break; + case SND_SOC_DAPM_POST_PMD: + if (tasha->vbat.is_enabled) { + snd_soc_update_bits(codec, vbat_cfg, 0x01, 0x00); + snd_soc_update_bits(codec, vbat_path_ctl, 0x10, 0x00); + snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x00); + snd_soc_write(codec, WCD9335_ANA_VBADC, 0x00); + tasha->vbat.is_enabled = false; + } + break; + }; + + return ret; +} + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI" +}; + +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 char * const amic_pwr_lvl_text[] = { + "LOW_PWR", "DEFAULT", "HIGH_PERF" +}; + +static const struct soc_enum amic_pwr_lvl_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(amic_pwr_lvl_text), + amic_pwr_lvl_text); + +static const struct snd_kcontrol_new tasha_snd_controls[] = { + SOC_SINGLE_SX_TLV("RX0 Digital Volume", WCD9335_CDC_RX0_RX_VOL_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX1 Digital Volume", WCD9335_CDC_RX1_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Digital Volume", WCD9335_CDC_RX2_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Digital Volume", WCD9335_CDC_RX3_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX4 Digital Volume", WCD9335_CDC_RX4_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX5 Digital Volume", WCD9335_CDC_RX5_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX6 Digital Volume", WCD9335_CDC_RX6_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX7 Digital Volume", WCD9335_CDC_RX7_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Digital Volume", WCD9335_CDC_RX8_RX_VOL_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("RX0 Mix Digital Volume", + WCD9335_CDC_RX0_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX1 Mix Digital Volume", + WCD9335_CDC_RX1_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX2 Mix Digital Volume", + WCD9335_CDC_RX2_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX3 Mix Digital Volume", + WCD9335_CDC_RX3_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX4 Mix Digital Volume", + WCD9335_CDC_RX4_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX5 Mix Digital Volume", + WCD9335_CDC_RX5_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX6 Mix Digital Volume", + WCD9335_CDC_RX6_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX7 Mix Digital Volume", + WCD9335_CDC_RX7_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX8 Mix Digital Volume", + WCD9335_CDC_RX8_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + + SOC_SINGLE_SX_TLV("DEC0 Volume", WCD9335_CDC_TX0_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC1 Volume", WCD9335_CDC_TX1_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC2 Volume", WCD9335_CDC_TX2_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC3 Volume", WCD9335_CDC_TX3_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC4 Volume", WCD9335_CDC_TX4_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC5 Volume", WCD9335_CDC_TX5_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC6 Volume", WCD9335_CDC_TX6_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC7 Volume", WCD9335_CDC_TX7_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC8 Volume", WCD9335_CDC_TX8_TX_VOL_CTL, 0, + -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("IIR0 INP0 Volume", + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP1 Volume", + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP2 Volume", + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP3 Volume", + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP0 Volume", + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84, + 40, digital_gain), + + SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tasha_get_anc_slot, + tasha_put_anc_slot), + SOC_ENUM_EXT("ANC Function", tasha_anc_func_enum, tasha_get_anc_func, + tasha_put_anc_func), + + SOC_ENUM_EXT("CLK MODE", tasha_clkmode_enum, tasha_get_clkmode, + tasha_put_clkmode), + + 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 INT5_1 HPF cut off", cf_int5_1_enum), + SOC_ENUM("RX INT5_2 HPF cut off", cf_int5_2_enum), + SOC_ENUM("RX INT6_1 HPF cut off", cf_int6_1_enum), + SOC_ENUM("RX INT6_2 HPF cut off", cf_int6_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_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + + SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP5 Switch", SND_SOC_NOPM, COMPANDER_5, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP6 Switch", SND_SOC_NOPM, COMPANDER_6, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, + tasha_get_compander, tasha_set_compander), + + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + tasha_rx_hph_mode_get, tasha_rx_hph_mode_put), + + SOC_ENUM_EXT("MAD Input", tasha_conn_mad_enum, + tasha_mad_input_get, tasha_mad_input_put), + SOC_SINGLE_EXT("LDO_H Enable", SND_SOC_NOPM, 0, 1, 0, + tasha_enable_ldo_h_get, tasha_enable_ldo_h_put), + + SOC_SINGLE_EXT("DMIC1_CLK_PIN_MODE", SND_SOC_NOPM, 17, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC1_DATA_PIN_MODE", SND_SOC_NOPM, 18, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC2_CLK_PIN_MODE", SND_SOC_NOPM, 19, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC2_DATA_PIN_MODE", SND_SOC_NOPM, 20, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC3_CLK_PIN_MODE", SND_SOC_NOPM, 21, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC3_DATA_PIN_MODE", SND_SOC_NOPM, 22, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + SOC_ENUM_EXT("AMIC_1_2 PWR MODE", amic_pwr_lvl_enum, + tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_3_4 PWR MODE", amic_pwr_lvl_enum, + tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_5_6 PWR MODE", amic_pwr_lvl_enum, + tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put), + + SOC_SINGLE_MULTI_EXT("Vbat ADC data", SND_SOC_NOPM, 0, 0xFFFF, 0, 2, + tasha_vbat_adc_data_get, NULL), + + SOC_ENUM_EXT("GSM mode Enable", tasha_vbat_gsm_mode_enum, + tasha_vbat_gsm_mode_func_get, + tasha_vbat_gsm_mode_func_put), +}; + +static int tasha_put_dec_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg; + u8 mic_sel; + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + switch (e->reg) { + case WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1: + mic_sel_reg = WCD9335_CDC_TX0_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1: + mic_sel_reg = WCD9335_CDC_TX1_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1: + mic_sel_reg = WCD9335_CDC_TX2_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1: + mic_sel_reg = WCD9335_CDC_TX3_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0: + mic_sel_reg = WCD9335_CDC_TX4_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0: + mic_sel_reg = WCD9335_CDC_TX5_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0: + mic_sel_reg = WCD9335_CDC_TX6_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0: + mic_sel_reg = WCD9335_CDC_TX7_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0: + mic_sel_reg = WCD9335_CDC_TX8_TX_PATH_CFG0; + break; + default: + dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + + /* ADC: 0, DMIC: 1 */ + mic_sel = val ? 0x0 : 0x1; + snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, mic_sel << 7); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int tasha_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + unsigned short look_ahead_dly_reg = WCD9335_CDC_RX0_RX_PATH_CFG0; + + val = ucontrol->value.enumerated.item[0]; + if (val >= e->items) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + if (e->reg == WCD9335_CDC_RX0_RX_PATH_SEC0) + look_ahead_dly_reg = WCD9335_CDC_RX0_RX_PATH_CFG0; + else if (e->reg == WCD9335_CDC_RX1_RX_PATH_SEC0) + look_ahead_dly_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + else if (e->reg == WCD9335_CDC_RX2_RX_PATH_SEC0) + look_ahead_dly_reg = WCD9335_CDC_RX2_RX_PATH_CFG0; + + /* Set Look Ahead Delay */ + snd_soc_update_bits(codec, look_ahead_dly_reg, + 0x08, (val ? 0x08 : 0x00)); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int tasha_ear_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ear_pa_gain = snd_soc_read(codec, WCD9335_ANA_EAR); + + ear_pa_gain = (ear_pa_gain & 0x70) >> 4; + + ucontrol->value.integer.value[0] = ear_pa_gain; + + dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__, + ear_pa_gain); + + return 0; +} + +static int tasha_ear_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + ear_pa_gain = ucontrol->value.integer.value[0] << 4; + + snd_soc_update_bits(codec, WCD9335_ANA_EAR, 0x70, ear_pa_gain); + return 0; +} + +static int tasha_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha->ear_spkr_gain; + + dev_dbg(codec->dev, "%s: ear_spkr_gain = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int tasha_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + tasha->ear_spkr_gain = ucontrol->value.integer.value[0]; + + return 0; +} + +static int tasha_config_compander(struct snd_soc_codec *codec, int interp_n, + int event) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int comp; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* EAR does not have compander */ + if (!interp_n) + return 0; + + comp = interp_n - 1; + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, tasha->comp_enabled[comp]); + + if (!tasha->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = WCD9335_CDC_COMPANDER1_CTL0 + (comp * 8); + rx_path_cfg0_reg = WCD9335_CDC_RX1_RX_PATH_CFG0 + (comp * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +static int tasha_codec_config_mad(struct snd_soc_codec *codec) +{ + int ret = 0; + int idx; + const struct firmware *fw; + struct firmware_cal *hwdep_cal = NULL; + struct wcd_mad_audio_cal *mad_cal = NULL; + const void *data; + const char *filename = TASHA_MAD_AUDIO_FIRMWARE_PATH; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + size_t cal_size; + + hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_MAD_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + ret = request_firmware(&fw, filename, codec->dev); + if (ret || !fw) { + dev_err(codec->dev, + "%s: MAD firmware acquire failed, err = %d\n", + __func__, ret); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + + if (cal_size < sizeof(*mad_cal)) { + dev_err(codec->dev, + "%s: Incorrect size %zd for MAD Cal, expected %zd\n", + __func__, cal_size, sizeof(*mad_cal)); + ret = -ENOMEM; + goto done; + } + + mad_cal = (struct wcd_mad_audio_cal *) (data); + if (!mad_cal) { + dev_err(codec->dev, + "%s: Invalid calibration data\n", + __func__); + ret = -EINVAL; + goto done; + } + + snd_soc_write(codec, WCD9335_SOC_MAD_MAIN_CTL_2, + mad_cal->microphone_info.cycle_time); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_MAIN_CTL_1, 0xFF << 3, + ((uint16_t)mad_cal->microphone_info.settle_time) + << 3); + + /* Audio */ + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_8, + mad_cal->audio_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_CTL_1, + 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_CTL_2, 0x03 << 2, + mad_cal->audio_info.detection_mechanism << 2); + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_7, + mad_cal->audio_info.rms_diff_threshold & 0x3F); + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_5, + mad_cal->audio_info.rms_threshold_lsb); + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_6, + mad_cal->audio_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL, + mad_cal->audio_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->audio_info.iir_coefficients[idx]); + } + + /* Beacon */ + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_8, + mad_cal->beacon_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_CTL_1, + 0x07 << 4, mad_cal->beacon_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_CTL_2, 0x03 << 2, + mad_cal->beacon_info.detection_mechanism << 2); + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_7, + mad_cal->beacon_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_5, + mad_cal->beacon_info.rms_threshold_lsb); + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_6, + mad_cal->beacon_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->beacon_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL, + mad_cal->beacon_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Beacon IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->beacon_info.iir_coefficients[idx]); + } + + /* Ultrasound */ + snd_soc_update_bits(codec, WCD9335_SOC_MAD_ULTR_CTL_1, + 0x07 << 4, + mad_cal->ultrasound_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_ULTR_CTL_2, 0x03 << 2, + mad_cal->ultrasound_info.detection_mechanism << 2); + snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_7, + mad_cal->ultrasound_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_5, + mad_cal->ultrasound_info.rms_threshold_lsb); + snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_6, + mad_cal->ultrasound_info.rms_threshold_msb); + +done: + if (!hwdep_cal) + release_firmware(fw); + + return ret; +} + +static int tasha_codec_enable_mad(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + dev_dbg(codec->dev, + "%s: event = %d\n", __func__, event); + + /* Return if CPE INPUT is DEC1 */ + if (snd_soc_read(codec, WCD9335_CPE_SS_SVA_CFG) & 0x01) + return ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + + /* Turn on MAD clk */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL, + 0x01, 0x01); + + /* Undo reset for MAD */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL, + 0x02, 0x00); + ret = tasha_codec_config_mad(codec); + if (ret) + dev_err(codec->dev, + "%s: Failed to config MAD, err = %d\n", + __func__, ret); + break; + case SND_SOC_DAPM_POST_PMD: + /* Reset the MAD block */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL, + 0x02, 0x02); + /* Turn off MAD clk */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL, + 0x01, 0x00); + break; + } + + return ret; +} + +static int tasha_codec_configure_cpe_input(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, + "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Configure CPE input as DEC1 */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_SVA_CFG, + 0x01, 0x01); + + /* Configure DEC1 Tx out with sample rate as 16K */ + snd_soc_update_bits(codec, WCD9335_CDC_TX1_TX_PATH_CTL, + 0x0F, 0x01); + + break; + case SND_SOC_DAPM_POST_PMD: + /* Reset DEC1 Tx out sample rate */ + snd_soc_update_bits(codec, WCD9335_CDC_TX1_TX_PATH_CTL, + 0x0F, 0x04); + snd_soc_update_bits(codec, WCD9335_CPE_SS_SVA_CFG, + 0x01, 0x00); + + break; + } + + return 0; +} + + +static int tasha_codec_aif4_mixer_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + if (test_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask)) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + dev_dbg(codec->dev, "%s: AIF4 switch value = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int tasha_codec_aif4_mixer_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_dapm_update *update = NULL; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: AIF4 switch value = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0]) { + snd_soc_dapm_mixer_update_power(widget->dapm, + kcontrol, 1, update); + set_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask); + } else { + snd_soc_dapm_mixer_update_power(widget->dapm, + kcontrol, 0, update); + clear_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask); + } + + return 1; +} + +static const char * const tasha_ear_pa_gain_text[] = { + "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", + "G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB" +}; + +static const char * const tasha_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", "G_4_DB", + "G_5_DB", "G_6_DB" +}; + +static const struct soc_enum tasha_ear_pa_gain_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_pa_gain_text), + tasha_ear_pa_gain_text); + +static const struct soc_enum tasha_ear_spkr_pa_gain_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_spkr_pa_gain_text), + tasha_ear_spkr_pa_gain_text); + +static const struct snd_kcontrol_new tasha_analog_gain_controls[] = { + SOC_ENUM_EXT("EAR PA Gain", tasha_ear_pa_gain_enum, + tasha_ear_pa_gain_get, tasha_ear_pa_gain_put), + + SOC_ENUM_EXT("EAR SPKR PA Gain", tasha_ear_spkr_pa_gain_enum, + tasha_ear_spkr_pa_gain_get, tasha_ear_spkr_pa_gain_put), + + SOC_SINGLE_TLV("HPHL Volume", WCD9335_HPH_L_EN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD9335_HPH_R_EN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("LINEOUT1 Volume", WCD9335_DIFF_LO_LO1_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT2 Volume", WCD9335_DIFF_LO_LO2_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT3 Volume", WCD9335_SE_LO_LO3_GAIN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("LINEOUT4 Volume", WCD9335_SE_LO_LO4_GAIN, 0, 20, 1, + line_gain), + + SOC_SINGLE_TLV("ADC1 Volume", WCD9335_ANA_AMIC1, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD9335_ANA_AMIC2, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD9335_ANA_AMIC3, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD9335_ANA_AMIC4, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC5 Volume", WCD9335_ANA_AMIC5, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC6 Volume", WCD9335_ANA_AMIC6, 0, 20, 0, + analog_gain), +}; + +static const char * const spl_src0_mux_text[] = { + "ZERO", "SRC_IN_HPHL", "SRC_IN_LO1", +}; + +static const char * const spl_src1_mux_text[] = { + "ZERO", "SRC_IN_HPHR", "SRC_IN_LO2", +}; + +static const char * const spl_src2_mux_text[] = { + "ZERO", "SRC_IN_LO3", "SRC_IN_SPKRL", +}; + +static const char * const spl_src3_mux_text[] = { + "ZERO", "SRC_IN_LO4", "SRC_IN_SPKRR", +}; + +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 sb_tx0_mux_text[] = { + "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192" +}; + +static const char * const sb_tx1_mux_text[] = { + "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192" +}; + +static const char * const sb_tx2_mux_text[] = { + "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192" +}; + +static const char * const sb_tx3_mux_text[] = { + "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192" +}; + +static const char * const sb_tx4_mux_text[] = { + "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192" +}; + +static const char * const sb_tx5_mux_text[] = { + "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192" +}; + +static const char * const sb_tx6_mux_text[] = { + "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192" +}; + +static const char * const sb_tx7_mux_text[] = { + "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192" +}; + +static const char * const sb_tx8_mux_text[] = { + "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192" +}; + +static const char * const sb_tx9_mux_text[] = { + "ZERO", "DEC7", "DEC7_192" +}; + +static const char * const sb_tx10_mux_text[] = { + "ZERO", "DEC6", "DEC6_192" +}; + +static const char * const sb_tx11_mux_text[] = { + "DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST" +}; + +static const char * const sb_tx11_inp1_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", + "DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12" +}; + +static const char * const sb_tx13_mux_text[] = { + "ZERO", "DEC5", "DEC5_192" +}; + +static const char * const tx13_inp_mux_text[] = { + "CDC_DEC_5", "MAD_BRDCST", "CPE_TX_PP" +}; + +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_interp_mux_text[] = { + "ZERO", "RX INT0 MIX2", +}; + +static const char * const rx_int1_interp_mux_text[] = { + "ZERO", "RX INT1 MIX2", +}; + +static const char * const rx_int2_interp_mux_text[] = { + "ZERO", "RX INT2 MIX2", +}; + +static const char * const rx_int3_interp_mux_text[] = { + "ZERO", "RX INT3 MIX2", +}; + +static const char * const rx_int4_interp_mux_text[] = { + "ZERO", "RX INT4 MIX2", +}; + +static const char * const rx_int5_interp_mux_text[] = { + "ZERO", "RX INT5 MIX2", +}; + +static const char * const rx_int6_interp_mux_text[] = { + "ZERO", "RX INT6 MIX2", +}; + +static const char * const rx_int7_interp_mux_text[] = { + "ZERO", "RX INT7 MIX2", +}; + +static const char * const rx_int8_interp_mux_text[] = { + "ZERO", "RX INT8 SEC MIX" +}; + +static const char * const mad_sel_text[] = { + "SPE", "MSM" +}; + +static const char * const adc_mux_text[] = { + "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2" +}; + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", + "SMIC0", "SMIC1", "SMIC2", "SMIC3" +}; + +static const char * const dmic_mux_alt_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", +}; + +static const char * const amic_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6" +}; + +static const char * const rx_echo_mux_text[] = { + "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2", "RX_MIX3", "RX_MIX4", + "RX_MIX5", "RX_MIX6", "RX_MIX7", "RX_MIX8", "RX_MIX_VBAT5", + "RX_MIX_VBAT6", "RX_MIX_VBAT7", "RX_MIX_VBAT8" +}; + +static const char * const anc0_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHL", "ANC_IN_EAR", "ANC_IN_EAR_SPKR", + "ANC_IN_LO1" +}; + +static const char * const anc1_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHR", "ANC_IN_LO2" +}; + +static const char * const native_mux_text[] = { + "OFF", "ON", +}; + +static const struct soc_enum spl_src0_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 0, 3, + spl_src0_mux_text); + +static const struct soc_enum spl_src1_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 2, 3, + spl_src1_mux_text); + +static const struct soc_enum spl_src2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 4, 3, + spl_src2_mux_text); + +static const struct soc_enum spl_src3_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 6, 3, + spl_src3_mux_text); + +static const struct soc_enum rx_int0_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int5_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int6_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int7_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_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(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum int1_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum int2_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum int3_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum int4_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum rx_int0_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int5_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int5_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int5_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int6_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int6_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int6_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int1_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int2_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int3_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int4_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int7_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, 4, + rx_sidetone_mix_text); + +static const struct soc_enum tx_adc_mux0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux3_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux4_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux5_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux6_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux7_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux8_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux10_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux11_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux12_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux13_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_dmic_mux0_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux2_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux3_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux4_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux5_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux6_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux7_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux8_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux10_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux11_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux12_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux13_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_amic_mux0_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux2_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux3_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux4_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux5_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux6_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux7_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux8_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux10_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux11_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux12_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux13_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum sb_tx0_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 0, 4, + sb_tx0_mux_text); + +static const struct soc_enum sb_tx1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 2, 4, + sb_tx1_mux_text); + +static const struct soc_enum sb_tx2_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 4, 4, + sb_tx2_mux_text); + +static const struct soc_enum sb_tx3_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 6, 4, + sb_tx3_mux_text); + +static const struct soc_enum sb_tx4_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 0, 4, + sb_tx4_mux_text); + +static const struct soc_enum sb_tx5_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 2, 4, + sb_tx5_mux_text); + +static const struct soc_enum sb_tx6_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 4, 4, + sb_tx6_mux_text); + +static const struct soc_enum sb_tx7_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 6, 4, + sb_tx7_mux_text); + +static const struct soc_enum sb_tx8_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 0, 4, + sb_tx8_mux_text); + +static const struct soc_enum sb_tx9_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 2, 3, + sb_tx9_mux_text); + +static const struct soc_enum sb_tx10_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 4, 3, + sb_tx10_mux_text); + +static const struct soc_enum sb_tx11_mux_enum = + SOC_ENUM_SINGLE(WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG, 0, 4, + sb_tx11_mux_text); + +static const struct soc_enum sb_tx11_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 0, 10, + sb_tx11_inp1_mux_text); + +static const struct soc_enum sb_tx13_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 4, 3, + sb_tx13_mux_text); + +static const struct soc_enum tx13_inp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, 0, 3, + tx13_inp_mux_text); + +static const struct soc_enum rx_mix_tx0_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 4, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx2_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx3_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 4, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx4_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx5_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 4, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx6_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx7_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 4, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx8_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum iir0_inp0_mux_enum = + SOC_ENUM_SINGLE(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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(WCD9335_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 rx_int0_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CTL, 5, 2, + rx_int0_interp_mux_text); + +static const struct soc_enum rx_int1_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CTL, 5, 2, + rx_int1_interp_mux_text); + +static const struct soc_enum rx_int2_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CTL, 5, 2, + rx_int2_interp_mux_text); + +static const struct soc_enum rx_int3_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CTL, 5, 2, + rx_int3_interp_mux_text); + +static const struct soc_enum rx_int4_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CTL, 5, 2, + rx_int4_interp_mux_text); + +static const struct soc_enum rx_int5_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CTL, 5, 2, + rx_int5_interp_mux_text); + +static const struct soc_enum rx_int6_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CTL, 5, 2, + rx_int6_interp_mux_text); + +static const struct soc_enum rx_int7_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CTL, 5, 2, + rx_int7_interp_mux_text); + +static const struct soc_enum rx_int8_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CTL, 5, 2, + rx_int8_interp_mux_text); + +static const struct soc_enum mad_sel_enum = + SOC_ENUM_SINGLE(WCD9335_CPE_SS_CFG, 0, 2, mad_sel_text); + +static const struct soc_enum anc0_fb_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 0, 5, + anc0_fb_mux_text); + +static const struct soc_enum anc1_fb_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 3, 3, + anc1_fb_mux_text); + +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, + tasha_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, + tasha_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, + tasha_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new spl_src0_mux = + SOC_DAPM_ENUM("SPL SRC0 MUX Mux", spl_src0_mux_chain_enum); + +static const struct snd_kcontrol_new spl_src1_mux = + SOC_DAPM_ENUM("SPL SRC1 MUX Mux", spl_src1_mux_chain_enum); + +static const struct snd_kcontrol_new spl_src2_mux = + SOC_DAPM_ENUM("SPL SRC2 MUX Mux", spl_src2_mux_chain_enum); + +static const struct snd_kcontrol_new spl_src3_mux = + SOC_DAPM_ENUM("SPL SRC3 MUX Mux", spl_src3_mux_chain_enum); + +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_int5_2_mux = + SOC_DAPM_ENUM("RX INT5_2 MUX Mux", rx_int5_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int6_2_mux = + SOC_DAPM_ENUM("RX INT6_2 MUX Mux", rx_int6_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 int1_1_native_mux = + SOC_DAPM_ENUM("RX INT1_1 NATIVE MUX Mux", int1_1_native_enum); + +static const struct snd_kcontrol_new int2_1_native_mux = + SOC_DAPM_ENUM("RX INT2_1 NATIVE MUX Mux", int2_1_native_enum); + +static const struct snd_kcontrol_new int3_1_native_mux = + SOC_DAPM_ENUM("RX INT3_1 NATIVE MUX Mux", int3_1_native_enum); + +static const struct snd_kcontrol_new int4_1_native_mux = + SOC_DAPM_ENUM("RX INT4_1 NATIVE MUX Mux", int4_1_native_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_int5_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT5_1 MIX1 INP0 Mux", rx_int5_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int5_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT5_1 MIX1 INP1 Mux", rx_int5_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int5_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT5_1 MIX1 INP2 Mux", rx_int5_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int6_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT6_1 MIX1 INP0 Mux", rx_int6_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int6_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT6_1 MIX1 INP1 Mux", rx_int6_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int6_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT6_1 MIX1 INP2 Mux", rx_int6_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_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int1_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT1 MIX2 INP Mux", rx_int1_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int2_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT2 MIX2 INP Mux", rx_int2_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int3_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT3 MIX2 INP Mux", rx_int3_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int4_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT4 MIX2 INP Mux", rx_int4_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int7_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT7 MIX2 INP Mux", rx_int7_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new tx_adc_mux0 = + SOC_DAPM_ENUM_EXT("ADC MUX0 Mux", tx_adc_mux0_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux1 = + SOC_DAPM_ENUM_EXT("ADC MUX1 Mux", tx_adc_mux1_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux2 = + SOC_DAPM_ENUM_EXT("ADC MUX2 Mux", tx_adc_mux2_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux3 = + SOC_DAPM_ENUM_EXT("ADC MUX3 Mux", tx_adc_mux3_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux4 = + SOC_DAPM_ENUM_EXT("ADC MUX4 Mux", tx_adc_mux4_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux5 = + SOC_DAPM_ENUM_EXT("ADC MUX5 Mux", tx_adc_mux5_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux6 = + SOC_DAPM_ENUM_EXT("ADC MUX6 Mux", tx_adc_mux6_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux7 = + SOC_DAPM_ENUM_EXT("ADC MUX7 Mux", tx_adc_mux7_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux8 = + SOC_DAPM_ENUM_EXT("ADC MUX8 Mux", tx_adc_mux8_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux10 = + SOC_DAPM_ENUM("ADC MUX10 Mux", tx_adc_mux10_chain_enum); + +static const struct snd_kcontrol_new tx_adc_mux11 = + SOC_DAPM_ENUM("ADC MUX11 Mux", tx_adc_mux11_chain_enum); + +static const struct snd_kcontrol_new tx_adc_mux12 = + SOC_DAPM_ENUM("ADC MUX12 Mux", tx_adc_mux12_chain_enum); + +static const struct snd_kcontrol_new tx_adc_mux13 = + SOC_DAPM_ENUM("ADC MUX13 Mux", tx_adc_mux13_chain_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_dmic_mux10 = + SOC_DAPM_ENUM("DMIC MUX10 Mux", tx_dmic_mux10_enum); + +static const struct snd_kcontrol_new tx_dmic_mux11 = + SOC_DAPM_ENUM("DMIC MUX11 Mux", tx_dmic_mux11_enum); + +static const struct snd_kcontrol_new tx_dmic_mux12 = + SOC_DAPM_ENUM("DMIC MUX12 Mux", tx_dmic_mux12_enum); + +static const struct snd_kcontrol_new tx_dmic_mux13 = + SOC_DAPM_ENUM("DMIC MUX13 Mux", tx_dmic_mux13_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_amic_mux10 = + SOC_DAPM_ENUM("AMIC MUX10 Mux", tx_amic_mux10_enum); + +static const struct snd_kcontrol_new tx_amic_mux11 = + SOC_DAPM_ENUM("AMIC MUX11 Mux", tx_amic_mux11_enum); + +static const struct snd_kcontrol_new tx_amic_mux12 = + SOC_DAPM_ENUM("AMIC MUX12 Mux", tx_amic_mux12_enum); + +static const struct snd_kcontrol_new tx_amic_mux13 = + SOC_DAPM_ENUM("AMIC MUX13 Mux", tx_amic_mux13_enum); + +static const struct snd_kcontrol_new sb_tx0_mux = + SOC_DAPM_ENUM("SLIM TX0 MUX Mux", sb_tx0_mux_enum); + +static const struct snd_kcontrol_new sb_tx1_mux = + SOC_DAPM_ENUM("SLIM TX1 MUX Mux", sb_tx1_mux_enum); + +static const struct snd_kcontrol_new sb_tx2_mux = + SOC_DAPM_ENUM("SLIM TX2 MUX Mux", sb_tx2_mux_enum); + +static const struct snd_kcontrol_new sb_tx3_mux = + SOC_DAPM_ENUM("SLIM TX3 MUX Mux", sb_tx3_mux_enum); + +static const struct snd_kcontrol_new sb_tx4_mux = + SOC_DAPM_ENUM("SLIM TX4 MUX Mux", sb_tx4_mux_enum); + +static const struct snd_kcontrol_new sb_tx5_mux = + SOC_DAPM_ENUM("SLIM TX5 MUX Mux", sb_tx5_mux_enum); + +static const struct snd_kcontrol_new sb_tx6_mux = + SOC_DAPM_ENUM("SLIM TX6 MUX Mux", sb_tx6_mux_enum); + +static const struct snd_kcontrol_new sb_tx7_mux = + SOC_DAPM_ENUM("SLIM TX7 MUX Mux", sb_tx7_mux_enum); + +static const struct snd_kcontrol_new sb_tx8_mux = + SOC_DAPM_ENUM("SLIM TX8 MUX Mux", sb_tx8_mux_enum); + +static const struct snd_kcontrol_new sb_tx9_mux = + SOC_DAPM_ENUM("SLIM TX9 MUX Mux", sb_tx9_mux_enum); + +static const struct snd_kcontrol_new sb_tx10_mux = + SOC_DAPM_ENUM("SLIM TX10 MUX Mux", sb_tx10_mux_enum); + +static const struct snd_kcontrol_new sb_tx11_mux = + SOC_DAPM_ENUM("SLIM TX11 MUX Mux", sb_tx11_mux_enum); + +static const struct snd_kcontrol_new sb_tx11_inp1_mux = + SOC_DAPM_ENUM("SLIM TX11 INP1 MUX Mux", sb_tx11_inp1_mux_enum); + +static const struct snd_kcontrol_new sb_tx13_mux = + SOC_DAPM_ENUM("SLIM TX13 MUX Mux", sb_tx13_mux_enum); + +static const struct snd_kcontrol_new tx13_inp_mux = + SOC_DAPM_ENUM("TX13 INP MUX Mux", tx13_inp_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx0_mux = + SOC_DAPM_ENUM("RX MIX TX0 MUX Mux", rx_mix_tx0_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx1_mux = + SOC_DAPM_ENUM("RX MIX TX1 MUX Mux", rx_mix_tx1_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx2_mux = + SOC_DAPM_ENUM("RX MIX TX2 MUX Mux", rx_mix_tx2_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx3_mux = + SOC_DAPM_ENUM("RX MIX TX3 MUX Mux", rx_mix_tx3_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx4_mux = + SOC_DAPM_ENUM("RX MIX TX4 MUX Mux", rx_mix_tx4_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx5_mux = + SOC_DAPM_ENUM("RX MIX TX5 MUX Mux", rx_mix_tx5_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx6_mux = + SOC_DAPM_ENUM("RX MIX TX6 MUX Mux", rx_mix_tx6_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx7_mux = + SOC_DAPM_ENUM("RX MIX TX7 MUX Mux", rx_mix_tx7_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx8_mux = + SOC_DAPM_ENUM("RX MIX TX8 MUX Mux", rx_mix_tx8_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 rx_int0_interp_mux = + SOC_DAPM_ENUM("RX INT0 INTERP Mux", rx_int0_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_interp_mux = + SOC_DAPM_ENUM("RX INT1 INTERP Mux", rx_int1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_interp_mux = + SOC_DAPM_ENUM("RX INT2 INTERP Mux", rx_int2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_interp_mux = + SOC_DAPM_ENUM("RX INT3 INTERP Mux", rx_int3_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_interp_mux = + SOC_DAPM_ENUM("RX INT4 INTERP Mux", rx_int4_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int5_interp_mux = + SOC_DAPM_ENUM("RX INT5 INTERP Mux", rx_int5_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int6_interp_mux = + SOC_DAPM_ENUM("RX INT6 INTERP Mux", rx_int6_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_interp_mux = + SOC_DAPM_ENUM("RX INT7 INTERP Mux", rx_int7_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int8_interp_mux = + SOC_DAPM_ENUM("RX INT8 INTERP Mux", rx_int8_interp_mux_enum); + +static const struct snd_kcontrol_new mad_sel_mux = + SOC_DAPM_ENUM("MAD_SEL MUX Mux", mad_sel_enum); + +static const struct snd_kcontrol_new aif4_mad_switch = + SOC_DAPM_SINGLE("Switch", WCD9335_CPE_SS_CFG, 5, 1, 0); + +static const struct snd_kcontrol_new mad_brdcst_switch = + SOC_DAPM_SINGLE("Switch", WCD9335_CPE_SS_CFG, 6, 1, 0); + +static const struct snd_kcontrol_new aif4_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, tasha_codec_aif4_mixer_switch_get, + tasha_codec_aif4_mixer_switch_put); + +static const struct snd_kcontrol_new anc_hphl_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_hphr_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_ear_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_ear_spkr_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_lineout1_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_lineout2_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_spkr_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux0_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux1_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux2_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux3_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux4_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux5_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux6_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux7_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux8_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc0_fb_mux = + SOC_DAPM_ENUM("ANC0 FB MUX Mux", anc0_fb_mux_enum); + +static const struct snd_kcontrol_new anc1_fb_mux = + SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum); + +static int tasha_codec_ec_buf_mux_enable(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d name = %s\n", + __func__, event, w->name); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3B); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x08, 0x08); + snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, + 0x08, 0x08); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, + 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x08, 0x00); + snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x00); + break; + } + + return 0; +}; + +static const char * const ec_buf_mux_text[] = { + "ZERO", "RXMIXEC", "SB_RX0", "SB_RX1", "SB_RX2", "SB_RX3", + "I2S_RX_SD0_L", "I2S_RX_SD0_R", "I2S_RX_SD1_L", "I2S_RX_SD1_R", + "DEC1" +}; + +static SOC_ENUM_SINGLE_DECL(ec_buf_mux_enum, WCD9335_CPE_SS_US_EC_MUX_CFG, + 0, ec_buf_mux_text); + +static const struct snd_kcontrol_new ec_buf_mux = + SOC_DAPM_ENUM("EC BUF Mux", ec_buf_mux_enum); + +static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("ANC EAR"), + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, + AIF4_PB, 0, tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF MIX1 PB", "AIF Mix Playback", 0, + SND_SOC_NOPM, AIF_MIX1_PB, 0, + tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, TASHA_RX0, 0, + &slim_rx_mux[TASHA_RX0]), + SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TASHA_RX1, 0, + &slim_rx_mux[TASHA_RX1]), + SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TASHA_RX2, 0, + &slim_rx_mux[TASHA_RX2]), + SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TASHA_RX3, 0, + &slim_rx_mux[TASHA_RX3]), + SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TASHA_RX4, 0, + &slim_rx_mux[TASHA_RX4]), + SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TASHA_RX5, 0, + &slim_rx_mux[TASHA_RX5]), + SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TASHA_RX6, 0, + &slim_rx_mux[TASHA_RX6]), + SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TASHA_RX7, 0, + &slim_rx_mux[TASHA_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("SPL SRC0 MUX", SND_SOC_NOPM, SPLINE_SRC0, 0, + &spl_src0_mux, tasha_codec_enable_spline_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("SPL SRC1 MUX", SND_SOC_NOPM, SPLINE_SRC1, 0, + &spl_src1_mux, tasha_codec_enable_spline_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("SPL SRC2 MUX", SND_SOC_NOPM, SPLINE_SRC2, 0, + &spl_src2_mux, tasha_codec_enable_spline_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("SPL SRC3 MUX", SND_SOC_NOPM, SPLINE_SRC3, 0, + &spl_src3_mux, tasha_codec_enable_spline_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", WCD9335_CDC_RX0_RX_PATH_MIX_CTL, + 5, 0, &rx_int0_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + 5, 0, &rx_int1_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + 5, 0, &rx_int2_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", WCD9335_CDC_RX3_RX_PATH_MIX_CTL, + 5, 0, &rx_int3_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", WCD9335_CDC_RX4_RX_PATH_MIX_CTL, + 5, 0, &rx_int4_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT5_2 MUX", WCD9335_CDC_RX5_RX_PATH_MIX_CTL, + 5, 0, &rx_int5_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT6_2 MUX", WCD9335_CDC_RX6_RX_PATH_MIX_CTL, + 5, 0, &rx_int6_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", WCD9335_CDC_RX7_RX_PATH_MIX_CTL, + 5, 0, &rx_int7_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", WCD9335_CDC_RX8_RX_PATH_MIX_CTL, + 5, 0, &rx_int8_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + + 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 INT5_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int5_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int5_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int5_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int6_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int6_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int6_1_mix_inp2_mux), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp0_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp1_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp2_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp0_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp1_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp2_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + 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 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int1_spline_mix_switch, + ARRAY_SIZE(rx_int1_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int2_spline_mix_switch, + ARRAY_SIZE(rx_int2_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int3_spline_mix_switch, + ARRAY_SIZE(rx_int3_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int4_spline_mix_switch, + ARRAY_SIZE(rx_int4_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int5_spline_mix_switch, + ARRAY_SIZE(rx_int5_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT5 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX INT6_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT6 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int6_spline_mix_switch, + ARRAY_SIZE(rx_int6_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT6 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + 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 INT7 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int7_spline_mix_switch, + ARRAY_SIZE(rx_int7_spline_mix_switch)), + + 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 INT8 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int8_spline_mix_switch, + ARRAY_SIZE(rx_int8_spline_mix_switch)), + + 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 INT2 MIX2", 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 INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT6 MIX2", 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, tasha_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, tasha_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("RX INT5 VBAT", SND_SOC_NOPM, 0, 0, + rx_int5_vbat_mix_switch, + ARRAY_SIZE(rx_int5_vbat_mix_switch), + tasha_codec_vbat_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT6 VBAT", SND_SOC_NOPM, 0, 0, + rx_int6_vbat_mix_switch, + ARRAY_SIZE(rx_int6_vbat_mix_switch), + tasha_codec_vbat_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT7 VBAT", SND_SOC_NOPM, 0, 0, + rx_int7_vbat_mix_switch, + ARRAY_SIZE(rx_int7_vbat_mix_switch), + tasha_codec_vbat_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT8 VBAT", SND_SOC_NOPM, 0, 0, + rx_int8_vbat_mix_switch, + ARRAY_SIZE(rx_int8_vbat_mix_switch), + tasha_codec_vbat_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT0 MIX2 INP", WCD9335_CDC_RX0_RX_PATH_CFG1, 4, + 0, &rx_int0_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT1 MIX2 INP", WCD9335_CDC_RX1_RX_PATH_CFG1, 4, + 0, &rx_int1_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT2 MIX2 INP", WCD9335_CDC_RX2_RX_PATH_CFG1, 4, + 0, &rx_int2_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT3 MIX2 INP", WCD9335_CDC_RX3_RX_PATH_CFG1, 4, + 0, &rx_int3_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT4 MIX2 INP", WCD9335_CDC_RX4_RX_PATH_CFG1, 4, + 0, &rx_int4_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT7 MIX2 INP", WCD9335_CDC_RX7_RX_PATH_CFG1, 4, + 0, &rx_int7_mix2_inp_mux), + + SND_SOC_DAPM_MUX("SLIM TX0 MUX", SND_SOC_NOPM, TASHA_TX0, 0, + &sb_tx0_mux), + SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TASHA_TX1, 0, + &sb_tx1_mux), + SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TASHA_TX2, 0, + &sb_tx2_mux), + SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TASHA_TX3, 0, + &sb_tx3_mux), + SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TASHA_TX4, 0, + &sb_tx4_mux), + SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TASHA_TX5, 0, + &sb_tx5_mux), + SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TASHA_TX6, 0, + &sb_tx6_mux), + SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TASHA_TX7, 0, + &sb_tx7_mux), + SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TASHA_TX8, 0, + &sb_tx8_mux), + SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TASHA_TX9, 0, + &sb_tx9_mux), + SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TASHA_TX10, 0, + &sb_tx10_mux), + SND_SOC_DAPM_MUX("SLIM TX11 MUX", SND_SOC_NOPM, TASHA_TX11, 0, + &sb_tx11_mux), + SND_SOC_DAPM_MUX("SLIM TX11 INP1 MUX", SND_SOC_NOPM, TASHA_TX11, 0, + &sb_tx11_inp1_mux), + SND_SOC_DAPM_MUX("SLIM TX13 MUX", SND_SOC_NOPM, TASHA_TX13, 0, + &sb_tx13_mux), + SND_SOC_DAPM_MUX("TX13 INP MUX", SND_SOC_NOPM, 0, 0, + &tx13_inp_mux), + + SND_SOC_DAPM_MUX_E("ADC MUX0", WCD9335_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc_mux0, tasha_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", WCD9335_CDC_TX1_TX_PATH_CTL, 5, 0, + &tx_adc_mux1, tasha_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", WCD9335_CDC_TX2_TX_PATH_CTL, 5, 0, + &tx_adc_mux2, tasha_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", WCD9335_CDC_TX3_TX_PATH_CTL, 5, 0, + &tx_adc_mux3, tasha_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", WCD9335_CDC_TX4_TX_PATH_CTL, 5, 0, + &tx_adc_mux4, tasha_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", WCD9335_CDC_TX5_TX_PATH_CTL, 5, 0, + &tx_adc_mux5, tasha_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", WCD9335_CDC_TX6_TX_PATH_CTL, 5, 0, + &tx_adc_mux6, tasha_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", WCD9335_CDC_TX7_TX_PATH_CTL, 5, 0, + &tx_adc_mux7, tasha_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", WCD9335_CDC_TX8_TX_PATH_CTL, 5, 0, + &tx_adc_mux8, tasha_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 MUX10", SND_SOC_NOPM, 10, 0, + &tx_adc_mux10, tasha_codec_tx_adc_cfg, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX11", SND_SOC_NOPM, 11, 0, + &tx_adc_mux11, tasha_codec_tx_adc_cfg, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX12", SND_SOC_NOPM, 12, 0, + &tx_adc_mux12, tasha_codec_tx_adc_cfg, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX13", SND_SOC_NOPM, 13, 0, + &tx_adc_mux13, tasha_codec_tx_adc_cfg, + SND_SOC_DAPM_POST_PMU), + + 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("DMIC MUX10", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux10), + SND_SOC_DAPM_MUX("DMIC MUX11", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux11), + SND_SOC_DAPM_MUX("DMIC MUX12", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux12), + SND_SOC_DAPM_MUX("DMIC MUX13", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux13), + + 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("AMIC MUX10", SND_SOC_NOPM, 0, 0, + &tx_amic_mux10), + SND_SOC_DAPM_MUX("AMIC MUX11", SND_SOC_NOPM, 0, 0, + &tx_amic_mux11), + SND_SOC_DAPM_MUX("AMIC MUX12", SND_SOC_NOPM, 0, 0, + &tx_amic_mux12), + SND_SOC_DAPM_MUX("AMIC MUX13", SND_SOC_NOPM, 0, 0, + &tx_amic_mux13), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD9335_ANA_AMIC1, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD9335_ANA_AMIC2, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD9335_ANA_AMIC3, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD9335_ANA_AMIC4, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC5", NULL, WCD9335_ANA_AMIC5, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC6", NULL, WCD9335_ANA_AMIC6, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("RX INT1 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHL, 0, tasha_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT2 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHR, 0, tasha_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT3 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO1, 0, tasha_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT4 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO2, 0, tasha_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS2", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS3", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS4", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS3_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS4_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY(DAPM_LDO_H_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_ldo_h, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("ANC0 FB MUX", SND_SOC_NOPM, 0, 0, &anc0_fb_mux), + SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux), + + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + SND_SOC_DAPM_INPUT("AMIC6"), + + SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, tasha_codec_enable_slimtx, + 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, tasha_codec_enable_slimtx, + 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, tasha_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM, + AIF4_VIFEED, 0, tasha_codec_enable_slimvi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0, + aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)), + + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, + aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)), + + SND_SOC_DAPM_INPUT("VIINPUT"), + + SND_SOC_DAPM_AIF_OUT("AIF5 CPE", "AIF5 CPE TX", 0, SND_SOC_NOPM, + AIF5_CPE_TX, 0), + + SND_SOC_DAPM_MUX_E("EC BUF MUX INP", SND_SOC_NOPM, 0, 0, &ec_buf_mux, + tasha_codec_ec_buf_mux_enable, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, + tasha_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, + tasha_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, + tasha_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, + tasha_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, + tasha_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, + tasha_codec_enable_dmic, 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_MIXER_E("IIR0", WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL, + 4, 0, NULL, 0, tasha_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER_E("IIR1", WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL, + 4, 0, NULL, 0, tasha_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER("SRC0", WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SRC1", WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("CPE IN Mixer", SND_SOC_NOPM, 0, 0, + cpe_in_mix_switch, + ARRAY_SIZE(cpe_in_mix_switch), + tasha_codec_configure_cpe_input, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT1_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int1_1_native_mux), + SND_SOC_DAPM_MUX("RX INT2_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int2_1_native_mux), + SND_SOC_DAPM_MUX("RX INT3_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int3_1_native_mux), + SND_SOC_DAPM_MUX("RX INT4_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int4_1_native_mux), + SND_SOC_DAPM_MUX("RX MIX TX0 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx0_mux), + SND_SOC_DAPM_MUX("RX MIX TX1 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx1_mux), + SND_SOC_DAPM_MUX("RX MIX TX2 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx2_mux), + SND_SOC_DAPM_MUX("RX MIX TX3 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx3_mux), + SND_SOC_DAPM_MUX("RX MIX TX4 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx4_mux), + SND_SOC_DAPM_MUX("RX MIX TX5 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx5_mux), + SND_SOC_DAPM_MUX("RX MIX TX6 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx6_mux), + SND_SOC_DAPM_MUX("RX MIX TX7 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx7_mux), + SND_SOC_DAPM_MUX("RX MIX TX8 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx8_mux), + + 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 INTERP", SND_SOC_NOPM, + INTERP_EAR, 0, &rx_int0_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 INTERP", SND_SOC_NOPM, + INTERP_HPHL, 0, &rx_int1_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 INTERP", SND_SOC_NOPM, + INTERP_HPHR, 0, &rx_int2_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3 INTERP", SND_SOC_NOPM, + INTERP_LO1, 0, &rx_int3_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4 INTERP", SND_SOC_NOPM, + INTERP_LO2, 0, &rx_int4_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT5 INTERP", SND_SOC_NOPM, + INTERP_LO3, 0, &rx_int5_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT6 INTERP", SND_SOC_NOPM, + INTERP_LO4, 0, &rx_int6_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7 INTERP", SND_SOC_NOPM, + INTERP_SPKR1, 0, &rx_int7_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8 INTERP", SND_SOC_NOPM, + INTERP_SPKR2, 0, &rx_int8_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_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, WCD9335_ANA_HPH, + 5, 0, tasha_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, WCD9335_ANA_HPH, + 4, 0, tasha_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, tasha_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, tasha_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT5 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT6 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PA", WCD9335_ANA_HPH, 7, 0, NULL, 0, + tasha_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", WCD9335_ANA_HPH, 6, 0, NULL, 0, + tasha_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("EAR PA", WCD9335_ANA_EAR, 7, 0, NULL, 0, + tasha_codec_enable_ear_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD9335_ANA_LO_1_2, 7, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD9335_ANA_LO_1_2, 6, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT3 PA", WCD9335_ANA_LO_3_4, 7, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT4 PA", WCD9335_ANA_LO_3_4, 6, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD9335_ANA_EAR, 7, 0, NULL, 0, + tasha_codec_enable_ear_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tasha_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("ANC HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tasha_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("ANC LINEOUT1 PA", WCD9335_ANA_LO_1_2, + 7, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC LINEOUT2 PA", WCD9335_ANA_LO_1_2, + 6, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tasha_codec_enable_spk_anc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + SND_SOC_DAPM_OUTPUT("ANC HPHL"), + SND_SOC_DAPM_OUTPUT("ANC HPHR"), + SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("SPK2 OUT"), + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + SND_SOC_DAPM_OUTPUT("LINEOUT3"), + SND_SOC_DAPM_OUTPUT("LINEOUT4"), + SND_SOC_DAPM_OUTPUT("ANC LINEOUT1"), + SND_SOC_DAPM_OUTPUT("ANC LINEOUT2"), + SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM, + ON_DEMAND_MICBIAS, 0, + tasha_codec_enable_on_demand_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SWITCH("ADC US MUX0", WCD9335_CDC_TX0_TX_PATH_192_CTL, 0, + 0, &adc_us_mux0_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX1", WCD9335_CDC_TX1_TX_PATH_192_CTL, 0, + 0, &adc_us_mux1_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX2", WCD9335_CDC_TX2_TX_PATH_192_CTL, 0, + 0, &adc_us_mux2_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX3", WCD9335_CDC_TX3_TX_PATH_192_CTL, 0, + 0, &adc_us_mux3_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX4", WCD9335_CDC_TX4_TX_PATH_192_CTL, 0, + 0, &adc_us_mux4_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX5", WCD9335_CDC_TX5_TX_PATH_192_CTL, 0, + 0, &adc_us_mux5_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX6", WCD9335_CDC_TX6_TX_PATH_192_CTL, 0, + 0, &adc_us_mux6_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX7", WCD9335_CDC_TX7_TX_PATH_192_CTL, 0, + 0, &adc_us_mux7_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX8", WCD9335_CDC_TX8_TX_PATH_192_CTL, 0, + 0, &adc_us_mux8_switch), + /* MAD related widgets */ + SND_SOC_DAPM_AIF_OUT_E("AIF4 MAD", "AIF4 MAD TX", 0, + SND_SOC_NOPM, 0, 0, + tasha_codec_enable_mad, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("MAD_SEL MUX", SND_SOC_NOPM, 0, 0, + &mad_sel_mux), + SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"), + SND_SOC_DAPM_INPUT("MADINPUT"), + SND_SOC_DAPM_SWITCH("MADONOFF", SND_SOC_NOPM, 0, 0, + &aif4_mad_switch), + SND_SOC_DAPM_SWITCH("MAD_BROADCAST", SND_SOC_NOPM, 0, 0, + &mad_brdcst_switch), + SND_SOC_DAPM_SWITCH("AIF4", SND_SOC_NOPM, 0, 0, + &aif4_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("ANC HPHL Enable", SND_SOC_NOPM, 0, 0, + &anc_hphl_switch), + SND_SOC_DAPM_SWITCH("ANC HPHR Enable", SND_SOC_NOPM, 0, 0, + &anc_hphr_switch), + SND_SOC_DAPM_SWITCH("ANC EAR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_switch), + SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_spkr_switch), + SND_SOC_DAPM_SWITCH("ANC LINEOUT1 Enable", SND_SOC_NOPM, 0, 0, + &anc_lineout1_switch), + SND_SOC_DAPM_SWITCH("ANC LINEOUT2 Enable", SND_SOC_NOPM, 0, 0, + &anc_lineout2_switch), + SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0, + &anc_spkr_pa_switch), +}; + +static int tasha_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 tasha_priv *tasha_p = snd_soc_codec_get_drvdata(dai->codec); + u32 i = 0; + struct wcd9xxx_ch *ch; + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + case AIF4_PB: + case AIF_MIX1_PB: + if (!rx_slot || !rx_num) { + pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n", + __func__, rx_slot, rx_num); + return -EINVAL; + } + list_for_each_entry(ch, &tasha_p->dai[dai->id].wcd9xxx_ch_list, + list) { + pr_debug("%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + rx_slot[i++] = ch->ch_num; + } + pr_debug("%s: rx_num %d\n", __func__, i); + *rx_num = i; + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + case AIF4_MAD_TX: + case AIF4_VIFEED: + if (!tx_slot || !tx_num) { + pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n", + __func__, tx_slot, tx_num); + return -EINVAL; + } + list_for_each_entry(ch, &tasha_p->dai[dai->id].wcd9xxx_ch_list, + list) { + pr_debug("%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + tx_slot[i++] = ch->ch_num; + } + pr_debug("%s: tx_num %d\n", __func__, i); + *tx_num = i; + break; + + default: + pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id); + break; + } + + return 0; +} + +static int tasha_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 tasha_priv *tasha; + struct wcd9xxx *core; + struct wcd9xxx_codec_dai_data *dai_data = NULL; + + if (!dai) { + pr_err("%s: dai is empty\n", __func__); + return -EINVAL; + } + tasha = snd_soc_codec_get_drvdata(dai->codec); + core = dev_get_drvdata(dai->codec->dev->parent); + + if (!tx_slot || !rx_slot) { + pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n", + __func__, tx_slot, rx_slot); + return -EINVAL; + } + pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n" + "tasha->intf_type %d\n", + __func__, dai->name, dai->id, tx_num, rx_num, + tasha->intf_type); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + wcd9xxx_init_slimslave(core, core->slim->laddr, + tx_num, tx_slot, rx_num, rx_slot); + /* Reserve TX12/TX13 for MAD data channel */ + dai_data = &tasha->dai[AIF4_MAD_TX]; + if (dai_data) { + if (TASHA_IS_2_0(tasha->wcd9xxx)) + list_add_tail(&core->tx_chs[TASHA_TX13].list, + &dai_data->wcd9xxx_ch_list); + else + list_add_tail(&core->tx_chs[TASHA_TX12].list, + &dai_data->wcd9xxx_ch_list); + } + } + return 0; +} + +static int tasha_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + return 0; +} + +static void tasha_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec); + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + return; + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && + test_bit(SB_CLK_GEAR, &tasha->status_mask)) { + tasha_codec_vote_max_bw(dai->codec, false); + clear_bit(SB_CLK_GEAR, &tasha->status_mask); + } +} + +static int tasha_set_decimator_rate(struct snd_soc_dai *dai, + u8 tx_fs_rate_reg_val, u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u32 tx_port = 0; + u8 shift = 0, shift_val = 0, tx_mux_sel = 0; + int decimator = -1; + u16 tx_port_reg = 0, tx_fs_reg = 0; + + list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) { + tx_port = ch->port; + dev_dbg(codec->dev, "%s: dai->id = %d, tx_port = %d", + __func__, dai->id, tx_port); + + if ((tx_port < 0) || (tx_port == 12) || (tx_port >= 14)) { + dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n", + __func__, tx_port, dai->id); + return -EINVAL; + } + /* Find the SB TX MUX input - which decimator is connected */ + if (tx_port < 4) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0; + shift = (tx_port << 1); + shift_val = 0x03; + } else if ((tx_port >= 4) && (tx_port < 8)) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1; + shift = ((tx_port - 4) << 1); + shift_val = 0x03; + } else if ((tx_port >= 8) && (tx_port < 11)) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2; + shift = ((tx_port - 8) << 1); + shift_val = 0x03; + } else if (tx_port == 11) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 0; + shift_val = 0x0F; + } else if (tx_port == 13) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 4; + shift_val = 0x03; + } + tx_mux_sel = snd_soc_read(codec, tx_port_reg) & + (shift_val << shift); + tx_mux_sel = tx_mux_sel >> shift; + + if (tx_port <= 8) { + if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3)) + decimator = tx_port; + } else if (tx_port <= 10) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = ((tx_port == 9) ? 7 : 6); + } else if (tx_port == 11) { + if ((tx_mux_sel >= 1) && (tx_mux_sel < 7)) + decimator = tx_mux_sel - 1; + } else if (tx_port == 13) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = 5; + } + + if (decimator >= 0) { + tx_fs_reg = WCD9335_CDC_TX0_TX_PATH_CTL + + 16 * decimator; + dev_dbg(codec->dev, "%s: set DEC%u (-> SLIM_TX%u) rate to %u\n", + __func__, decimator, tx_port, sample_rate); + snd_soc_update_bits(codec, tx_fs_reg, 0x0F, + tx_fs_rate_reg_val); + } else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) { + /* Check if the TX Mux input is RX MIX TXn */ + dev_dbg(codec->dev, "%s: RX_MIX_TX%u going to SLIM TX%u\n", + __func__, tx_port, tx_port); + } else { + dev_err(codec->dev, "%s: ERROR: Invalid decimator: %d\n", + __func__, decimator); + return -EINVAL; + } + } + return 0; +} + +static int tasha_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 int_mix_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp; + u32 j; + u16 int_mux_cfg1, int_fs_reg; + u8 int_mux_cfg1_val; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) { + int_2_inp = ch->port + INTn_2_INP_SEL_RX0 - + TASHA_RX_PORT_START_NUMBER; + if ((int_2_inp < INTn_2_INP_SEL_RX0) || + (int_2_inp > INTn_2_INP_SEL_RX7)) { + pr_err("%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - TASHA_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + int_mux_cfg1 = WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1; + for (j = 0; j < TASHA_NUM_INTERPOLATORS; j++) { + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) & + 0x0F; + if (int_mux_cfg1_val == int_2_inp) { + int_fs_reg = WCD9335_CDC_RX0_RX_PATH_MIX_CTL + + 20 * j; + pr_debug("%s: AIF_MIX_PB DAI(%d) connected to INT%u_2\n", + __func__, dai->id, j); + pr_debug("%s: set INT%u_2 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, + 0x0F, int_mix_fs_rate_reg_val); + } + int_mux_cfg1 += 2; + } + } + return 0; +} + +static int tasha_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 int_prim_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp; + u32 j; + u16 int_mux_cfg0, int_mux_cfg1; + u16 int_fs_reg; + u8 int_mux_cfg0_val, int_mux_cfg1_val; + u8 inp0_sel, inp1_sel, inp2_sel; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) { + int_1_mix1_inp = ch->port + INTn_1_MIX_INP_SEL_RX0 - + TASHA_RX_PORT_START_NUMBER; + if ((int_1_mix1_inp < INTn_1_MIX_INP_SEL_RX0) || + (int_1_mix1_inp > INTn_1_MIX_INP_SEL_RX7)) { + pr_err("%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - TASHA_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + int_mux_cfg0 = WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0; + + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the slim rx port + * is connected + */ + for (j = 0; j < TASHA_NUM_INTERPOLATORS; j++) { + int_mux_cfg1 = int_mux_cfg0 + 1; + + int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1); + inp0_sel = int_mux_cfg0_val & 0x0F; + inp1_sel = (int_mux_cfg0_val >> 4) & 0x0F; + inp2_sel = (int_mux_cfg1_val >> 4) & 0x0F; + if ((inp0_sel == int_1_mix1_inp) || + (inp1_sel == int_1_mix1_inp) || + (inp2_sel == int_1_mix1_inp)) { + int_fs_reg = WCD9335_CDC_RX0_RX_PATH_CTL + + 20 * j; + pr_debug("%s: AIF_PB DAI(%d) connected to INT%u_1\n", + __func__, dai->id, j); + pr_debug("%s: set INT%u_1 sample rate to %u\n", + __func__, j, sample_rate); + /* sample_rate is in Hz */ + if ((j == 0) && (sample_rate == 44100)) { + pr_info("%s: Cannot set 44.1KHz on INT0\n", + __func__); + } else + snd_soc_update_bits(codec, int_fs_reg, + 0x0F, int_prim_fs_rate_reg_val); + } + int_mux_cfg0 += 2; + } + } + + return 0; +} + + +static int tasha_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + int rate_val = 0; + int i, ret; + + /* set mixing path rate */ + for (i = 0; i < ARRAY_SIZE(int_mix_sample_rate_val); i++) { + if (sample_rate == + int_mix_sample_rate_val[i].sample_rate) { + rate_val = + int_mix_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_mix_sample_rate_val)) || + (rate_val < 0)) + goto prim_rate; + ret = tasha_set_mix_interpolator_rate(dai, + (u8) rate_val, sample_rate); +prim_rate: + /* set primary path sample rate */ + for (i = 0; i < ARRAY_SIZE(int_prim_sample_rate_val); i++) { + if (sample_rate == + int_prim_sample_rate_val[i].sample_rate) { + rate_val = + int_prim_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_prim_sample_rate_val)) || + (rate_val < 0)) + return -EINVAL; + ret = tasha_set_prim_interpolator_rate(dai, + (u8) rate_val, sample_rate); + return ret; +} + +static int tasha_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec); + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && + test_bit(SB_CLK_GEAR, &tasha->status_mask)) { + tasha_codec_vote_max_bw(dai->codec, false); + clear_bit(SB_CLK_GEAR, &tasha->status_mask); + } + return 0; +} + +static int tasha_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec); + int ret; + int tx_fs_rate = -EINVAL; + int rx_fs_rate = -EINVAL; + int i2s_bit_mode; + struct snd_soc_codec *codec = dai->codec; + + pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, + dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = tasha_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + pr_err("%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16: + tasha->dai[dai->id].bit_width = 16; + i2s_bit_mode = 0x01; + break; + case 24: + tasha->dai[dai->id].bit_width = 24; + i2s_bit_mode = 0x00; + break; + default: + return -EINVAL; + } + tasha->dai[dai->id].rate = params_rate(params); + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + switch (params_rate(params)) { + case 8000: + rx_fs_rate = 0; + break; + case 16000: + rx_fs_rate = 1; + break; + case 32000: + rx_fs_rate = 2; + break; + case 48000: + rx_fs_rate = 3; + break; + case 96000: + rx_fs_rate = 4; + break; + case 192000: + rx_fs_rate = 5; + break; + default: + dev_err(tasha->dev, + "%s: Invalid RX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + }; + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0x20, i2s_bit_mode << 5); + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0x1c, (rx_fs_rate << 2)); + } + 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(tasha->dev, "%s: Invalid TX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + + }; + if (dai->id != AIF4_VIFEED && + dai->id != AIF4_MAD_TX) { + ret = tasha_set_decimator_rate(dai, tx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(tasha->dev, "%s: cannot set TX Decimator rate: %d\n", + __func__, tx_fs_rate); + return ret; + } + } + tasha->dai[dai->id].rate = params_rate(params); + switch (params_width(params)) { + case 16: + tasha->dai[dai->id].bit_width = 16; + i2s_bit_mode = 0x01; + break; + case 24: + tasha->dai[dai->id].bit_width = 24; + i2s_bit_mode = 0x00; + break; + case 32: + tasha->dai[dai->id].bit_width = 32; + i2s_bit_mode = 0x00; + break; + default: + dev_err(tasha->dev, "%s: Invalid format 0x%x\n", + __func__, params_width(params)); + return -EINVAL; + }; + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0x20, i2s_bit_mode << 5); + if (tx_fs_rate > 1) + tx_fs_rate--; + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0x1c, tx_fs_rate << 2); + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG, + 0x05, 0x05); + + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG, + 0x05, 0x05); + + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG, + 0x05, 0x05); + + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG, + 0x05, 0x05); + } + break; + default: + pr_err("%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + }; + if (dai->id == AIF4_VIFEED) + tasha->dai[dai->id].bit_width = 32; + + return 0; +} + +static int tasha_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* CPU is master */ + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + if (dai->id == AIF1_CAP) + snd_soc_update_bits(dai->codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0x2, 0); + else if (dai->id == AIF1_PB) + snd_soc_update_bits(dai->codec, + WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0x2, 0); + } + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* CPU is slave */ + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + if (dai->id == AIF1_CAP) + snd_soc_update_bits(dai->codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0x2, 0x2); + else if (dai->id == AIF1_PB) + snd_soc_update_bits(dai->codec, + WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0x2, 0x2); + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int tasha_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static struct snd_soc_dai_ops tasha_dai_ops = { + .startup = tasha_startup, + .shutdown = tasha_shutdown, + .hw_params = tasha_hw_params, + .prepare = tasha_prepare, + .set_sysclk = tasha_set_dai_sysclk, + .set_fmt = tasha_set_dai_fmt, + .set_channel_map = tasha_set_channel_map, + .get_channel_map = tasha_get_channel_map, +}; + +static struct snd_soc_dai_driver tasha_dai[] = { + { + .name = "tasha_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_rx4", + .id = AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_mix_rx1", + .id = AIF_MIX1_PB, + .playback = { + .stream_name = "AIF Mix Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_mad1", + .id = AIF4_MAD_TX, + .capture = { + .stream_name = "AIF4 MAD TX", + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000, + .formats = TASHA_FORMATS_S16_S24_S32_LE, + .rate_min = 16000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_vifeedback", + .id = AIF4_VIFEED, + .capture = { + .stream_name = "VIfeed", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .formats = TASHA_FORMATS_S16_S24_S32_LE, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_cpe", + .id = AIF5_CPE_TX, + .capture = { + .stream_name = "AIF5 CPE TX", + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, + .formats = TASHA_FORMATS_S16_S24_S32_LE, + .rate_min = 16000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, + }, + }, +}; + +static struct snd_soc_dai_driver tasha_i2s_dai[] = { + { + .name = "tasha_i2s_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_i2s_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_i2s_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_i2s_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tasha_dai_ops, + }, +}; + +static void tasha_codec_power_gate_digital_core(struct tasha_priv *tasha) +{ + struct snd_soc_codec *codec = tasha->codec; + + if (!codec) + return; + + mutex_lock(&tasha->power_lock); + dev_dbg(codec->dev, "%s: Entering power gating function, %d\n", + __func__, tasha->power_active_ref); + + if (tasha->power_active_ref > 0) + goto exit; + + wcd9xxx_set_power_state(tasha->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_BEGIN, + WCD9XXX_DIG_CORE_REGION_1); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x02, 0x00); + clear_bit(AUDIO_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, sido_buck_svs_voltage); + wcd9xxx_set_power_state(tasha->wcd9xxx, WCD_REGION_POWER_DOWN, + WCD9XXX_DIG_CORE_REGION_1); +exit: + dev_dbg(codec->dev, "%s: Exiting power gating function, %d\n", + __func__, tasha->power_active_ref); + mutex_unlock(&tasha->power_lock); +} + +static void tasha_codec_power_gate_work(struct work_struct *work) +{ + struct tasha_priv *tasha; + struct delayed_work *dwork; + struct snd_soc_codec *codec; + + dwork = to_delayed_work(work); + tasha = container_of(dwork, struct tasha_priv, power_gate_work); + codec = tasha->codec; + + if (!codec) + return; + + tasha_codec_power_gate_digital_core(tasha); +} + +/* called under power_lock acquisition */ +static int tasha_dig_core_remove_power_collapse(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha_codec_vote_max_bw(codec, true); + snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5); + snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7); + snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_RST_CTL, 0x02, 0x00); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_RST_CTL, 0x02, 0x02); + + wcd9xxx_set_power_state(tasha->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + regcache_mark_dirty(codec->component.regmap); + regcache_sync_region(codec->component.regmap, + TASHA_DIG_CORE_REG_MIN, TASHA_DIG_CORE_REG_MAX); + tasha_codec_vote_max_bw(codec, false); + + return 0; +} + +static int tasha_dig_core_power_collapse(struct tasha_priv *tasha, + int req_state) +{ + struct snd_soc_codec *codec; + int cur_state; + + /* Exit if feature is disabled */ + if (!dig_core_collapse_enable) + return 0; + + mutex_lock(&tasha->power_lock); + if (req_state == POWER_COLLAPSE) + tasha->power_active_ref--; + else if (req_state == POWER_RESUME) + tasha->power_active_ref++; + else + goto unlock_mutex; + + if (tasha->power_active_ref < 0) { + dev_dbg(tasha->dev, "%s: power_active_ref is negative\n", + __func__); + goto unlock_mutex; + } + + codec = tasha->codec; + if (!codec) + goto unlock_mutex; + + if (req_state == POWER_COLLAPSE) { + if (tasha->power_active_ref == 0) { + schedule_delayed_work(&tasha->power_gate_work, + msecs_to_jiffies(dig_core_collapse_timer * 1000)); + } + } else if (req_state == POWER_RESUME) { + if (tasha->power_active_ref == 1) { + /* + * At this point, there can be two cases: + * 1. Core already in power collapse state + * 2. Timer kicked in and still did not expire or + * waiting for the power_lock + */ + cur_state = wcd9xxx_get_current_power_state( + tasha->wcd9xxx, + WCD9XXX_DIG_CORE_REGION_1); + if (cur_state == WCD_REGION_POWER_DOWN) + tasha_dig_core_remove_power_collapse(codec); + else { + mutex_unlock(&tasha->power_lock); + cancel_delayed_work_sync( + &tasha->power_gate_work); + mutex_lock(&tasha->power_lock); + } + } + } + +unlock_mutex: + mutex_unlock(&tasha->power_lock); + + return 0; +} + +static int __tasha_cdc_mclk_enable_locked(struct tasha_priv *tasha, + bool enable) +{ + int ret = 0; + + if (!tasha->wcd_ext_clk) { + dev_err(tasha->dev, "%s: wcd ext clock is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(tasha->dev, "%s: mclk_enable = %u\n", __func__, enable); + + if (enable) { + tasha_dig_core_power_collapse(tasha, POWER_RESUME); + ret = tasha_cdc_req_mclk_enable(tasha, true); + if (ret) + goto err; + + set_bit(AUDIO_NOMINAL, &tasha->status_mask); + tasha_codec_apply_sido_voltage(tasha, + SIDO_VOLTAGE_NOMINAL_MV); + } else { + if (!dig_core_collapse_enable) { + clear_bit(AUDIO_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, + sido_buck_svs_voltage); + } + tasha_cdc_req_mclk_enable(tasha, false); + tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE); + } + +err: + return ret; +} + +static int __tasha_cdc_mclk_enable(struct tasha_priv *tasha, + bool enable) +{ + int ret; + + WCD9XXX_V2_BG_CLK_LOCK(tasha->resmgr); + ret = __tasha_cdc_mclk_enable_locked(tasha, enable); + WCD9XXX_V2_BG_CLK_UNLOCK(tasha->resmgr); + + return ret; +} + +int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, bool dapm) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + return __tasha_cdc_mclk_enable(tasha, enable); +} +EXPORT_SYMBOL(tasha_cdc_mclk_enable); + +int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable, bool dapm) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(tasha->dev, "%s: clk_mode: %d, enable: %d, clk_internal: %d\n", + __func__, tasha->clk_mode, enable, tasha->clk_internal); + if (tasha->clk_mode || tasha->clk_internal) { + if (enable) { + tasha_cdc_sido_ccl_enable(tasha, true); + wcd_resmgr_enable_master_bias(tasha->resmgr); + tasha_dig_core_power_collapse(tasha, POWER_RESUME); + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + set_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, + SIDO_VOLTAGE_NOMINAL_MV); + tasha->clk_internal = true; + } else { + tasha->clk_internal = false; + clear_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, + sido_buck_svs_voltage); + tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE); + wcd_resmgr_disable_master_bias(tasha->resmgr); + tasha_cdc_sido_ccl_enable(tasha, false); + } + } else { + ret = __tasha_cdc_mclk_enable(tasha, enable); + } + return ret; +} +EXPORT_SYMBOL(tasha_cdc_mclk_tx_enable); + +static ssize_t tasha_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + struct tasha_priv *tasha; + struct wcd9xxx *wcd9xxx; + char buffer[TASHA_VERSION_ENTRY_SIZE]; + int len = 0; + + tasha = (struct tasha_priv *) entry->private_data; + if (!tasha) { + pr_err("%s: tasha priv is null\n", __func__); + return -EINVAL; + } + + wcd9xxx = tasha->wcd9xxx; + + if (wcd9xxx->codec_type->id_major == TASHA_MAJOR) { + if (TASHA_IS_1_0(wcd9xxx)) + len = snprintf(buffer, sizeof(buffer), "WCD9335_1_0\n"); + else if (TASHA_IS_1_1(wcd9xxx)) + len = snprintf(buffer, sizeof(buffer), "WCD9335_1_1\n"); + else + snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } else if (wcd9xxx->codec_type->id_major == TASHA2P0_MAJOR) { + len = snprintf(buffer, sizeof(buffer), "WCD9335_2_0\n"); + } else + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops tasha_codec_info_ops = { + .read = tasha_codec_version_read, +}; + +/* + * tasha_codec_info_create_codec_entry - creates wcd9335 module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates wcd9335 module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int tasha_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct tasha_priv *tasha; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + tasha = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + tasha->entry = snd_register_module_info(codec_root->module, + "tasha", + codec_root); + if (!tasha->entry) { + dev_dbg(codec->dev, "%s: failed to create wcd9335 entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + tasha->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create wcd9335 version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = tasha; + version_entry->size = TASHA_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &tasha_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + tasha->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(tasha_codec_info_create_codec_entry); + +static int __tasha_codec_internal_rco_ctrl( + struct snd_soc_codec *codec, bool enable) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (enable) { + tasha_cdc_sido_ccl_enable(tasha, true); + if (wcd_resmgr_get_clk_type(tasha->resmgr) == + WCD_CLK_RCO) { + ret = wcd_resmgr_enable_clk_block(tasha->resmgr, + WCD_CLK_RCO); + } else { + ret = tasha_cdc_req_mclk_enable(tasha, true); + ret |= wcd_resmgr_enable_clk_block(tasha->resmgr, + WCD_CLK_RCO); + ret |= tasha_cdc_req_mclk_enable(tasha, false); + } + + } else { + ret = wcd_resmgr_disable_clk_block(tasha->resmgr, + WCD_CLK_RCO); + tasha_cdc_sido_ccl_enable(tasha, false); + } + + if (ret) { + dev_err(codec->dev, "%s: Error in %s RCO\n", + __func__, (enable ? "enabling" : "disabling")); + ret = -EINVAL; + } + + return ret; +} + +/* + * tasha_codec_internal_rco_ctrl() + * Make sure that the caller does not acquire + * BG_CLK_LOCK. + */ +static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + WCD9XXX_V2_BG_CLK_LOCK(tasha->resmgr); + ret = __tasha_codec_internal_rco_ctrl(codec, enable); + WCD9XXX_V2_BG_CLK_UNLOCK(tasha->resmgr); + return ret; +} + +/* + * tasha_mbhc_hs_detect: starts mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + * @mbhc_cfg: handle to mbhc configuration structure + * return 0 if mbhc_start is success or error code in case of failure + */ +int tasha_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + return wcd_mbhc_start(&tasha->mbhc, mbhc_cfg); +} +EXPORT_SYMBOL(tasha_mbhc_hs_detect); + +/* + * tasha_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + */ +void tasha_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + wcd_mbhc_stop(&tasha->mbhc); +} +EXPORT_SYMBOL(tasha_mbhc_hs_detect_exit); + +static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} + +static const struct tasha_reg_mask_val tasha_reg_update_reset_val_1_1[] = { + {WCD9335_RCO_CTRL_2, 0xFF, 0x47}, + {WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0xFF, 0x60}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_val_1_1[] = { + {WCD9335_FLYBACK_VNEG_DAC_CTRL_1, 0xFF, 0x65}, + {WCD9335_FLYBACK_VNEG_DAC_CTRL_2, 0xFF, 0x52}, + {WCD9335_FLYBACK_VNEG_DAC_CTRL_3, 0xFF, 0xAF}, + {WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0xFF, 0x60}, + {WCD9335_FLYBACK_VNEG_CTRL_3, 0xFF, 0xF4}, + {WCD9335_FLYBACK_VNEG_CTRL_9, 0xFF, 0x40}, + {WCD9335_FLYBACK_VNEG_CTRL_2, 0xFF, 0x4F}, + {WCD9335_FLYBACK_EN, 0xFF, 0x6E}, + {WCD9335_CDC_RX2_RX_PATH_SEC0, 0xF8, 0xF8}, + {WCD9335_CDC_RX1_RX_PATH_SEC0, 0xF8, 0xF8}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_val_1_0[] = { + {WCD9335_FLYBACK_VNEG_CTRL_3, 0xFF, 0x54}, + {WCD9335_CDC_RX2_RX_PATH_SEC0, 0xFC, 0xFC}, + {WCD9335_CDC_RX1_RX_PATH_SEC0, 0xFC, 0xFC}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_val_2_0[] = { + {WCD9335_RCO_CTRL_2, 0x0F, 0x08}, + {WCD9335_RX_BIAS_FLYB_MID_RST, 0xF0, 0x10}, + {WCD9335_FLYBACK_CTRL_1, 0x20, 0x20}, + {WCD9335_HPH_OCP_CTL, 0xFF, 0x7A}, + {WCD9335_HPH_L_TEST, 0x01, 0x01}, + {WCD9335_HPH_R_TEST, 0x01, 0x01}, + {WCD9335_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12}, + {WCD9335_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x1E, 0x18}, + {WCD9335_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12}, + {WCD9335_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x1E, 0x18}, + {WCD9335_CDC_TX0_TX_PATH_SEC7, 0xFF, 0x45}, + {WCD9335_CDC_RX0_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD9335_HPH_REFBUFF_LP_CTL, 0x08, 0x08}, + {WCD9335_HPH_REFBUFF_LP_CTL, 0x06, 0x02}, + {WCD9335_DIFF_LO_CORE_OUT_PROG, 0xFC, 0xA0}, + {WCD9335_SE_LO_COM1, 0xFF, 0xC0}, + {WCD9335_CDC_RX3_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD9335_CDC_RX4_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD9335_CDC_RX5_RX_PATH_SEC0, 0xFC, 0xF8}, + {WCD9335_CDC_RX6_RX_PATH_SEC0, 0xFC, 0xF8}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_defaults[] = { + {WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x00}, + {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x01}, + {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x04, 0x04}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_i2c_defaults[] = { + {WCD9335_ANA_CLK_TOP, 0x20, 0x20}, + {WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x01}, + {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x00}, + {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x05, 0x05}, + {WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG, 0x01, 0x01}, + {WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG, 0x01, 0x01}, + {WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG, 0x01, 0x01}, + {WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG, 0x01, 0x01}, + {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG, 0x05, 0x05}, + {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG, 0x05, 0x05}, + {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG, 0x05, 0x05}, + {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG, 0x05, 0x05}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_common_val[] = { + /* Rbuckfly/R_EAR(32) */ + {WCD9335_CDC_CLSH_K2_MSB, 0x0F, 0x00}, + {WCD9335_CDC_CLSH_K2_LSB, 0xFF, 0x60}, + {WCD9335_CPE_SS_DMIC_CFG, 0x80, 0x00}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x70, 0x50}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x70, 0x50}, + {WCD9335_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08}, + {WCD9335_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08}, + {WCD9335_ANA_LO_1_2, 0x3C, 0X3C}, + {WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x70, 0x00}, + {WCD9335_SOC_MAD_AUDIO_CTL_2, 0x03, 0x03}, + {WCD9335_CDC_TOP_TOP_CFG1, 0x02, 0x02}, + {WCD9335_CDC_TOP_TOP_CFG1, 0x01, 0x01}, + {WCD9335_EAR_CMBUFF, 0x08, 0x00}, + {WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD9335_CDC_RX0_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX1_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX2_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX3_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX4_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX5_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX6_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX7_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX8_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_VBADC_IBIAS_FE, 0x0C, 0x08}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_1_x_val[] = { + /* Enable TX HPF Filter & Linear Phase */ + {WCD9335_CDC_TX0_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX1_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX2_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX3_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX4_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX5_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX6_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX7_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX8_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_RX0_RX_PATH_SEC0, 0xF8, 0xF8}, + {WCD9335_CDC_RX0_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX1_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX2_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX3_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX4_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX5_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX6_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX7_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX8_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX0_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX1_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX2_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX3_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX4_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX5_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX6_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_TX0_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX1_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX2_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX3_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX4_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX5_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX6_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX7_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX8_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_RX3_RX_PATH_SEC0, 0xF8, 0xF0}, + {WCD9335_CDC_RX4_RX_PATH_SEC0, 0xF8, 0xF0}, + {WCD9335_CDC_RX5_RX_PATH_SEC0, 0xF8, 0xF8}, + {WCD9335_CDC_RX6_RX_PATH_SEC0, 0xF8, 0xF8}, + {WCD9335_RX_OCP_COUNT, 0xFF, 0xFF}, + {WCD9335_HPH_OCP_CTL, 0xF0, 0x70}, + {WCD9335_CPE_SS_CPAR_CFG, 0xFF, 0x00}, + {WCD9335_FLYBACK_VNEG_CTRL_1, 0xFF, 0x63}, + {WCD9335_FLYBACK_VNEG_CTRL_4, 0xFF, 0x7F}, + {WCD9335_CLASSH_CTRL_VCL_1, 0xFF, 0x60}, + {WCD9335_CLASSH_CTRL_CCL_5, 0xFF, 0x40}, + {WCD9335_RX_TIMER_DIV, 0xFF, 0x32}, + {WCD9335_SE_LO_COM2, 0xFF, 0x01}, + {WCD9335_MBHC_ZDET_ANA_CTL, 0x0F, 0x07}, + {WCD9335_RX_BIAS_HPH_PA, 0xF0, 0x60}, + {WCD9335_HPH_RDAC_LDO_CTL, 0x88, 0x88}, + {WCD9335_HPH_L_EN, 0x20, 0x20}, + {WCD9335_HPH_R_EN, 0x20, 0x20}, + {WCD9335_DIFF_LO_CORE_OUT_PROG, 0xFC, 0xD8}, + {WCD9335_CDC_RX5_RX_PATH_SEC3, 0xBD, 0xBD}, + {WCD9335_CDC_RX6_RX_PATH_SEC3, 0xBD, 0xBD}, + {WCD9335_DIFF_LO_COM_PA_FREQ, 0x70, 0x40}, +}; + +static void tasha_update_reg_reset_values(struct snd_soc_codec *codec) +{ + u32 i; + struct wcd9xxx *tasha_core = dev_get_drvdata(codec->dev->parent); + + if (TASHA_IS_1_1(tasha_core)) { + for (i = 0; i < ARRAY_SIZE(tasha_reg_update_reset_val_1_1); + i++) + snd_soc_write(codec, + tasha_reg_update_reset_val_1_1[i].reg, + tasha_reg_update_reset_val_1_1[i].val); + } +} + +static void tasha_codec_init_reg(struct snd_soc_codec *codec) +{ + u32 i; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_common_val); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_common_val[i].reg, + tasha_codec_reg_init_common_val[i].mask, + tasha_codec_reg_init_common_val[i].val); + + if (TASHA_IS_1_1(wcd9xxx) || + TASHA_IS_1_0(wcd9xxx)) + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_1_x_val); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_1_x_val[i].reg, + tasha_codec_reg_init_1_x_val[i].mask, + tasha_codec_reg_init_1_x_val[i].val); + + if (TASHA_IS_1_1(wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_1); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_val_1_1[i].reg, + tasha_codec_reg_init_val_1_1[i].mask, + tasha_codec_reg_init_val_1_1[i].val); + } else if (TASHA_IS_1_0(wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_0); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_val_1_0[i].reg, + tasha_codec_reg_init_val_1_0[i].mask, + tasha_codec_reg_init_val_1_0[i].val); + } else if (TASHA_IS_2_0(wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_2_0); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_val_2_0[i].reg, + tasha_codec_reg_init_val_2_0[i].mask, + tasha_codec_reg_init_val_2_0[i].val); + } +} + +static void tasha_update_reg_defaults(struct tasha_priv *tasha) +{ + u32 i; + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tasha->wcd9xxx; + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_defaults); i++) + regmap_update_bits(wcd9xxx->regmap, + tasha_codec_reg_defaults[i].reg, + tasha_codec_reg_defaults[i].mask, + tasha_codec_reg_defaults[i].val); + + tasha->intf_type = wcd9xxx_get_intf_type(); + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_i2c_defaults); i++) + regmap_update_bits(wcd9xxx->regmap, + tasha_codec_reg_i2c_defaults[i].reg, + tasha_codec_reg_i2c_defaults[i].mask, + tasha_codec_reg_i2c_defaults[i].val); + +} + +static void tasha_slim_interface_init_reg(struct snd_soc_codec *codec) +{ + int i; + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) + wcd9xxx_interface_reg_write(priv->wcd9xxx, + TASHA_SLIM_PGD_PORT_INT_EN0 + i, + 0xFF); +} + +static irqreturn_t tasha_slimbus_irq(int irq, void *data) +{ + struct tasha_priv *priv = data; + unsigned long status = 0; + int i, j, port_id, k; + u32 bit; + u8 val, int_val = 0; + bool tx, cleared; + unsigned short reg = 0; + + for (i = TASHA_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0; + i <= TASHA_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) { + val = wcd9xxx_interface_reg_read(priv->wcd9xxx, i); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = (j >= 16 ? true : false); + port_id = (tx ? j - 16 : j); + val = wcd9xxx_interface_reg_read(priv->wcd9xxx, + TASHA_SLIM_PGD_PORT_INT_RX_SOURCE0 + j); + if (val) { + if (!tx) + reg = TASHA_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + priv->wcd9xxx, reg); + /* + * Ignore interrupts for ports for which the + * interrupts are not specifically enabled. + */ + if (!(int_val & (1 << (port_id % 8)))) + continue; + } + if (val & TASHA_SLIM_IRQ_OVERFLOW) + pr_err_ratelimited( + "%s: overflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if (val & TASHA_SLIM_IRQ_UNDERFLOW) + pr_err_ratelimited( + "%s: underflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if ((val & TASHA_SLIM_IRQ_OVERFLOW) || + (val & TASHA_SLIM_IRQ_UNDERFLOW)) { + if (!tx) + reg = TASHA_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + priv->wcd9xxx, reg); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + wcd9xxx_interface_reg_write(priv->wcd9xxx, + reg, int_val); + } + } + if (val & TASHA_SLIM_IRQ_PORT_CLOSED) { + /* + * INT SOURCE register starts from RX to TX + * but port number in the ch_mask is in opposite way + */ + bit = (tx ? j - 16 : j + 16); + pr_debug("%s: %s port %d closed value %x, bit %u\n", + __func__, (tx ? "TX" : "RX"), port_id, val, + bit); + for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) { + pr_debug("%s: priv->dai[%d].ch_mask = 0x%lx\n", + __func__, k, priv->dai[k].ch_mask); + if (test_and_clear_bit(bit, + &priv->dai[k].ch_mask)) { + cleared = true; + if (!priv->dai[k].ch_mask) + wake_up(&priv->dai[k].dai_wait); + /* + * There are cases when multiple DAIs + * might be using the same slimbus + * channel. Hence don't break here. + */ + } + } + WARN(!cleared, + "Couldn't find slimbus %s port %d for closing\n", + (tx ? "TX" : "RX"), port_id); + } + wcd9xxx_interface_reg_write(priv->wcd9xxx, + TASHA_SLIM_PGD_PORT_INT_CLR_RX_0 + + (j / 8), + 1 << (j % 8)); + } + + return IRQ_HANDLED; +} + +static int tasha_setup_irqs(struct tasha_priv *tasha) +{ + int ret = 0; + struct snd_soc_codec *codec = tasha->codec; + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS, + tasha_slimbus_irq, "SLIMBUS Slave", tasha); + if (ret) + pr_err("%s: Failed to request irq %d\n", __func__, + WCD9XXX_IRQ_SLIMBUS); + else + tasha_slim_interface_init_reg(codec); + + return ret; +} + +static void tasha_init_slim_slave_cfg(struct snd_soc_codec *codec) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + struct afe_param_cdc_slimbus_slave_cfg *cfg; + struct wcd9xxx *wcd9xxx = priv->wcd9xxx; + uint64_t eaddr = 0; + + cfg = &priv->slimbus_slave_cfg; + cfg->minor_version = 1; + cfg->tx_slave_port_offset = 0; + cfg->rx_slave_port_offset = 16; + + memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr)); + WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6); + cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF; + cfg->device_enum_addr_msw = eaddr >> 32; + + dev_dbg(codec->dev, "%s: slimbus logical address 0x%llx\n", + __func__, eaddr); +} + +static void tasha_cleanup_irqs(struct tasha_priv *tasha) +{ + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tasha); +} + +static int tasha_handle_pdata(struct tasha_priv *tasha, + struct wcd9xxx_pdata *pdata) +{ + struct snd_soc_codec *codec = tasha->codec; + u8 dmic_ctl_val, mad_dmic_ctl_val; + u8 anc_ctl_value; + u32 def_dmic_rate, dmic_clk_drv; + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + int rc = 0; + + if (!pdata) { + dev_err(codec->dev, "%s: NULL pdata\n", __func__); + return -ENODEV; + } + + /* set micbias voltage */ + vout_ctl_1 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb1_mv); + vout_ctl_2 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb2_mv); + vout_ctl_3 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb3_mv); + vout_ctl_4 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb4_mv); + + if (IS_ERR_VALUE(vout_ctl_1) || IS_ERR_VALUE(vout_ctl_2) || + IS_ERR_VALUE(vout_ctl_3) || IS_ERR_VALUE(vout_ctl_4)) { + rc = -EINVAL; + goto done; + } + snd_soc_update_bits(codec, WCD9335_ANA_MICB1, 0x3F, vout_ctl_1); + snd_soc_update_bits(codec, WCD9335_ANA_MICB2, 0x3F, vout_ctl_2); + snd_soc_update_bits(codec, WCD9335_ANA_MICB3, 0x3F, vout_ctl_3); + snd_soc_update_bits(codec, WCD9335_ANA_MICB4, 0x3F, vout_ctl_4); + + /* Set the DMIC sample rate */ + switch (pdata->mclk_rate) { + case TASHA_MCLK_CLK_9P6MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + break; + case TASHA_MCLK_CLK_12P288MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ; + break; + default: + /* should never happen */ + dev_err(codec->dev, "%s: Invalid mclk_rate %d\n", + __func__, pdata->mclk_rate); + rc = -EINVAL; + goto done; + }; + + if (pdata->dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + pdata->dmic_sample_rate = def_dmic_rate; + } + if (pdata->mad_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: mad_dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + /* + * use dmic_sample_rate as the default for MAD + * if mad dmic sample rate is undefined + */ + pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate; + } + if (pdata->ecpp_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, + "%s: ecpp_dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + /* + * use dmic_sample_rate as the default for ECPP DMIC + * if ecpp dmic sample rate is undefined + */ + pdata->ecpp_dmic_sample_rate = pdata->dmic_sample_rate; + } + + if (pdata->dmic_clk_drv == + WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) { + pdata->dmic_clk_drv = WCD9335_DMIC_CLK_DRIVE_DEFAULT; + dev_info(codec->dev, + "%s: dmic_clk_strength invalid, default = %d\n", + __func__, pdata->dmic_clk_drv); + } + + switch (pdata->dmic_clk_drv) { + case 2: + dmic_clk_drv = 0; + break; + case 4: + dmic_clk_drv = 1; + break; + case 8: + dmic_clk_drv = 2; + break; + case 16: + dmic_clk_drv = 3; + break; + default: + dev_err(codec->dev, + "%s: invalid dmic_clk_drv %d, using default\n", + __func__, pdata->dmic_clk_drv); + dmic_clk_drv = 0; + break; + } + + snd_soc_update_bits(codec, WCD9335_TEST_DEBUG_PAD_DRVCTL, + 0x0C, dmic_clk_drv << 2); + + /* + * Default the DMIC clk rates to mad_dmic_sample_rate, + * whereas, the anc/txfe dmic rates to dmic_sample_rate + * since the anc/txfe are independent of mad block. + */ + mad_dmic_ctl_val = tasha_get_dmic_clk_val(tasha->codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC0_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC1_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC2_CTL, + 0x0E, mad_dmic_ctl_val << 1); + + dmic_ctl_val = tasha_get_dmic_clk_val(tasha->codec, + pdata->mclk_rate, + pdata->dmic_sample_rate); + + if (dmic_ctl_val == WCD9335_DMIC_CLK_DIV_2) + anc_ctl_value = WCD9335_ANC_DMIC_X2_FULL_RATE; + else + anc_ctl_value = WCD9335_ANC_DMIC_X2_HALF_RATE; + + snd_soc_update_bits(codec, WCD9335_CDC_ANC0_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD9335_CDC_ANC0_MODE_2_CTL, + 0x20, anc_ctl_value << 5); + snd_soc_update_bits(codec, WCD9335_CDC_ANC1_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD9335_CDC_ANC1_MODE_2_CTL, + 0x20, anc_ctl_value << 5); +done: + return rc; +} + +static struct wcd_cpe_core *tasha_codec_get_cpe_core( + struct snd_soc_codec *codec) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + return priv->cpe_core; +} + +static int tasha_codec_cpe_fll_update_divider( + struct snd_soc_codec *codec, u32 cpe_fll_rate) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + u32 div_val = 0, l_val = 0; + u32 computed_cpe_fll; + + if (cpe_fll_rate != CPE_FLL_CLK_75MHZ && + cpe_fll_rate != CPE_FLL_CLK_150MHZ) { + dev_err(codec->dev, + "%s: Invalid CPE fll rate request %u\n", + __func__, cpe_fll_rate); + return -EINVAL; + } + + if (wcd9xxx->mclk_rate == TASHA_MCLK_CLK_12P288MHZ) { + /* update divider to 10 and enable 5x divider */ + snd_soc_write(codec, WCD9335_CPE_FLL_USER_CTL_1, + 0x55); + div_val = 10; + } else if (wcd9xxx->mclk_rate == TASHA_MCLK_CLK_9P6MHZ) { + /* update divider to 8 and enable 2x divider */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0, + 0x7C, 0x70); + snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_1, + 0xE0, 0x20); + div_val = 8; + } else { + dev_err(codec->dev, + "%s: Invalid MCLK rate %u\n", + __func__, wcd9xxx->mclk_rate); + return -EINVAL; + } + + l_val = ((cpe_fll_rate / 1000) * div_val) / + (wcd9xxx->mclk_rate / 1000); + + /* If l_val was integer truncated, increment l_val once */ + computed_cpe_fll = (wcd9xxx->mclk_rate / div_val) * l_val; + if (computed_cpe_fll < cpe_fll_rate) + l_val++; + + + /* update L value LSB and MSB */ + snd_soc_write(codec, WCD9335_CPE_FLL_L_VAL_CTL_0, + (l_val & 0xFF)); + snd_soc_write(codec, WCD9335_CPE_FLL_L_VAL_CTL_1, + ((l_val >> 8) & 0xFF)); + + tasha->current_cpe_clk_freq = cpe_fll_rate; + dev_dbg(codec->dev, + "%s: updated l_val to %u for cpe_clk %u and mclk %u\n", + __func__, l_val, cpe_fll_rate, wcd9xxx->mclk_rate); + + return 0; +} + +static int __tasha_cdc_change_cpe_clk(struct snd_soc_codec *codec, + u32 clk_freq) +{ + int ret = 0; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (!tasha_cdc_is_svs_enabled(tasha)) { + dev_dbg(codec->dev, + "%s: SVS not enabled or tasha is not 2p0, return\n", + __func__); + return 0; + } + dev_dbg(codec->dev, "%s: clk_freq = %u\n", __func__, clk_freq); + + if (clk_freq == CPE_FLL_CLK_75MHZ) { + /* Change to SVS */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x08, 0x08); + if (tasha_codec_cpe_fll_update_divider(codec, clk_freq)) { + ret = -EINVAL; + goto done; + } + + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x10, 0x10); + + clear_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, sido_buck_svs_voltage); + + } else if (clk_freq == CPE_FLL_CLK_150MHZ) { + /* change to nominal */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x08, 0x08); + + set_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, SIDO_VOLTAGE_NOMINAL_MV); + + if (tasha_codec_cpe_fll_update_divider(codec, clk_freq)) { + ret = -EINVAL; + goto done; + } + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x10, 0x10); + } else { + dev_err(codec->dev, + "%s: Invalid clk_freq request %d for CPE FLL\n", + __func__, clk_freq); + ret = -EINVAL; + } + +done: + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x10, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x08, 0x00); + return ret; +} + + +static int tasha_codec_cpe_fll_enable(struct snd_soc_codec *codec, + bool enable) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u8 clk_sel_reg_val = 0x00; + + dev_dbg(codec->dev, "%s: enable = %s\n", + __func__, enable ? "true" : "false"); + + if (enable) { + if (tasha_cdc_is_svs_enabled(tasha)) { + /* FLL enable is always at SVS */ + if (__tasha_cdc_change_cpe_clk(codec, + CPE_FLL_CLK_75MHZ)) { + dev_err(codec->dev, + "%s: clk change to %d failed\n", + __func__, CPE_FLL_CLK_75MHZ); + return -EINVAL; + } + } else { + if (tasha_codec_cpe_fll_update_divider(codec, + CPE_FLL_CLK_75MHZ)) { + dev_err(codec->dev, + "%s: clk change to %d failed\n", + __func__, CPE_FLL_CLK_75MHZ); + return -EINVAL; + } + } + + if (TASHA_IS_1_0(wcd9xxx)) { + tasha_cdc_mclk_enable(codec, true, false); + clk_sel_reg_val = 0x02; + } + + /* Setup CPE reference clk */ + snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP, + 0x02, clk_sel_reg_val); + + /* enable CPE FLL reference clk */ + snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP, + 0x01, 0x01); + + /* program the PLL */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0, + 0x01, 0x01); + + /* TEST clk setting */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_TEST_CTL_0, + 0x80, 0x80); + /* set FLL mode to HW controlled */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x60, 0x00); + snd_soc_write(codec, WCD9335_CPE_FLL_FLL_MODE, 0x80); + } else { + /* disable CPE FLL reference clk */ + snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP, + 0x01, 0x00); + /* undo TEST clk setting */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_TEST_CTL_0, + 0x80, 0x00); + /* undo FLL mode to HW control */ + snd_soc_write(codec, WCD9335_CPE_FLL_FLL_MODE, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x60, 0x20); + /* undo the PLL */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0, + 0x01, 0x00); + + if (TASHA_IS_1_0(wcd9xxx)) + tasha_cdc_mclk_enable(codec, false, false); + + /* + * FLL could get disabled while at nominal, + * scale it back to SVS + */ + if (tasha_cdc_is_svs_enabled(tasha)) + __tasha_cdc_change_cpe_clk(codec, + CPE_FLL_CLK_75MHZ); + } + + return 0; + +} + +static void tasha_cdc_query_cpe_clk_plan(void *data, + struct cpe_svc_cfg_clk_plan *clk_freq) +{ + struct snd_soc_codec *codec = data; + struct tasha_priv *tasha; + u32 cpe_clk_khz; + + if (!codec) { + pr_err("%s: Invalid codec handle\n", + __func__); + return; + } + + tasha = snd_soc_codec_get_drvdata(codec); + cpe_clk_khz = tasha->current_cpe_clk_freq / 1000; + + dev_dbg(codec->dev, + "%s: current_clk_freq = %u\n", + __func__, tasha->current_cpe_clk_freq); + + clk_freq->current_clk_feq = cpe_clk_khz; + clk_freq->num_clk_freqs = 2; + + if (tasha_cdc_is_svs_enabled(tasha)) { + clk_freq->clk_freqs[0] = CPE_FLL_CLK_75MHZ / 1000; + clk_freq->clk_freqs[1] = CPE_FLL_CLK_150MHZ / 1000; + } else { + clk_freq->clk_freqs[0] = CPE_FLL_CLK_75MHZ; + clk_freq->clk_freqs[1] = CPE_FLL_CLK_150MHZ; + } +} + +static void tasha_cdc_change_cpe_clk(void *data, + u32 clk_freq) +{ + struct snd_soc_codec *codec = data; + struct tasha_priv *tasha; + u32 cpe_clk_khz, req_freq = 0; + + if (!codec) { + pr_err("%s: Invalid codec handle\n", + __func__); + return; + } + + tasha = snd_soc_codec_get_drvdata(codec); + cpe_clk_khz = tasha->current_cpe_clk_freq / 1000; + + if (tasha_cdc_is_svs_enabled(tasha)) { + if ((clk_freq * 1000) <= CPE_FLL_CLK_75MHZ) + req_freq = CPE_FLL_CLK_75MHZ; + else + req_freq = CPE_FLL_CLK_150MHZ; + } + + dev_dbg(codec->dev, + "%s: requested clk_freq = %u, current clk_freq = %u\n", + __func__, clk_freq * 1000, + tasha->current_cpe_clk_freq); + + if (tasha_cdc_is_svs_enabled(tasha)) { + if (__tasha_cdc_change_cpe_clk(codec, req_freq)) + dev_err(codec->dev, + "%s: clock/voltage scaling failed\n", + __func__); + } +} + +static int tasha_codec_slim_reserve_bw(struct snd_soc_codec *codec, + u32 bw_ops, bool commit) +{ + struct wcd9xxx *wcd9xxx; + + if (!codec) { + pr_err("%s: Invalid handle to codec\n", + __func__); + return -EINVAL; + } + + wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!wcd9xxx) { + dev_err(codec->dev, "%s: Invalid parent drv_data\n", + __func__); + return -EINVAL; + } + + return wcd9xxx_slim_reserve_bw(wcd9xxx, bw_ops, commit); +} + +static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec, + bool vote) +{ + u32 bw_ops; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + return 0; + + if (vote) + bw_ops = SLIM_BW_CLK_GEAR_9; + else + bw_ops = SLIM_BW_UNVOTE; + + return tasha_codec_slim_reserve_bw(codec, + bw_ops, true); +} + +static int tasha_cpe_err_irq_control(struct snd_soc_codec *codec, + enum cpe_err_irq_cntl_type cntl_type, u8 *status) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u8 irq_bits; + + if (TASHA_IS_2_0(tasha->wcd9xxx)) + irq_bits = 0xFF; + else + irq_bits = 0x3F; + + if (status) + irq_bits = (*status) & irq_bits; + + switch (cntl_type) { + case CPE_ERR_IRQ_MASK: + snd_soc_update_bits(codec, + WCD9335_CPE_SS_SS_ERROR_INT_MASK, + irq_bits, irq_bits); + break; + case CPE_ERR_IRQ_UNMASK: + snd_soc_update_bits(codec, + WCD9335_CPE_SS_SS_ERROR_INT_MASK, + irq_bits, 0x00); + break; + case CPE_ERR_IRQ_CLEAR: + snd_soc_write(codec, WCD9335_CPE_SS_SS_ERROR_INT_CLEAR, + irq_bits); + break; + case CPE_ERR_IRQ_STATUS: + if (!status) + return -EINVAL; + *status = snd_soc_read(codec, + WCD9335_CPE_SS_SS_ERROR_INT_STATUS); + break; + } + + return 0; +} + +static const struct wcd_cpe_cdc_cb cpe_cb = { + .cdc_clk_en = tasha_codec_internal_rco_ctrl, + .cpe_clk_en = tasha_codec_cpe_fll_enable, + .get_afe_out_port_id = tasha_codec_get_mad_port_id, + .lab_cdc_ch_ctl = tasha_codec_enable_slimtx_mad, + .cdc_ext_clk = tasha_cdc_mclk_enable, + .bus_vote_bw = tasha_codec_vote_max_bw, + .cpe_err_irq_control = tasha_cpe_err_irq_control, +}; + +static struct cpe_svc_init_param cpe_svc_params = { + .version = CPE_SVC_INIT_PARAM_V1, + .query_freq_plans_cb = tasha_cdc_query_cpe_clk_plan, + .change_freq_plan_cb = tasha_cdc_change_cpe_clk, +}; + +static int tasha_cpe_initialize(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd_cpe_params cpe_params; + + memset(&cpe_params, 0, + sizeof(struct wcd_cpe_params)); + cpe_params.codec = codec; + cpe_params.get_cpe_core = tasha_codec_get_cpe_core; + cpe_params.cdc_cb = &cpe_cb; + cpe_params.dbg_mode = cpe_debug_mode; + cpe_params.cdc_major_ver = CPE_SVC_CODEC_WCD9335; + cpe_params.cdc_minor_ver = CPE_SVC_CODEC_V1P0; + cpe_params.cdc_id = CPE_SVC_CODEC_WCD9335; + + cpe_params.cdc_irq_info.cpe_engine_irq = + WCD9335_IRQ_SVA_OUTBOX1; + cpe_params.cdc_irq_info.cpe_err_irq = + WCD9335_IRQ_SVA_ERROR; + cpe_params.cdc_irq_info.cpe_fatal_irqs = + TASHA_CPE_FATAL_IRQS; + + cpe_svc_params.context = codec; + cpe_params.cpe_svc_params = &cpe_svc_params; + + tasha->cpe_core = wcd_cpe_init("cpe_9335", codec, + &cpe_params); + if (IS_ERR_OR_NULL(tasha->cpe_core)) { + dev_err(codec->dev, + "%s: Failed to enable CPE\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static const struct wcd_resmgr_cb tasha_resmgr_cb = { + .cdc_rco_ctrl = __tasha_codec_internal_rco_ctrl, +}; + +static int tasha_device_down(struct wcd9xxx *wcd9xxx) +{ + struct snd_soc_codec *codec; + struct tasha_priv *priv; + int count; + int i = 0; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + priv = snd_soc_codec_get_drvdata(codec); + wcd_cpe_ssr_event(priv->cpe_core, WCD_CPE_BUS_DOWN_EVENT); + for (i = 0; i < priv->nr; i++) + swrm_wcd_notify(priv->swr_ctrl_data[i].swr_pdev, + SWR_DEVICE_DOWN, NULL); + snd_soc_card_change_online_state(codec->component.card, 0); + for (count = 0; count < NUM_CODEC_DAIS; count++) + priv->dai[count].bus_down_in_recovery = true; + + priv->resmgr->sido_input_src = SIDO_SOURCE_INTERNAL; + + return 0; +} + +static int tasha_post_reset_cb(struct wcd9xxx *wcd9xxx) +{ + int i, ret = 0; + struct wcd9xxx *control; + struct snd_soc_codec *codec; + struct tasha_priv *tasha; + struct wcd9xxx_pdata *pdata; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + tasha = snd_soc_codec_get_drvdata(codec); + control = dev_get_drvdata(codec->dev->parent); + + wcd9xxx_set_power_state(tasha->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + + mutex_lock(&tasha->codec_mutex); + + tasha_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tasha_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tasha_init_slim_slave_cfg(codec); + if (tasha->machine_codec_event_cb) + tasha->machine_codec_event_cb(codec, + WCD9335_CODEC_EVENT_CODEC_UP); + snd_soc_card_change_online_state(codec->component.card, 1); + + /* Class-H Init*/ + wcd_clsh_init(&tasha->clsh_d); + /* Default HPH Mode to Class-H HiFi */ + tasha->hph_mode = CLS_H_HIFI; + + for (i = 0; i < TASHA_MAX_MICBIAS; i++) + tasha->micb_ref[i] = 0; + + tasha_update_reg_defaults(tasha); + + tasha->codec = codec; + for (i = 0; i < COMPANDER_MAX; i++) + tasha->comp_enabled[i] = 0; + + dev_dbg(codec->dev, "%s: MCLK Rate = %x\n", + __func__, control->mclk_rate); + + if (control->mclk_rate == TASHA_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (control->mclk_rate == TASHA_MCLK_CLK_9P6MHZ) + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + tasha_codec_init_reg(codec); + + wcd_resmgr_post_ssr_v2(tasha->resmgr); + + tasha_enable_efuse_sensing(codec); + + regcache_mark_dirty(codec->component.regmap); + regcache_sync(codec->component.regmap); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tasha_handle_pdata(tasha, pdata); + if (IS_ERR_VALUE(ret)) + dev_err(codec->dev, "%s: invalid pdata\n", __func__); + + /* MBHC Init */ + wcd_mbhc_deinit(&tasha->mbhc); + tasha->mbhc_started = false; + + /* Initialize MBHC module */ + ret = wcd_mbhc_init(&tasha->mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, TASHA_ZDET_SUPPORTED); + if (ret) + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + else + tasha_mbhc_hs_detect(codec, tasha->mbhc.mbhc_cfg); + + tasha_cleanup_irqs(tasha); + ret = tasha_setup_irqs(tasha); + if (ret) { + dev_err(codec->dev, "%s: tasha irq setup failed %d\n", + __func__, ret); + goto err; + } + + tasha_set_spkr_mode(codec, tasha->spkr_mode); + wcd_cpe_ssr_event(tasha->cpe_core, WCD_CPE_BUS_UP_EVENT); + +err: + mutex_unlock(&tasha->codec_mutex); + return ret; +} + +static struct regulator *tasha_codec_find_ondemand_regulator( + struct snd_soc_codec *codec, const char *name) +{ + int i; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + + for (i = 0; i < wcd9xxx->num_of_supplies; ++i) { + if (pdata->regulator[i].ondemand && + wcd9xxx->supplies[i].supply && + !strcmp(wcd9xxx->supplies[i].supply, name)) + return wcd9xxx->supplies[i].consumer; + } + + dev_dbg(tasha->dev, "Warning: regulator not found:%s\n", + name); + return NULL; +} + +static int tasha_codec_probe(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tasha_priv *tasha; + struct wcd9xxx_pdata *pdata; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int i, ret; + void *ptr = NULL; + struct regulator *supply; + + control = dev_get_drvdata(codec->dev->parent); + + dev_info(codec->dev, "%s()\n", __func__); + tasha = snd_soc_codec_get_drvdata(codec); + tasha->intf_type = wcd9xxx_get_intf_type(); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + control->dev_down = tasha_device_down; + control->post_reset = tasha_post_reset_cb; + control->ssr_priv = (void *)codec; + } + + /* Resource Manager post Init */ + ret = wcd_resmgr_post_init(tasha->resmgr, &tasha_resmgr_cb, codec); + if (ret) { + dev_err(codec->dev, "%s: wcd resmgr post init failed\n", + __func__); + goto err; + } + /* Class-H Init*/ + wcd_clsh_init(&tasha->clsh_d); + /* Default HPH Mode to Class-H HiFi */ + tasha->hph_mode = CLS_H_HIFI; + + tasha->codec = codec; + for (i = 0; i < COMPANDER_MAX; i++) + tasha->comp_enabled[i] = 0; + + tasha->spkr_gain_offset = RX_GAIN_OFFSET_0_DB; + tasha->intf_type = wcd9xxx_get_intf_type(); + tasha_update_reg_reset_values(codec); + pr_debug("%s: MCLK Rate = %x\n", __func__, control->mclk_rate); + if (control->mclk_rate == TASHA_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (control->mclk_rate == TASHA_MCLK_CLK_9P6MHZ) + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + tasha_codec_init_reg(codec); + + tasha_enable_efuse_sensing(codec); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tasha_handle_pdata(tasha, pdata); + if (IS_ERR_VALUE(ret)) { + pr_err("%s: bad pdata\n", __func__); + goto err; + } + + supply = tasha_codec_find_ondemand_regulator(codec, + on_demand_supply_name[ON_DEMAND_MICBIAS]); + if (supply) { + tasha->on_demand_list[ON_DEMAND_MICBIAS].supply = supply; + tasha->on_demand_list[ON_DEMAND_MICBIAS].ondemand_supply_count = + 0; + } + + tasha->fw_data = devm_kzalloc(codec->dev, + sizeof(*(tasha->fw_data)), GFP_KERNEL); + if (!tasha->fw_data) + goto err; + set_bit(WCD9XXX_ANC_CAL, tasha->fw_data->cal_bit); + set_bit(WCD9XXX_MBHC_CAL, tasha->fw_data->cal_bit); + set_bit(WCD9XXX_MAD_CAL, tasha->fw_data->cal_bit); + set_bit(WCD9XXX_VBAT_CAL, tasha->fw_data->cal_bit); + + ret = wcd_cal_create_hwdep(tasha->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + goto err_hwdep; + } + + /* Initialize MBHC module */ + if (TASHA_IS_2_0(tasha->wcd9xxx)) { + wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].reg = + WCD9335_MBHC_FSM_STATUS; + wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].mask = 0x01; + } + ret = wcd_mbhc_init(&tasha->mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, TASHA_ZDET_SUPPORTED); + if (ret) { + pr_err("%s: mbhc initialization failed\n", __func__); + goto err_hwdep; + } + + ptr = devm_kzalloc(codec->dev, (sizeof(tasha_rx_chs) + + sizeof(tasha_tx_chs)), GFP_KERNEL); + if (!ptr) { + ret = -ENOMEM; + goto err_hwdep; + } + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + snd_soc_dapm_new_controls(dapm, tasha_dapm_i2s_widgets, + ARRAY_SIZE(tasha_dapm_i2s_widgets)); + snd_soc_dapm_add_routes(dapm, audio_i2s_map, + ARRAY_SIZE(audio_i2s_map)); + for (i = 0; i < ARRAY_SIZE(tasha_i2s_dai); i++) { + INIT_LIST_HEAD(&tasha->dai[i].wcd9xxx_ch_list); + init_waitqueue_head(&tasha->dai[i].dai_wait); + } + } else if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + for (i = 0; i < NUM_CODEC_DAIS; i++) { + INIT_LIST_HEAD(&tasha->dai[i].wcd9xxx_ch_list); + init_waitqueue_head(&tasha->dai[i].dai_wait); + } + tasha_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tasha_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tasha_slimbus_slave_port_cfg.slave_port_mapping[0] = + TASHA_TX13; + tasha_init_slim_slave_cfg(codec); + } + + snd_soc_add_codec_controls(codec, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_codec_controls(codec, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + snd_soc_add_codec_controls(codec, + tasha_analog_gain_controls, + ARRAY_SIZE(tasha_analog_gain_controls)); + control->num_rx_port = TASHA_RX_MAX; + control->rx_chs = ptr; + memcpy(control->rx_chs, tasha_rx_chs, sizeof(tasha_rx_chs)); + control->num_tx_port = TASHA_TX_MAX; + control->tx_chs = ptr + sizeof(tasha_rx_chs); + memcpy(control->tx_chs, tasha_tx_chs, sizeof(tasha_tx_chs)); + + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture"); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF Mix Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + snd_soc_dapm_ignore_suspend(dapm, "AIF5 CPE TX"); + } + + snd_soc_dapm_sync(dapm); + + ret = tasha_setup_irqs(tasha); + if (ret) { + pr_err("%s: tasha irq setup failed %d\n", __func__, ret); + goto err_pdata; + } + + ret = tasha_cpe_initialize(codec); + if (ret) { + dev_err(codec->dev, + "%s: cpe initialization failed, err = %d\n", + __func__, ret); + /* Do not fail probe if CPE failed */ + ret = 0; + } + + for (i = 0; i < TASHA_NUM_DECIMATORS; i++) { + tasha->tx_hpf_work[i].tasha = tasha; + tasha->tx_hpf_work[i].decimator = i; + INIT_DELAYED_WORK(&tasha->tx_hpf_work[i].dwork, + tasha_tx_hpf_corner_freq_callback); + } + + for (i = 0; i < TASHA_NUM_DECIMATORS; i++) { + tasha->tx_mute_dwork[i].tasha = tasha; + tasha->tx_mute_dwork[i].decimator = i; + INIT_DELAYED_WORK(&tasha->tx_mute_dwork[i].dwork, + tasha_tx_mute_update_callback); + } + + tasha->spk_anc_dwork.tasha = tasha; + INIT_DELAYED_WORK(&tasha->spk_anc_dwork.dwork, + tasha_spk_anc_update_callback); + + mutex_lock(&tasha->codec_mutex); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); + mutex_unlock(&tasha->codec_mutex); + snd_soc_dapm_sync(dapm); + + return ret; + +err_pdata: + devm_kfree(codec->dev, ptr); + control->rx_chs = NULL; + control->tx_chs = NULL; +err_hwdep: + devm_kfree(codec->dev, tasha->fw_data); + tasha->fw_data = NULL; +err: + return ret; +} + +static int tasha_codec_remove(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *control; + + control = dev_get_drvdata(codec->dev->parent); + control->rx_chs = NULL; + control->tx_chs = NULL; + + tasha_cleanup_irqs(tasha); + /* Cleanup MBHC */ + /* Cleanup resmgr */ + + return 0; +} + +static struct regmap *tasha_get_regmap(struct device *dev) +{ + struct wcd9xxx *control = dev_get_drvdata(dev->parent); + + return control->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_tasha = { + .probe = tasha_codec_probe, + .remove = tasha_codec_remove, + .controls = tasha_snd_controls, + .num_controls = ARRAY_SIZE(tasha_snd_controls), + .dapm_widgets = tasha_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tasha_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + .get_regmap = tasha_get_regmap, +}; + +#ifdef CONFIG_PM +static int tasha_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tasha_priv *tasha = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s: system suspend\n", __func__); + if (cancel_delayed_work_sync(&tasha->power_gate_work)) + tasha_codec_power_gate_digital_core(tasha); + + return 0; +} + +static int tasha_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tasha_priv *tasha = platform_get_drvdata(pdev); + + if (!tasha) { + dev_err(dev, "%s: tasha private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system resume\n", __func__); + return 0; +} + +static const struct dev_pm_ops tasha_pm_ops = { + .suspend = tasha_suspend, + .resume = tasha_resume, +}; +#endif + +static int tasha_swrm_read(void *handle, int reg) +{ + struct tasha_priv *tasha; + struct wcd9xxx *wcd9xxx; + unsigned short swr_rd_addr_base; + unsigned short swr_rd_data_base; + int val, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tasha = (struct tasha_priv *)handle; + wcd9xxx = tasha->wcd9xxx; + + dev_dbg(tasha->dev, "%s: Reading soundwire register, 0x%x\n", + __func__, reg); + swr_rd_addr_base = WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0; + swr_rd_data_base = WCD9335_SWR_AHB_BRIDGE_RD_DATA_0; + /* read_lock */ + mutex_lock(&tasha->swr_read_lock); + ret = regmap_bulk_write(wcd9xxx->regmap, swr_rd_addr_base, + (u8 *)®, 4); + if (ret < 0) { + pr_err("%s: RD Addr Failure\n", __func__); + goto err; + } + /* Check for RD status */ + ret = regmap_bulk_read(wcd9xxx->regmap, swr_rd_data_base, + (u8 *)&val, 4); + if (ret < 0) { + pr_err("%s: RD Data Failure\n", __func__); + goto err; + } + ret = val; +err: + /* read_unlock */ + mutex_unlock(&tasha->swr_read_lock); + return ret; +} + +static int tasha_swrm_i2s_bulk_write(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_reg_val *bulk_reg, + size_t len) +{ + int i, ret = 0; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + + swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0; + + for (i = 0; i < (len * 2); i += 2) { + /* First Write the Data to register */ + ret = regmap_bulk_write(wcd9xxx->regmap, + swr_wr_data_base, bulk_reg[i].buf, 4); + if (ret < 0) { + dev_err(wcd9xxx->dev, "%s: WR Data Failure\n", + __func__); + break; + } + /* Next Write Address */ + ret = regmap_bulk_write(wcd9xxx->regmap, + swr_wr_addr_base, bulk_reg[i+1].buf, 4); + if (ret < 0) { + dev_err(wcd9xxx->dev, "%s: WR Addr Failure\n", + __func__); + break; + } + } + return ret; +} + +static int tasha_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len) +{ + struct tasha_priv *tasha; + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_reg_val *bulk_reg; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + int i, j, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + if (len <= 0) { + pr_err("%s: Invalid size: %zu\n", __func__, len); + return -EINVAL; + } + tasha = (struct tasha_priv *)handle; + wcd9xxx = tasha->wcd9xxx; + + swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0; + + bulk_reg = kzalloc((2 * len * sizeof(struct wcd9xxx_reg_val)), + GFP_KERNEL); + if (!bulk_reg) + return -ENOMEM; + + for (i = 0, j = 0; i < (len * 2); i += 2, j++) { + bulk_reg[i].reg = swr_wr_data_base; + bulk_reg[i].buf = (u8 *)(&val[j]); + bulk_reg[i].bytes = 4; + bulk_reg[i+1].reg = swr_wr_addr_base; + bulk_reg[i+1].buf = (u8 *)(®[j]); + bulk_reg[i+1].bytes = 4; + } + mutex_lock(&tasha->swr_write_lock); + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = tasha_swrm_i2s_bulk_write(wcd9xxx, bulk_reg, len); + if (ret) { + dev_err(tasha->dev, "%s: i2s bulk write failed, ret: %d\n", + __func__, ret); + } + } else { + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + (len * 2), false); + if (ret) { + dev_err(tasha->dev, "%s: swrm bulk write failed, ret: %d\n", + __func__, ret); + } + } + + mutex_unlock(&tasha->swr_write_lock); + kfree(bulk_reg); + + return ret; +} + +static int tasha_swrm_write(void *handle, int reg, int val) +{ + struct tasha_priv *tasha; + struct wcd9xxx *wcd9xxx; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + struct wcd9xxx_reg_val bulk_reg[2]; + int ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tasha = (struct tasha_priv *)handle; + wcd9xxx = tasha->wcd9xxx; + + swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0; + + /* First Write the Data to register */ + bulk_reg[0].reg = swr_wr_data_base; + bulk_reg[0].buf = (u8 *)(&val); + bulk_reg[0].bytes = 4; + bulk_reg[1].reg = swr_wr_addr_base; + bulk_reg[1].buf = (u8 *)(®); + bulk_reg[1].bytes = 4; + + mutex_lock(&tasha->swr_write_lock); + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = tasha_swrm_i2s_bulk_write(wcd9xxx, bulk_reg, 1); + if (ret) { + dev_err(tasha->dev, "%s: i2s swrm write failed, ret: %d\n", + __func__, ret); + } + } else { + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false); + if (ret < 0) + pr_err("%s: WR Data Failure\n", __func__); + } + + mutex_unlock(&tasha->swr_write_lock); + return ret; +} + +static int tasha_swrm_clock(void *handle, bool enable) +{ + struct tasha_priv *tasha = (struct tasha_priv *) handle; + + mutex_lock(&tasha->swr_clk_lock); + + dev_dbg(tasha->dev, "%s: swrm clock %s\n", + __func__, (enable?"enable" : "disable")); + if (enable) { + tasha->swr_clk_users++; + if (tasha->swr_clk_users == 1) { + if (TASHA_IS_2_0(tasha->wcd9xxx)) + regmap_update_bits( + tasha->wcd9xxx->regmap, + WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x00); + __tasha_cdc_mclk_enable(tasha, true); + regmap_update_bits(tasha->wcd9xxx->regmap, + WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + } + } else { + tasha->swr_clk_users--; + if (tasha->swr_clk_users == 0) { + regmap_update_bits(tasha->wcd9xxx->regmap, + WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + __tasha_cdc_mclk_enable(tasha, false); + if (TASHA_IS_2_0(tasha->wcd9xxx)) + regmap_update_bits( + tasha->wcd9xxx->regmap, + WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x10); + } + } + dev_dbg(tasha->dev, "%s: swrm clock users %d\n", + __func__, tasha->swr_clk_users); + mutex_unlock(&tasha->swr_clk_lock); + return 0; +} + +static int tasha_swrm_handle_irq(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action) +{ + struct tasha_priv *tasha; + int ret = 0; + struct wcd9xxx *wcd9xxx; + + if (!handle) { + pr_err("%s: null handle received\n", __func__); + return -EINVAL; + } + tasha = (struct tasha_priv *) handle; + wcd9xxx = tasha->wcd9xxx; + + if (action) { + ret = wcd9xxx_request_irq(&wcd9xxx->core_res, + WCD9335_IRQ_SOUNDWIRE, + swrm_irq_handler, + "Tasha SWR Master", swrm_handle); + if (ret) + dev_err(tasha->dev, "%s: Failed to request irq %d\n", + __func__, WCD9335_IRQ_SOUNDWIRE); + } else + wcd9xxx_free_irq(&wcd9xxx->core_res, WCD9335_IRQ_SOUNDWIRE, + swrm_handle); + + return ret; +} + +static void tasha_add_child_devices(struct work_struct *work) +{ + struct tasha_priv *tasha; + struct platform_device *pdev; + struct device_node *node; + struct wcd9xxx *wcd9xxx; + struct tasha_swr_ctrl_data *swr_ctrl_data = NULL, *temp; + int ret, ctrl_num = 0; + struct wcd_swr_ctrl_platform_data *platdata; + char plat_dev_name[WCD9335_STRING_LEN]; + + tasha = container_of(work, struct tasha_priv, + tasha_add_child_devices_work); + if (!tasha) { + pr_err("%s: Memory for WCD9335 does not exist\n", + __func__); + return; + } + wcd9xxx = tasha->wcd9xxx; + if (!wcd9xxx) { + pr_err("%s: Memory for WCD9XXX does not exist\n", + __func__); + return; + } + if (!wcd9xxx->dev->of_node) { + pr_err("%s: DT node for wcd9xxx does not exist\n", + __func__); + return; + } + + platdata = &tasha->swr_plat_data; + + for_each_child_of_node(wcd9xxx->dev->of_node, node) { + if (!strcmp(node->name, "swr_master")) + strlcpy(plat_dev_name, "tasha_swr_ctrl", + (WCD9335_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (WCD9335_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = tasha->dev; + pdev->dev.of_node = node; + + if (!strcmp(node->name, "swr_master")) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (!strcmp(node->name, "swr_master")) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct tasha_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(wcd9xxx->dev, "out of memory\n"); + ret = -ENOMEM; + goto err; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + tasha->nr = ctrl_num; + tasha->swr_ctrl_data = swr_ctrl_data; + } + } + + return; +fail_pdev_add: + platform_device_put(pdev); +err: + return; +} + +/* + * tasha_codec_ver: to get tasha codec version + * @codec: handle to snd_soc_codec * + * return enum codec_variant - version + */ +enum codec_variant tasha_codec_ver(void) +{ + return codec_ver; +} +EXPORT_SYMBOL(tasha_codec_ver); + +static int __tasha_enable_efuse_sensing(struct tasha_priv *tasha) +{ + int val, rc; + + __tasha_cdc_mclk_enable(tasha, true); + + regmap_update_bits(tasha->wcd9xxx->regmap, + WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x1E, 0x20); + regmap_update_bits(tasha->wcd9xxx->regmap, + WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x01, 0x01); + + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + rc = regmap_read(tasha->wcd9xxx->regmap, + WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS, &val); + + if (rc || (!(val & 0x01))) + WARN(1, "%s: Efuse sense is not complete\n", __func__); + + __tasha_cdc_mclk_enable(tasha, false); + + return rc; +} + +void tasha_get_codec_ver(struct tasha_priv *tasha) +{ + int i; + int val; + struct tasha_reg_mask_val codec_reg[] = { + {WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10, 0xFF, 0xFF}, + {WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11, 0xFF, 0x83}, + {WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12, 0xFF, 0x0A}, + }; + + __tasha_enable_efuse_sensing(tasha); + for (i = 0; i < ARRAY_SIZE(codec_reg); i++) { + regmap_read(tasha->wcd9xxx->regmap, codec_reg[i].reg, &val); + if (!(val && codec_reg[i].val)) { + codec_ver = WCD9335; + goto ret; + } + } + codec_ver = WCD9326; +ret: + pr_debug("%s: codec is %d\n", __func__, codec_ver); +} +EXPORT_SYMBOL(tasha_get_codec_ver); + +static int tasha_probe(struct platform_device *pdev) +{ + int ret = 0; + struct tasha_priv *tasha; + struct clk *wcd_ext_clk, *wcd_native_clk; + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd9xxx_power_region *cdc_pwr; + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) { + if (apr_get_subsys_state() == APR_SUBSYS_DOWN) { + dev_err(&pdev->dev, "%s: dsp down\n", __func__); + return -EPROBE_DEFER; + } + } + + tasha = devm_kzalloc(&pdev->dev, sizeof(struct tasha_priv), + GFP_KERNEL); + if (!tasha) + return -ENOMEM; + platform_set_drvdata(pdev, tasha); + + tasha->wcd9xxx = dev_get_drvdata(pdev->dev.parent); + tasha->dev = &pdev->dev; + INIT_DELAYED_WORK(&tasha->power_gate_work, tasha_codec_power_gate_work); + mutex_init(&tasha->power_lock); + mutex_init(&tasha->sido_lock); + INIT_WORK(&tasha->tasha_add_child_devices_work, + tasha_add_child_devices); + BLOCKING_INIT_NOTIFIER_HEAD(&tasha->notifier); + mutex_init(&tasha->micb_lock); + mutex_init(&tasha->swr_read_lock); + mutex_init(&tasha->swr_write_lock); + mutex_init(&tasha->swr_clk_lock); + mutex_init(&tasha->mclk_lock); + + cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region), + GFP_KERNEL); + if (!cdc_pwr) { + ret = -ENOMEM; + goto err_cdc_pwr; + } + tasha->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr; + cdc_pwr->pwr_collapse_reg_min = TASHA_DIG_CORE_REG_MIN; + cdc_pwr->pwr_collapse_reg_max = TASHA_DIG_CORE_REG_MAX; + wcd9xxx_set_power_state(tasha->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + + mutex_init(&tasha->codec_mutex); + /* + * Init resource manager so that if child nodes such as SoundWire + * requests for clock, resource manager can honor the request + */ + resmgr = wcd_resmgr_init(&tasha->wcd9xxx->core_res, NULL); + if (IS_ERR(resmgr)) { + ret = PTR_ERR(resmgr); + dev_err(&pdev->dev, "%s: Failed to initialize wcd resmgr\n", + __func__); + goto err_resmgr; + } + tasha->resmgr = resmgr; + tasha->swr_plat_data.handle = (void *) tasha; + tasha->swr_plat_data.read = tasha_swrm_read; + tasha->swr_plat_data.write = tasha_swrm_write; + tasha->swr_plat_data.bulk_write = tasha_swrm_bulk_write; + tasha->swr_plat_data.clk = tasha_swrm_clock; + tasha->swr_plat_data.handle_irq = tasha_swrm_handle_irq; + + /* Register for Clock */ + wcd_ext_clk = clk_get(tasha->wcd9xxx->dev, "wcd_clk"); + if (IS_ERR(wcd_ext_clk)) { + dev_err(tasha->wcd9xxx->dev, "%s: clk get %s failed\n", + __func__, "wcd_ext_clk"); + goto err_clk; + } + tasha->wcd_ext_clk = wcd_ext_clk; + tasha->sido_voltage = SIDO_VOLTAGE_NOMINAL_MV; + set_bit(AUDIO_NOMINAL, &tasha->status_mask); + tasha->sido_ccl_cnt = 0; + + /* Register native clk for 44.1 playback */ + wcd_native_clk = clk_get(tasha->wcd9xxx->dev, "wcd_native_clk"); + if (IS_ERR(wcd_native_clk)) + dev_dbg(tasha->wcd9xxx->dev, "%s: clk get %s failed\n", + __func__, "wcd_native_clk"); + else + tasha->wcd_native_clk = wcd_native_clk; + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tasha, + tasha_dai, ARRAY_SIZE(tasha_dai)); + else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tasha, + tasha_i2s_dai, + ARRAY_SIZE(tasha_i2s_dai)); + else + ret = -EINVAL; + if (ret) { + dev_err(&pdev->dev, "%s: Codec registration failed, ret = %d\n", + __func__, ret); + goto err_cdc_reg; + } + /* Update codec register default values */ + tasha_update_reg_defaults(tasha); + schedule_work(&tasha->tasha_add_child_devices_work); + tasha_get_codec_ver(tasha); + + dev_info(&pdev->dev, "%s: Tasha driver probe done\n", __func__); + return ret; + +err_cdc_reg: + clk_put(tasha->wcd_ext_clk); + if (tasha->wcd_native_clk) + clk_put(tasha->wcd_native_clk); +err_clk: + wcd_resmgr_remove(tasha->resmgr); +err_resmgr: + devm_kfree(&pdev->dev, cdc_pwr); +err_cdc_pwr: + mutex_destroy(&tasha->mclk_lock); + devm_kfree(&pdev->dev, tasha); + return ret; +} + +static int tasha_remove(struct platform_device *pdev) +{ + struct tasha_priv *tasha; + + tasha = platform_get_drvdata(pdev); + + mutex_destroy(&tasha->codec_mutex); + clk_put(tasha->wcd_ext_clk); + if (tasha->wcd_native_clk) + clk_put(tasha->wcd_native_clk); + mutex_destroy(&tasha->mclk_lock); + devm_kfree(&pdev->dev, tasha); + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver tasha_codec_driver = { + .probe = tasha_probe, + .remove = tasha_remove, + .driver = { + .name = "tasha_codec", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &tasha_pm_ops, +#endif + }, +}; + +module_platform_driver(tasha_codec_driver); + +MODULE_DESCRIPTION("Tasha Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h new file mode 100644 index 0000000000000000000000000000000000000000..d27bb96abeaf57800e1c4d917d9106a4f3cbcdec --- /dev/null +++ b/sound/soc/codecs/wcd9335.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef WCD9335_H +#define WCD9335_H + +#include +#include +#include +#include +#include "wcd-mbhc-v2.h" + +#define TASHA_REG_VAL(reg, val) {reg, 0, val} + +#define TASHA_REGISTER_START_OFFSET 0x800 +#define TASHA_SB_PGD_PORT_RX_BASE 0x40 +#define TASHA_SB_PGD_PORT_TX_BASE 0x50 + +#define TASHA_ZDET_SUPPORTED true +/* z value defined in milliohm */ +#define TASHA_ZDET_VAL_32 32000 +#define TASHA_ZDET_VAL_400 400000 +#define TASHA_ZDET_VAL_1200 1200000 +#define TASHA_ZDET_VAL_100K 100000000 +/* z floating defined in ohms */ +#define TASHA_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE + +#define WCD9335_DMIC_CLK_DIV_2 0x0 +#define WCD9335_DMIC_CLK_DIV_3 0x1 +#define WCD9335_DMIC_CLK_DIV_4 0x2 +#define WCD9335_DMIC_CLK_DIV_6 0x3 +#define WCD9335_DMIC_CLK_DIV_8 0x4 +#define WCD9335_DMIC_CLK_DIV_16 0x5 +#define WCD9335_DMIC_CLK_DRIVE_DEFAULT 0x02 + +#define WCD9335_ANC_DMIC_X2_FULL_RATE 1 +#define WCD9335_ANC_DMIC_X2_HALF_RATE 0 + +/* Number of input and output Slimbus port */ +enum { + TASHA_RX0 = 0, + TASHA_RX1, + TASHA_RX2, + TASHA_RX3, + TASHA_RX4, + TASHA_RX5, + TASHA_RX6, + TASHA_RX7, + TASHA_RX8, + TASHA_RX9, + TASHA_RX10, + TASHA_RX11, + TASHA_RX12, + TASHA_RX_MAX, +}; + +enum { + TASHA_TX0 = 0, + TASHA_TX1, + TASHA_TX2, + TASHA_TX3, + TASHA_TX4, + TASHA_TX5, + TASHA_TX6, + TASHA_TX7, + TASHA_TX8, + TASHA_TX9, + TASHA_TX10, + TASHA_TX11, + TASHA_TX12, + TASHA_TX13, + TASHA_TX14, + TASHA_TX15, + TASHA_TX_MAX, +}; + +enum { + /* INTR_REG 0 */ + WCD9335_IRQ_FLL_LOCK_LOSS = 1, + WCD9335_IRQ_HPH_PA_OCPL_FAULT, + WCD9335_IRQ_HPH_PA_OCPR_FAULT, + WCD9335_IRQ_EAR_PA_OCP_FAULT, + WCD9335_IRQ_HPH_PA_CNPL_COMPLETE, + WCD9335_IRQ_HPH_PA_CNPR_COMPLETE, + WCD9335_IRQ_EAR_PA_CNP_COMPLETE, + /* INTR_REG 1 */ + WCD9335_IRQ_MBHC_SW_DET, + WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, + WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, + WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD9335_IRQ_RESERVED_0, + WCD9335_IRQ_RESERVED_1, + WCD9335_IRQ_RESERVED_2, + /* INTR_REG 2 */ + WCD9335_IRQ_LINE_PA1_CNP_COMPLETE, + WCD9335_IRQ_LINE_PA2_CNP_COMPLETE, + WCD9335_IRQ_LINE_PA3_CNP_COMPLETE, + WCD9335_IRQ_LINE_PA4_CNP_COMPLETE, + WCD9335_IRQ_SOUNDWIRE, + WCD9335_IRQ_VDD_DIG_RAMP_COMPLETE, + WCD9335_IRQ_RCO_ERROR, + WCD9335_IRQ_SVA_ERROR, + /* INTR_REG 3 */ + WCD9335_IRQ_MAD_AUDIO, + WCD9335_IRQ_MAD_BEACON, + WCD9335_IRQ_MAD_ULTRASOUND, + WCD9335_IRQ_VBAT_ATTACK, + WCD9335_IRQ_VBAT_RESTORE, + WCD9335_IRQ_SVA_OUTBOX1, + WCD9335_IRQ_SVA_OUTBOX2, + WCD9335_NUM_IRQS, +}; + +enum wcd9335_codec_event { + WCD9335_CODEC_EVENT_CODEC_UP = 0, +}; + +enum tasha_on_demand_supply { + ON_DEMAND_MICBIAS = 0, + ON_DEMAND_SUPPLIES_MAX, +}; + +/* structure used to put the defined + * ondemand supply for codec + * and count being used. + */ +struct on_demand_supply { + struct regulator *supply; + int ondemand_supply_count; +}; + +/* Dai data structure holds the + * dai specific info like rate, + * channel number etc. + */ +struct tasha_codec_dai_data { + u32 rate; + u32 *ch_num; + u32 ch_act; + u32 ch_tot; +}; + +/* Structure used to update codec + * register defaults after reset + */ +struct tasha_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +/* Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + SPKR_MODE_DEFAULT, + SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +/* + * Rx path gain offsets + */ +enum { + RX_GAIN_OFFSET_M1P5_DB, + RX_GAIN_OFFSET_0_DB, +}; + +extern void *tasha_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type); +extern int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, + bool dapm); +extern int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable, + bool dapm); +extern int tasha_enable_efuse_sensing(struct snd_soc_codec *codec); +extern int tasha_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg); +extern void tasha_mbhc_hs_detect_exit(struct snd_soc_codec *codec); +extern void tasha_mbhc_zdet_gpio_ctrl( + int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high), + struct snd_soc_codec *codec); +extern int tasha_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +extern void tasha_event_register( + int (*machine_event_cb)(struct snd_soc_codec *codec, + enum wcd9335_codec_event), + struct snd_soc_codec *codec); +extern int tasha_codec_enable_standalone_micbias(struct snd_soc_codec *codec, + int micb_num, + bool enable); +extern int tasha_set_spkr_mode(struct snd_soc_codec *codec, int mode); +extern int tasha_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset); +extern enum codec_variant tasha_codec_ver(void); +#endif diff --git a/sound/soc/codecs/wcd934x/Makefile b/sound/soc/codecs/wcd934x/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..2843fa11d58ed2c46ae2e4fd0aaccc13f60ca2ba --- /dev/null +++ b/sound/soc/codecs/wcd934x/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for wcd934x codec driver. +# +snd-soc-wcd934x-objs := wcd934x.o wcd934x-dsp-cntl.o +obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o +snd-soc-wcd934x-mbhc-objs := wcd934x-mbhc.o +obj-$(CONFIG_SND_SOC_WCD934X_MBHC) += snd-soc-wcd934x-mbhc.o +snd-soc-wcd934x-dsd-objs := wcd934x-dsd.o +obj-$(CONFIG_SND_SOC_WCD934X_DSD) += snd-soc-wcd934x-dsd.o diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.c b/sound/soc/codecs/wcd934x/wcd934x-dsd.c new file mode 100644 index 0000000000000000000000000000000000000000..580591a32ba1952bdf0c45b874640714d99114a4 --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.c @@ -0,0 +1,767 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include "wcd934x-dsd.h" + +#define DSD_VOLUME_MAX_0dB 0 +#define DSD_VOLUME_MIN_M110dB -110 + +#define DSD_VOLUME_RANGE_CHECK(x) ((x >= DSD_VOLUME_MIN_M110dB) &&\ + (x <= DSD_VOLUME_MAX_0dB)) +#define DSD_VOLUME_STEPS 3 +#define DSD_VOLUME_UPDATE_DELAY_MS 30 +#define DSD_VOLUME_USLEEP_MARGIN_US 100 +#define DSD_VOLUME_STEP_DELAY_US ((1000 * DSD_VOLUME_UPDATE_DELAY_MS) / \ + (2 * DSD_VOLUME_STEPS)) + +#define TAVIL_VERSION_1_0 0 +#define TAVIL_VERSION_1_1 1 + +static const DECLARE_TLV_DB_MINMAX(tavil_dsd_db_scale, DSD_VOLUME_MIN_M110dB, + DSD_VOLUME_MAX_0dB); + +static const char *const dsd_if_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7", + "DSD_DATA_PAD" +}; + +static const char * const dsd_filt0_mux_text[] = { + "ZERO", "DSD_L IF MUX", +}; + +static const char * const dsd_filt1_mux_text[] = { + "ZERO", "DSD_R IF MUX", +}; + +static const struct soc_enum dsd_filt0_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_DSD0_PATH_CTL, 0, + ARRAY_SIZE(dsd_filt0_mux_text), dsd_filt0_mux_text); + +static const struct soc_enum dsd_filt1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_DSD1_PATH_CTL, 0, + ARRAY_SIZE(dsd_filt1_mux_text), dsd_filt1_mux_text); + +static SOC_ENUM_SINGLE_DECL(dsd_l_if_enum, WCD934X_CDC_DSD0_CFG0, + 2, dsd_if_text); +static SOC_ENUM_SINGLE_DECL(dsd_r_if_enum, WCD934X_CDC_DSD1_CFG0, + 2, dsd_if_text); + +static const struct snd_kcontrol_new dsd_filt0_mux = + SOC_DAPM_ENUM("DSD Filt0 Mux", dsd_filt0_mux_enum); + +static const struct snd_kcontrol_new dsd_filt1_mux = + SOC_DAPM_ENUM("DSD Filt1 Mux", dsd_filt1_mux_enum); + +static const struct snd_kcontrol_new dsd_l_if_mux = + SOC_DAPM_ENUM("DSD Left If Mux", dsd_l_if_enum); +static const struct snd_kcontrol_new dsd_r_if_mux = + SOC_DAPM_ENUM("DSD Right If Mux", dsd_r_if_enum); + +static const struct snd_soc_dapm_route tavil_dsd_audio_map[] = { + {"DSD_L IF MUX", "RX0", "CDC_IF RX0 MUX"}, + {"DSD_L IF MUX", "RX1", "CDC_IF RX1 MUX"}, + {"DSD_L IF MUX", "RX2", "CDC_IF RX2 MUX"}, + {"DSD_L IF MUX", "RX3", "CDC_IF RX3 MUX"}, + {"DSD_L IF MUX", "RX4", "CDC_IF RX4 MUX"}, + {"DSD_L IF MUX", "RX5", "CDC_IF RX5 MUX"}, + {"DSD_L IF MUX", "RX6", "CDC_IF RX6 MUX"}, + {"DSD_L IF MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"DSD_FILTER_0", NULL, "DSD_L IF MUX"}, + {"DSD_FILTER_0", NULL, "RX INT1 NATIVE SUPPLY"}, + {"RX INT1 MIX3", "DSD HPHL Switch", "DSD_FILTER_0"}, + + {"DSD_R IF MUX", "RX0", "CDC_IF RX0 MUX"}, + {"DSD_R IF MUX", "RX1", "CDC_IF RX1 MUX"}, + {"DSD_R IF MUX", "RX2", "CDC_IF RX2 MUX"}, + {"DSD_R IF MUX", "RX3", "CDC_IF RX3 MUX"}, + {"DSD_R IF MUX", "RX4", "CDC_IF RX4 MUX"}, + {"DSD_R IF MUX", "RX5", "CDC_IF RX5 MUX"}, + {"DSD_R IF MUX", "RX6", "CDC_IF RX6 MUX"}, + {"DSD_R IF MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"DSD_FILTER_1", NULL, "DSD_R IF MUX"}, + {"DSD_FILTER_1", NULL, "RX INT2 NATIVE SUPPLY"}, + {"RX INT2 MIX3", "DSD HPHR Switch", "DSD_FILTER_1"}, +}; + +static bool is_valid_dsd_interpolator(int interp_num) +{ + if ((interp_num == INTERP_HPHL) || (interp_num == INTERP_HPHR) || + (interp_num == INTERP_LO1) || (interp_num == INTERP_LO2)) + return true; + + return false; +} + +/** + * tavil_dsd_set_mixer_value - Set DSD HPH/LO mixer value + * + * @dsd_conf: pointer to dsd config + * @interp_num: Interpolator number (HPHL/R, LO1/2) + * @sw_value: Mixer switch value + * + * Returns 0 on success or -EINVAL on failure + */ +int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num, int sw_value) +{ + if (!dsd_conf) + return -EINVAL; + + if (!is_valid_dsd_interpolator(interp_num)) + return -EINVAL; + + dsd_conf->dsd_interp_mixer[interp_num] = !!sw_value; + + return 0; +} +EXPORT_SYMBOL(tavil_dsd_set_mixer_value); + +/** + * tavil_dsd_get_current_mixer_value - Get DSD HPH/LO mixer value + * + * @dsd_conf: pointer to dsd config + * @interp_num: Interpolator number (HPHL/R, LO1/2) + * + * Returns current mixer val for success or -EINVAL for failure + */ +int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num) +{ + if (!dsd_conf) + return -EINVAL; + + if (!is_valid_dsd_interpolator(interp_num)) + return -EINVAL; + + return dsd_conf->dsd_interp_mixer[interp_num]; +} +EXPORT_SYMBOL(tavil_dsd_get_current_mixer_value); + +/** + * tavil_dsd_set_out_select - DSD0/1 out select to HPH or LO + * + * @dsd_conf: pointer to dsd config + * @interp_num: Interpolator number (HPHL/R, LO1/2) + * + * Returns 0 for success or -EINVAL for failure + */ +int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf, + int interp_num) +{ + unsigned int reg, val; + struct snd_soc_codec *codec; + + if (!dsd_conf || !dsd_conf->codec) + return -EINVAL; + + codec = dsd_conf->codec; + + if (!is_valid_dsd_interpolator(interp_num)) { + dev_err(codec->dev, "%s: Invalid Interpolator: %d for DSD\n", + __func__, interp_num); + return -EINVAL; + } + + switch (interp_num) { + case INTERP_HPHL: + reg = WCD934X_CDC_DSD0_CFG0; + val = 0x00; + break; + case INTERP_HPHR: + reg = WCD934X_CDC_DSD1_CFG0; + val = 0x00; + break; + case INTERP_LO1: + reg = WCD934X_CDC_DSD0_CFG0; + val = 0x02; + break; + case INTERP_LO2: + reg = WCD934X_CDC_DSD1_CFG0; + val = 0x02; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, reg, 0x02, val); + + return 0; +} +EXPORT_SYMBOL(tavil_dsd_set_out_select); + +/** + * tavil_dsd_reset - Reset DSD block + * + * @dsd_conf: pointer to dsd config + * + */ +void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf) +{ + if (!dsd_conf || !dsd_conf->codec) + return; + + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x02, 0x02); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x01, 0x00); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x02, 0x02); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x01, 0x00); +} +EXPORT_SYMBOL(tavil_dsd_reset); + +/** + * tavil_dsd_set_interp_rate - Set interpolator rate for DSD + * + * @dsd_conf: pointer to dsd config + * @rx_port: RX port number + * @sample_rate: Sample rate of the RX interpolator + * @sample_rate_val: Interpolator rate value + */ +void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port, + u32 sample_rate, u8 sample_rate_val) +{ + u8 dsd_inp_sel; + u8 dsd0_inp, dsd1_inp; + u8 val0, val1; + u8 dsd0_out_sel, dsd1_out_sel; + u16 int_fs_reg, interp_num = 0; + struct snd_soc_codec *codec; + + if (!dsd_conf || !dsd_conf->codec) + return; + + codec = dsd_conf->codec; + + dsd_inp_sel = DSD_INP_SEL_RX0 + rx_port - WCD934X_RX_PORT_START_NUMBER; + + val0 = snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0); + val1 = snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0); + dsd0_inp = (val0 & 0x3C) >> 2; + dsd1_inp = (val1 & 0x3C) >> 2; + dsd0_out_sel = (val0 & 0x02) >> 1; + dsd1_out_sel = (val1 & 0x02) >> 1; + + /* Set HPHL or LO1 interp rate based on out select */ + if (dsd_inp_sel == dsd0_inp) { + interp_num = dsd0_out_sel ? INTERP_LO1 : INTERP_HPHL; + dsd_conf->base_sample_rate[DSD0] = sample_rate; + } + + /* Set HPHR or LO2 interp rate based on out select */ + if (dsd_inp_sel == dsd1_inp) { + interp_num = dsd1_out_sel ? INTERP_LO2 : INTERP_HPHR; + dsd_conf->base_sample_rate[DSD1] = sample_rate; + } + + if (interp_num) { + int_fs_reg = WCD934X_CDC_RX0_RX_PATH_CTL + 20 * interp_num; + if ((snd_soc_read(codec, int_fs_reg) & 0x0f) < 0x09) { + dev_dbg(codec->dev, "%s: Set Interp %d to sample_rate val 0x%x\n", + __func__, interp_num, sample_rate_val); + snd_soc_update_bits(codec, int_fs_reg, 0x0F, + sample_rate_val); + } + } +} +EXPORT_SYMBOL(tavil_dsd_set_interp_rate); + +static int tavil_set_dsd_mode(struct snd_soc_codec *codec, int dsd_num, + u8 *pcm_rate_val) +{ + unsigned int dsd_out_sel_reg; + u8 dsd_mode; + u32 sample_rate; + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + + if (!dsd_conf) + return -EINVAL; + + if ((dsd_num < 0) || (dsd_num > 1)) + return -EINVAL; + + sample_rate = dsd_conf->base_sample_rate[dsd_num]; + dsd_out_sel_reg = WCD934X_CDC_DSD0_CFG0 + dsd_num * 16; + + switch (sample_rate) { + case 176400: + dsd_mode = 0; /* DSD_64 */ + *pcm_rate_val = 0xb; + break; + case 352800: + dsd_mode = 1; /* DSD_128 */ + *pcm_rate_val = 0xc; + break; + default: + dev_err(codec->dev, "%s: Invalid DSD rate: %d\n", + __func__, sample_rate); + return -EINVAL; + } + + snd_soc_update_bits(codec, dsd_out_sel_reg, 0x01, dsd_mode); + + return 0; +} + +static void tavil_dsd_data_pull(struct snd_soc_codec *codec, int dsd_num, + u8 pcm_rate_val, bool enable) +{ + u8 clk_en, mute_en; + u8 dsd_inp_sel; + + if (enable) { + clk_en = 0x20; + mute_en = 0x10; + } else { + clk_en = 0x00; + mute_en = 0x00; + } + + if (dsd_num & 0x01) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_MIX_CTL, + 0x20, clk_en); + dsd_inp_sel = (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & + 0x3C) >> 2; + dsd_inp_sel = (enable) ? dsd_inp_sel : 0; + if (dsd_inp_sel < 9) { + snd_soc_update_bits(codec, + WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, + 0x0F, dsd_inp_sel); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_CTL, + 0x0F, pcm_rate_val); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_CTL, + 0x10, mute_en); + } + } + if (dsd_num & 0x02) { + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_MIX_CTL, + 0x20, clk_en); + dsd_inp_sel = (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & + 0x3C) >> 2; + dsd_inp_sel = (enable) ? dsd_inp_sel : 0; + if (dsd_inp_sel < 9) { + snd_soc_update_bits(codec, + WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, + 0x0F, dsd_inp_sel); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_CTL, + 0x0F, pcm_rate_val); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_CTL, + 0x10, mute_en); + } + } +} + +static void tavil_dsd_update_volume(struct tavil_dsd_config *dsd_conf) +{ + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0, + 0x01, 0x01); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0, + 0x01, 0x00); +} + +static int tavil_enable_dsd(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + int rc, clk_users; + int interp_idx; + u8 pcm_rate_val; + + if (!dsd_conf) { + dev_err(codec->dev, "%s: null dsd_config pointer\n", __func__); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: DSD%d, event: %d\n", __func__, + w->shift, event); + + if (w->shift == DSD0) { + /* Read out select */ + if (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x02) + interp_idx = INTERP_LO1; + else + interp_idx = INTERP_HPHL; + } else if (w->shift == DSD1) { + /* Read out select */ + if (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & 0x02) + interp_idx = INTERP_LO2; + else + interp_idx = INTERP_HPHR; + } else { + dev_err(codec->dev, "%s: Unsupported DSD:%d\n", + __func__, w->shift); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + clk_users = tavil_codec_enable_interp_clk(codec, event, + interp_idx); + + rc = tavil_set_dsd_mode(codec, w->shift, &pcm_rate_val); + if (rc) + return rc; + + tavil_dsd_data_pull(codec, (1 << w->shift), pcm_rate_val, + true); + + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, 0x01, + 0x01); + if (w->shift == DSD0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x01, 0x01); + /* Apply Gain */ + snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1, + dsd_conf->volume[DSD0]); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + + } else if (w->shift == DSD1) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x01, 0x01); + /* Apply Gain */ + snd_soc_write(codec, WCD934X_CDC_DSD1_CFG1, + dsd_conf->volume[DSD1]); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + } + /* 10msec sleep required after DSD clock is set */ + usleep_range(10000, 10100); + + if (clk_users > 1) { + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x02, 0x02); + if (w->shift == DSD0) + snd_soc_update_bits(codec, + WCD934X_CDC_DSD0_CFG2, + 0x04, 0x00); + if (w->shift == DSD1) + snd_soc_update_bits(codec, + WCD934X_CDC_DSD1_CFG2, + 0x04, 0x00); + + } + break; + case SND_SOC_DAPM_POST_PMD: + if (w->shift == DSD0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x01, 0x00); + } else if (w->shift == DSD1) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x01, 0x00); + } + + tavil_codec_enable_interp_clk(codec, event, interp_idx); + + if (!(snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01) && + !(snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) { + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, + 0x01, 0x00); + tavil_dsd_data_pull(codec, 0x03, 0x04, false); + tavil_dsd_reset(dsd_conf); + } + break; + } + + return 0; +} + +static int tavil_dsd_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = DSD_VOLUME_MIN_M110dB; + uinfo->value.integer.max = DSD_VOLUME_MAX_0dB; + + return 0; +} + +static int tavil_dsd_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + int nv[DSD_MAX], cv[DSD_MAX]; + int step_size, nv1; + int i, dsd_idx; + + if (!dsd_conf) + return 0; + + mutex_lock(&dsd_conf->vol_mutex); + + for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) { + cv[dsd_idx] = dsd_conf->volume[dsd_idx]; + nv[dsd_idx] = ucontrol->value.integer.value[dsd_idx]; + } + + if ((!DSD_VOLUME_RANGE_CHECK(nv[DSD0])) || + (!DSD_VOLUME_RANGE_CHECK(nv[DSD1]))) + goto done; + + for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) { + if (cv[dsd_idx] == nv[dsd_idx]) + continue; + + dev_dbg(codec->dev, "%s: DSD%d cur.vol: %d, new vol: %d\n", + __func__, dsd_idx, cv[dsd_idx], nv[dsd_idx]); + + step_size = (nv[dsd_idx] - cv[dsd_idx]) / + DSD_VOLUME_STEPS; + + nv1 = cv[dsd_idx]; + + for (i = 0; i < DSD_VOLUME_STEPS; i++) { + nv1 += step_size; + snd_soc_write(codec, + WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx, + nv1); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + + /* sleep required after each volume step */ + usleep_range(DSD_VOLUME_STEP_DELAY_US, + (DSD_VOLUME_STEP_DELAY_US + + DSD_VOLUME_USLEEP_MARGIN_US)); + } + if (nv1 != nv[dsd_idx]) { + snd_soc_write(codec, + WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx, + nv[dsd_idx]); + + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + } + + dsd_conf->volume[dsd_idx] = nv[dsd_idx]; + } + +done: + mutex_unlock(&dsd_conf->vol_mutex); + + return 0; +} + +static int tavil_dsd_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + + if (dsd_conf) { + ucontrol->value.integer.value[0] = dsd_conf->volume[DSD0]; + ucontrol->value.integer.value[1] = dsd_conf->volume[DSD1]; + } + + return 0; +} + +static const struct snd_kcontrol_new tavil_dsd_vol_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "DSD Volume", + .info = tavil_dsd_vol_info, + .get = tavil_dsd_vol_get, + .put = tavil_dsd_vol_put, + .tlv = { .p = tavil_dsd_db_scale }, + }, +}; + +static const struct snd_soc_dapm_widget tavil_dsd_widgets[] = { + SND_SOC_DAPM_MUX("DSD_L IF MUX", SND_SOC_NOPM, 0, 0, &dsd_l_if_mux), + SND_SOC_DAPM_MUX_E("DSD_FILTER_0", SND_SOC_NOPM, 0, 0, &dsd_filt0_mux, + tavil_enable_dsd, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("DSD_R IF MUX", SND_SOC_NOPM, 0, 0, &dsd_r_if_mux), + SND_SOC_DAPM_MUX_E("DSD_FILTER_1", SND_SOC_NOPM, 1, 0, &dsd_filt1_mux, + tavil_enable_dsd, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +/** + * tavil_dsd_post_ssr_init - DSD intialization after subsystem restart + * + * @codec: pointer to snd_soc_codec + * + * Returns 0 on success or error on failure + */ +int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_conf) +{ + struct snd_soc_codec *codec; + + if (!dsd_conf || !dsd_conf->codec) + return -EINVAL; + + codec = dsd_conf->codec; + /* Disable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08); + + /* DSD registers init */ + if (dsd_conf->version == TAVIL_VERSION_1_0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00); + } + /* DSD0: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04); + /* DSD1: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07, + 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07, + 0x04); + + /* Enable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00); + + return 0; +} +EXPORT_SYMBOL(tavil_dsd_post_ssr_init); + +/** + * tavil_dsd_init - DSD intialization + * + * @codec: pointer to snd_soc_codec + * + * Returns pointer to tavil_dsd_config for success or NULL for failure + */ +struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm; + struct tavil_dsd_config *dsd_conf; + u8 val; + + if (!codec) + return NULL; + + dapm = snd_soc_codec_get_dapm(codec); + + /* Read efuse register to check if DSD is supported */ + val = snd_soc_read(codec, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14); + if (val & 0x80) { + dev_info(codec->dev, "%s: DSD unsupported for this codec version\n", + __func__); + return NULL; + } + + dsd_conf = devm_kzalloc(codec->dev, sizeof(struct tavil_dsd_config), + GFP_KERNEL); + if (!dsd_conf) + return NULL; + + dsd_conf->codec = codec; + + /* Read version */ + dsd_conf->version = snd_soc_read(codec, + WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0); + /* DSD registers init */ + if (dsd_conf->version == TAVIL_VERSION_1_0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00); + } + /* DSD0: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04); + /* DSD1: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07, + 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07, + 0x04); + + snd_soc_dapm_new_controls(dapm, tavil_dsd_widgets, + ARRAY_SIZE(tavil_dsd_widgets)); + + snd_soc_dapm_add_routes(dapm, tavil_dsd_audio_map, + ARRAY_SIZE(tavil_dsd_audio_map)); + + mutex_init(&dsd_conf->vol_mutex); + dsd_conf->volume[DSD0] = DSD_VOLUME_MAX_0dB; + dsd_conf->volume[DSD1] = DSD_VOLUME_MAX_0dB; + + snd_soc_add_codec_controls(codec, tavil_dsd_vol_controls, + ARRAY_SIZE(tavil_dsd_vol_controls)); + + /* Enable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00); + + return dsd_conf; +} +EXPORT_SYMBOL(tavil_dsd_init); + +/** + * tavil_dsd_deinit - DSD de-intialization + * + * @dsd_conf: pointer to tavil_dsd_config + */ +void tavil_dsd_deinit(struct tavil_dsd_config *dsd_conf) +{ + struct snd_soc_codec *codec; + + if (!dsd_conf) + return; + + codec = dsd_conf->codec; + + mutex_destroy(&dsd_conf->vol_mutex); + + /* Disable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08); + + devm_kfree(codec->dev, dsd_conf); +} +EXPORT_SYMBOL(tavil_dsd_deinit); diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.h b/sound/soc/codecs/wcd934x/wcd934x-dsd.h new file mode 100644 index 0000000000000000000000000000000000000000..498288335b3b8ebc9299b7b281f74fd6014f6ad6 --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.h @@ -0,0 +1,97 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD934X_DSD_H__ +#define __WCD934X_DSD_H__ + +#include +#include "wcd934x.h" + +enum { + DSD0, + DSD1, + DSD_MAX, +}; + +enum { + DSD_INP_SEL_ZERO = 0, + DSD_INP_SEL_RX0, + DSD_INP_SEL_RX1, + DSD_INP_SEL_RX2, + DSD_INP_SEL_RX3, + DSD_INP_SEL_RX4, + DSD_INP_SEL_RX5, + DSD_INP_SEL_RX6, + DSD_INP_SEL_RX7, +}; + +struct tavil_dsd_config { + struct snd_soc_codec *codec; + unsigned int dsd_interp_mixer[INTERP_MAX]; + u32 base_sample_rate[DSD_MAX]; + int volume[DSD_MAX]; + struct mutex vol_mutex; + int version; +}; + +#ifdef CONFIG_SND_SOC_WCD934X_DSD +int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num, int sw_value); +int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num); +int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf, + int interp_num); +void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf); +void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port, + u32 sample_rate, u8 sample_rate_val); +struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec); +void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config); +int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_config); +#else +int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num, int sw_value) +{ + return 0; +} + +int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num) +{ + return 0; +} + +int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf, + int interp_num) +{ + return 0; +} + +void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf) +{ } + +void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port, + u32 sample_rate, u8 sample_rate_val) +{ } + +struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec) +{ + return NULL; +} + +void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config) +{ } +int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_config) +{ + return 0; +} +#endif +#endif diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c new file mode 100644 index 0000000000000000000000000000000000000000..6a5da915f2bcd7550835ff19543a559261f46baa --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -0,0 +1,1369 @@ +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd934x.h" +#include "wcd934x-dsp-cntl.h" + +#define WCD_CNTL_DIR_NAME_LEN_MAX 32 +#define WCD_CPE_FLL_MAX_RETRIES 5 +#define WCD_MEM_ENABLE_MAX_RETRIES 20 +#define WCD_DSP_BOOT_TIMEOUT_MS 3000 +#define WCD_SYSFS_ENTRY_MAX_LEN 8 +#define WCD_PROCFS_ENTRY_MAX_LEN 16 +#define WCD_934X_RAMDUMP_START_ADDR 0x20100000 +#define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128) + +#define WCD_CNTL_MUTEX_LOCK(codec, lock) \ +{ \ + dev_dbg(codec->dev, "%s: mutex_lock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \ +{ \ + dev_dbg(codec->dev, "%s: mutex_unlock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +enum wcd_mem_type { + WCD_MEM_TYPE_ALWAYS_ON, + WCD_MEM_TYPE_SWITCHABLE, +}; + +struct wcd_cntl_attribute { + struct attribute attr; + ssize_t (*show)(struct wcd_dsp_cntl *cntl, char *buf); + ssize_t (*store)(struct wcd_dsp_cntl *cntl, const char *buf, + ssize_t count); +}; + +#define WCD_CNTL_ATTR(_name, _mode, _show, _store) \ +static struct wcd_cntl_attribute cntl_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ + .show = _show, \ + .store = _store, \ +} + +#define to_wcd_cntl_attr(a) \ + container_of((a), struct wcd_cntl_attribute, attr) + +#define to_wcd_cntl(kobj) \ + container_of((kobj), struct wcd_dsp_cntl, wcd_kobj) + +static u8 mem_enable_values[] = { + 0xFE, 0xFC, 0xF8, 0xF0, + 0xE0, 0xC0, 0x80, 0x00, +}; + +static ssize_t wdsp_boot_show(struct wcd_dsp_cntl *cntl, char *buf) +{ + return snprintf(buf, WCD_SYSFS_ENTRY_MAX_LEN, + "%u", cntl->boot_reqs); +} + +static ssize_t wdsp_boot_store(struct wcd_dsp_cntl *cntl, + const char *buf, ssize_t count) +{ + u32 val; + bool vote; + int ret; + + ret = kstrtou32(buf, 10, &val); + if (ret) { + dev_err(cntl->codec->dev, + "%s: Invalid entry, ret = %d\n", __func__, ret); + return -EINVAL; + } + + if (val > 0) { + cntl->boot_reqs++; + vote = true; + } else { + cntl->boot_reqs--; + vote = false; + } + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->vote_for_dsp) + ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote); + else + ret = -EINVAL; + + if (IS_ERR_VALUE(ret)) + dev_err(cntl->codec->dev, + "%s: failed to %s dsp\n", __func__, + vote ? "enable" : "disable"); + return count; +} + +WCD_CNTL_ATTR(boot, 0660, wdsp_boot_show, wdsp_boot_store); + +static ssize_t wcd_cntl_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct wcd_cntl_attribute *wcd_attr = to_wcd_cntl_attr(attr); + struct wcd_dsp_cntl *cntl = to_wcd_cntl(kobj); + ssize_t ret = -EINVAL; + + if (cntl && wcd_attr->show) + ret = wcd_attr->show(cntl, buf); + + return ret; +} + +static ssize_t wcd_cntl_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + struct wcd_cntl_attribute *wcd_attr = to_wcd_cntl_attr(attr); + struct wcd_dsp_cntl *cntl = to_wcd_cntl(kobj); + ssize_t ret = -EINVAL; + + if (cntl && wcd_attr->store) + ret = wcd_attr->store(cntl, buf, count); + + return ret; +} + +static const struct sysfs_ops wcd_cntl_sysfs_ops = { + .show = wcd_cntl_sysfs_show, + .store = wcd_cntl_sysfs_store, +}; + +static struct kobj_type wcd_cntl_ktype = { + .sysfs_ops = &wcd_cntl_sysfs_ops, +}; + +static void wcd_cntl_change_online_state(struct wcd_dsp_cntl *cntl, + u8 online) +{ + struct wdsp_ssr_entry *ssr_entry = &cntl->ssr_entry; + unsigned long ret; + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + ssr_entry->offline = !online; + /* Make sure the write is complete */ + wmb(); + ret = xchg(&ssr_entry->offline_change, 1); + wake_up_interruptible(&ssr_entry->offline_poll_wait); + dev_dbg(cntl->codec->dev, + "%s: requested %u, offline %u offline_change %u, ret = %ldn", + __func__, online, ssr_entry->offline, + ssr_entry->offline_change, ret); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); +} + +static ssize_t wdsp_ssr_entry_read(struct snd_info_entry *entry, + void *file_priv_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + int len = 0; + char buffer[WCD_PROCFS_ENTRY_MAX_LEN]; + struct wcd_dsp_cntl *cntl; + struct wdsp_ssr_entry *ssr_entry; + ssize_t ret; + u8 offline; + + cntl = (struct wcd_dsp_cntl *) entry->private_data; + if (!cntl) { + pr_err("%s: Invalid private data for SSR procfs entry\n", + __func__); + return -EINVAL; + } + + ssr_entry = &cntl->ssr_entry; + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + offline = ssr_entry->offline; + /* Make sure the read is complete */ + rmb(); + dev_dbg(cntl->codec->dev, "%s: offline = %s\n", __func__, + offline ? "true" : "false"); + len = snprintf(buffer, sizeof(buffer), "%s\n", + offline ? "OFFLINE" : "ONLINE"); + ret = simple_read_from_buffer(buf, count, &pos, buffer, len); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); + + return ret; +} + +static unsigned int wdsp_ssr_entry_poll(struct snd_info_entry *entry, + void *private_data, struct file *file, + poll_table *wait) +{ + struct wcd_dsp_cntl *cntl; + struct wdsp_ssr_entry *ssr_entry; + unsigned int ret = 0; + + if (!entry || !entry->private_data) { + pr_err("%s: %s is NULL\n", __func__, + (!entry) ? "entry" : "private_data"); + return -EINVAL; + } + + cntl = (struct wcd_dsp_cntl *) entry->private_data; + ssr_entry = &cntl->ssr_entry; + + dev_dbg(cntl->codec->dev, "%s: Poll wait, offline = %u\n", + __func__, ssr_entry->offline); + poll_wait(file, &ssr_entry->offline_poll_wait, wait); + dev_dbg(cntl->codec->dev, "%s: Woken up Poll wait, offline = %u\n", + __func__, ssr_entry->offline); + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + if (xchg(&ssr_entry->offline_change, 0)) + ret = POLLIN | POLLPRI | POLLRDNORM; + dev_dbg(cntl->codec->dev, "%s: ret (%d) from poll_wait\n", + __func__, ret); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); + + return ret; +} + +static struct snd_info_entry_ops wdsp_ssr_entry_ops = { + .read = wdsp_ssr_entry_read, + .poll = wdsp_ssr_entry_poll, +}; + +static int wcd_cntl_cpe_fll_calibrate(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0, retry = 0; + u8 cal_lsb, cal_msb; + u8 lock_det; + + /* Make sure clocks are gated */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x05, 0x00); + + /* Enable CPE FLL reference clock */ + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x80, 0x80); + + snd_soc_update_bits(codec, WCD934X_CPE_FLL_USER_CTL_5, + 0xF3, 0x13); + snd_soc_write(codec, WCD934X_CPE_FLL_L_VAL_CTL_0, 0x50); + + /* Disable CPAR reset and Enable CPAR clk */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, + 0x02, 0x02); + + /* Write calibration l-value based on cdc clk rate */ + if (cntl->clk_rate == 9600000) { + cal_lsb = 0x6d; + cal_msb = 0x00; + } else { + cal_lsb = 0x56; + cal_msb = 0x00; + } + snd_soc_write(codec, WCD934X_CPE_FLL_USER_CTL_6, cal_lsb); + snd_soc_write(codec, WCD934X_CPE_FLL_USER_CTL_7, cal_msb); + + /* FLL mode to follow power up sequence */ + snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE, + 0x60, 0x00); + + /* HW controlled CPE FLL */ + snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE, + 0x80, 0x80); + + /* Force on CPE FLL */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG, + 0x04, 0x04); + + do { + /* Time for FLL calibration to complete */ + usleep_range(1000, 1100); + lock_det = snd_soc_read(codec, WCD934X_CPE_FLL_STATUS_3); + retry++; + } while (!(lock_det & 0x01) && + retry <= WCD_CPE_FLL_MAX_RETRIES); + + if (!(lock_det & 0x01)) { + dev_err(codec->dev, "%s: lock detect not set, 0x%02x\n", + __func__, lock_det); + ret = -EIO; + goto err_lock_det; + } + + snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE, + 0x60, 0x20); + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG, + 0x04, 0x00); + return ret; + +err_lock_det: + /* Undo the register settings */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, + 0x02, 0x00); + return ret; +} + +static void wcd_cntl_config_cpar(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + u8 nom_lo, nom_hi, svs2_lo, svs2_hi; + + /* Configure CPAR */ + nom_hi = svs2_hi = 0; + if (cntl->clk_rate == 9600000) { + nom_lo = 0x90; + svs2_lo = 0x50; + } else { + nom_lo = 0x70; + svs2_lo = 0x3e; + } + + snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_NOM_LOW, nom_lo); + snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_NOM_HIGH, nom_hi); + snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW, svs2_lo); + snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH, svs2_hi); + + snd_soc_update_bits(codec, WCD934X_CPE_SS_PWR_CPEFLL_CTL, + 0x03, 0x03); +} + +static int wcd_cntl_cpe_fll_ctrl(struct wcd_dsp_cntl *cntl, + bool enable) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + if (enable) { + ret = wcd_cntl_cpe_fll_calibrate(cntl); + if (IS_ERR_VALUE(ret)) { + dev_err(codec->dev, + "%s: cpe_fll_cal failed, err = %d\n", + __func__, ret); + goto done; + } + + wcd_cntl_config_cpar(cntl); + + /* Enable AHB CLK and CPE CLK*/ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x05, 0x05); + } else { + /* Disable AHB CLK and CPE CLK */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x05, 0x00); + /* Reset the CPAR mode for CPE FLL */ + snd_soc_write(codec, WCD934X_CPE_FLL_FLL_MODE, 0x20); + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, + 0x02, 0x00); + } +done: + return ret; +} + +static int wcd_cntl_clocks_enable(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret; + + WCD_CNTL_MUTEX_LOCK(codec, cntl->clk_mutex); + /* Enable codec clock */ + if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en) + ret = cntl->cdc_cb->cdc_clk_en(codec, true); + else + ret = -EINVAL; + + if (IS_ERR_VALUE(ret)) { + dev_err(codec->dev, + "%s: Failed to enable cdc clk, err = %d\n", + __func__, ret); + goto done; + } + + /* Configure and Enable CPE FLL clock */ + ret = wcd_cntl_cpe_fll_ctrl(cntl, true); + if (IS_ERR_VALUE(ret)) { + dev_err(codec->dev, + "%s: Failed to enable cpe clk, err = %d\n", + __func__, ret); + goto err_cpe_clk; + } + cntl->is_clk_enabled = true; + + /* Ungate the CPR clock */ + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, 0x10, 0x00); +done: + WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex); + return ret; + +err_cpe_clk: + if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en) + cntl->cdc_cb->cdc_clk_en(codec, false); + + WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex); + return ret; +} + +static int wcd_cntl_clocks_disable(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + WCD_CNTL_MUTEX_LOCK(codec, cntl->clk_mutex); + if (!cntl->is_clk_enabled) { + dev_info(codec->dev, "%s: clocks already disabled\n", + __func__); + goto done; + } + + /* Gate the CPR clock */ + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, 0x10, 0x10); + + /* Disable CPE FLL clock */ + ret = wcd_cntl_cpe_fll_ctrl(cntl, false); + if (IS_ERR_VALUE(ret)) + dev_err(codec->dev, + "%s: Failed to disable cpe clk, err = %d\n", + __func__, ret); + + /* + * Even if CPE FLL disable failed, go ahead and disable + * the codec clock + */ + if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en) + ret = cntl->cdc_cb->cdc_clk_en(codec, false); + else + ret = -EINVAL; + + cntl->is_clk_enabled = false; +done: + WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex); + return ret; +} + +static void wcd_cntl_cpar_ctrl(struct wcd_dsp_cntl *cntl, + bool enable) +{ + struct snd_soc_codec *codec = cntl->codec; + + if (enable) + snd_soc_write(codec, WCD934X_CPE_SS_CPAR_CTL, 0x03); + else + snd_soc_write(codec, WCD934X_CPE_SS_CPAR_CTL, 0x00); +} + +static int wcd_cntl_enable_memory(struct wcd_dsp_cntl *cntl, + enum wcd_mem_type mem_type) +{ + struct snd_soc_codec *codec = cntl->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int loop_cnt = 0; + u8 status; + int ret = 0; + + + switch (mem_type) { + + case WCD_MEM_TYPE_ALWAYS_ON: + + /* 512KB of always on region */ + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + break; + + case WCD_MEM_TYPE_SWITCHABLE: + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL, + 0x80, 0x80); + snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + 0x01, 0x01); + do { + loop_cnt++; + /* Time to enable the power domain for memory */ + usleep_range(100, 150); + status = snd_soc_read(codec, + WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL); + } while ((status & 0x02) != 0x02 && + loop_cnt != WCD_MEM_ENABLE_MAX_RETRIES); + + if ((status & 0x02) != 0x02) { + dev_err(cntl->codec->dev, + "%s: power domain not enabled, status = 0x%02x\n", + __func__, status); + ret = -EIO; + goto done; + } + + /* Rest of the memory */ + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, + 0x05); + break; + + default: + dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n", + __func__, mem_type); + ret = -EINVAL; + break; + } +done: + /* Make sure Deep sleep of memories is enabled for all banks */ + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F); + + return ret; +} + +static void wcd_cntl_disable_memory(struct wcd_dsp_cntl *cntl, + enum wcd_mem_type mem_type) +{ + struct snd_soc_codec *codec = cntl->codec; + u8 val; + + switch (mem_type) { + case WCD_MEM_TYPE_ALWAYS_ON: + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, + 0xFF); + break; + case WCD_MEM_TYPE_SWITCHABLE: + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, + 0x07); + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + 0x01, 0x00); + val = snd_soc_read(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL); + if (val & 0x02) + dev_err(codec->dev, + "%s: Disable switchable failed, val = 0x%02x", + __func__, val); + + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL, + 0x80, 0x00); + break; + default: + dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n", + __func__, mem_type); + break; + } + + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F); +} + +static void wcd_cntl_do_shutdown(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + + /* Disable WDOG */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, + 0x3F, 0x01); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x04, 0x00); + + /* Put WDSP in reset state */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x02, 0x00); + + /* If DSP transitions from boot to shutdown, then vote for SVS */ + if (cntl->is_wdsp_booted) + cntl->cdc_cb->cdc_vote_svs(codec, true); + cntl->is_wdsp_booted = false; +} + +static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + /* + * Debug mode is set from debugfs file node. If debug_mode + * is set, then do not configure the watchdog timer. This + * will be required for debugging the DSP firmware. + */ + if (cntl->debug_mode) { + snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, + 0x3F, 0x01); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x04, 0x00); + } else { + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, + 0x3F, 0x21); + } + + /* Make sure all the error interrupts are cleared */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B, 0xFF); + + reinit_completion(&cntl->boot_complete); + + /* Remove WDSP out of reset */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x02, 0x02); + + /* + * In debug mode, DSP may not boot up normally, + * wait indefinitely for DSP to boot. + */ + if (cntl->debug_mode) { + wait_for_completion(&cntl->boot_complete); + dev_dbg(codec->dev, "%s: WDSP booted in dbg mode\n", __func__); + cntl->is_wdsp_booted = true; + goto done; + } + + /* Boot in normal mode */ + ret = wait_for_completion_timeout(&cntl->boot_complete, + msecs_to_jiffies(WCD_DSP_BOOT_TIMEOUT_MS)); + if (!ret) { + dev_err(codec->dev, "%s: WDSP boot timed out\n", + __func__); + ret = -ETIMEDOUT; + goto err_boot; + } else { + /* + * Re-initialize the return code to 0, as in success case, + * it will hold the remaining time for completion timeout + */ + ret = 0; + } + + dev_dbg(codec->dev, "%s: WDSP booted in normal mode\n", __func__); + cntl->is_wdsp_booted = true; + + /* Enable WDOG */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, + 0x10, 0x10); +done: + /* If dsp booted up, then remove vote on SVS */ + if (cntl->is_wdsp_booted) + cntl->cdc_cb->cdc_vote_svs(codec, false); + + return ret; +err_boot: + /* call shutdown to perform cleanup */ + wcd_cntl_do_shutdown(cntl); + return ret; +} + +static irqreturn_t wcd_cntl_ipc_irq(int irq, void *data) +{ + struct wcd_dsp_cntl *cntl = data; + int ret; + + complete(&cntl->boot_complete); + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->signal_handler) + ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_IPC1_INTR, + NULL); + else + ret = -EINVAL; + + if (IS_ERR_VALUE(ret)) + dev_err(cntl->codec->dev, + "%s: Failed to handle irq %d\n", __func__, irq); + + return IRQ_HANDLED; +} + +static irqreturn_t wcd_cntl_err_irq(int irq, void *data) +{ + struct wcd_dsp_cntl *cntl = data; + struct snd_soc_codec *codec = cntl->codec; + struct wdsp_err_signal_arg arg; + u16 status = 0; + u8 reg_val; + int ret = 0; + + reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A); + status = status | reg_val; + + reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B); + status = status | (reg_val << 8); + + dev_info(codec->dev, "%s: error interrupt status = 0x%x\n", + __func__, status); + + if ((status & cntl->irqs.fatal_irqs) && + (cntl->m_dev && cntl->m_ops && cntl->m_ops->signal_handler)) { + arg.mem_dumps_enabled = cntl->ramdump_enable; + arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR; + arg.dump_size = WCD_934X_RAMDUMP_SIZE; + ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_ERR_INTR, + &arg); + if (IS_ERR_VALUE(ret)) + dev_err(cntl->codec->dev, + "%s: Failed to handle fatal irq 0x%x\n", + __func__, status & cntl->irqs.fatal_irqs); + wcd_cntl_change_online_state(cntl, 0); + } else { + dev_err(cntl->codec->dev, "%s: Invalid signal_handler\n", + __func__); + } + + return IRQ_HANDLED; +} + +static int wcd_control_handler(struct device *dev, void *priv_data, + enum wdsp_event_type event, void *data) +{ + struct wcd_dsp_cntl *cntl = priv_data; + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + switch (event) { + case WDSP_EVENT_POST_INIT: + case WDSP_EVENT_POST_DLOAD_CODE: + case WDSP_EVENT_DLOAD_FAILED: + case WDSP_EVENT_POST_SHUTDOWN: + + if (event == WDSP_EVENT_POST_DLOAD_CODE) + /* Mark DSP online since code download is complete */ + wcd_cntl_change_online_state(cntl, 1); + + /* Disable CPAR */ + wcd_cntl_cpar_ctrl(cntl, false); + /* Disable all the clocks */ + ret = wcd_cntl_clocks_disable(cntl); + if (IS_ERR_VALUE(ret)) + dev_err(codec->dev, + "%s: Failed to disable clocks, err = %d\n", + __func__, ret); + break; + + case WDSP_EVENT_PRE_DLOAD_DATA: + case WDSP_EVENT_PRE_DLOAD_CODE: + + /* Enable all the clocks */ + ret = wcd_cntl_clocks_enable(cntl); + if (IS_ERR_VALUE(ret)) { + dev_err(codec->dev, + "%s: Failed to enable clocks, err = %d\n", + __func__, ret); + goto done; + } + + /* Enable CPAR */ + wcd_cntl_cpar_ctrl(cntl, true); + + if (event == WDSP_EVENT_PRE_DLOAD_CODE) + wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_ALWAYS_ON); + else if (event == WDSP_EVENT_PRE_DLOAD_DATA) + wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE); + break; + + case WDSP_EVENT_DO_BOOT: + + ret = wcd_cntl_do_boot(cntl); + if (IS_ERR_VALUE(ret)) + dev_err(codec->dev, + "%s: WDSP boot failed, err = %d\n", + __func__, ret); + break; + + case WDSP_EVENT_DO_SHUTDOWN: + + wcd_cntl_do_shutdown(cntl); + wcd_cntl_disable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE); + break; + + default: + dev_dbg(codec->dev, "%s: unhandled event %d\n", + __func__, event); + } + +done: + return ret; +} + +static int wcd_cntl_sysfs_init(char *dir, struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + ret = kobject_init_and_add(&cntl->wcd_kobj, &wcd_cntl_ktype, + kernel_kobj, dir); + if (IS_ERR_VALUE(ret)) { + dev_err(codec->dev, + "%s: Failed to add kobject %s, err = %d\n", + __func__, dir, ret); + goto done; + } + + ret = sysfs_create_file(&cntl->wcd_kobj, &cntl_attr_boot.attr); + if (IS_ERR_VALUE(ret)) { + dev_err(codec->dev, + "%s: Failed to add wdsp_boot sysfs entry to %s\n", + __func__, dir); + goto fail_create_file; + } + + return ret; + +fail_create_file: + kobject_put(&cntl->wcd_kobj); +done: + return ret; +} + +static void wcd_cntl_sysfs_remove(struct wcd_dsp_cntl *cntl) +{ + sysfs_remove_file(&cntl->wcd_kobj, &cntl_attr_boot.attr); + kobject_put(&cntl->wcd_kobj); +} + +static void wcd_cntl_debugfs_init(char *dir, struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + + cntl->entry = debugfs_create_dir(dir, NULL); + if (IS_ERR_OR_NULL(dir)) { + dev_err(codec->dev, "%s debugfs_create_dir failed for %s\n", + __func__, dir); + goto done; + } + + debugfs_create_u32("debug_mode", 0644, + cntl->entry, &cntl->debug_mode); + debugfs_create_bool("ramdump_enable", 0644, + cntl->entry, &cntl->ramdump_enable); +done: + return; +} + +static void wcd_cntl_debugfs_remove(struct wcd_dsp_cntl *cntl) +{ + if (cntl) + debugfs_remove(cntl->entry); +} + +static int wcd_miscdev_release(struct inode *inode, struct file *filep) +{ + struct wcd_dsp_cntl *cntl = container_of(filep->private_data, + struct wcd_dsp_cntl, miscdev); + if (!cntl->m_dev || !cntl->m_ops || + !cntl->m_ops->vote_for_dsp) { + dev_err(cntl->codec->dev, + "%s: DSP not ready to boot\n", __func__); + return -EINVAL; + } + + /* Make sure the DSP users goes to zero upon closing dev node */ + while (cntl->boot_reqs > 0) { + cntl->m_ops->vote_for_dsp(cntl->m_dev, false); + cntl->boot_reqs--; + } + + return 0; +} + +static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf, + size_t count, loff_t *pos) +{ + struct wcd_dsp_cntl *cntl = container_of(filep->private_data, + struct wcd_dsp_cntl, miscdev); + char val[count]; + bool vote; + int ret = 0; + + if (count == 0 || count > 2) { + pr_err("%s: Invalid count = %zd\n", __func__, count); + ret = -EINVAL; + goto done; + } + + ret = copy_from_user(val, ubuf, count); + if (IS_ERR_VALUE(ret)) { + dev_err(cntl->codec->dev, + "%s: copy_from_user failed, err = %d\n", + __func__, ret); + ret = -EFAULT; + goto done; + } + + if (val[0] == '1') { + cntl->boot_reqs++; + vote = true; + } else if (val[0] == '0') { + if (cntl->boot_reqs == 0) { + dev_err(cntl->codec->dev, + "%s: WDSP already disabled\n", __func__); + ret = -EINVAL; + goto done; + } + cntl->boot_reqs--; + vote = false; + } else { + dev_err(cntl->codec->dev, "%s: Invalid value %s\n", + __func__, val); + ret = -EINVAL; + goto done; + } + + dev_dbg(cntl->codec->dev, + "%s: booted = %s, ref_cnt = %d, vote = %s\n", + __func__, cntl->is_wdsp_booted ? "true" : "false", + cntl->boot_reqs, vote ? "true" : "false"); + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->vote_for_dsp) + ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote); + else + ret = -EINVAL; +done: + if (ret) + return ret; + else + return count; +} + +static const struct file_operations wcd_miscdev_fops = { + .write = wcd_miscdev_write, + .release = wcd_miscdev_release, +}; + +static int wcd_cntl_miscdev_create(struct wcd_dsp_cntl *cntl) +{ + snprintf(cntl->miscdev_name, ARRAY_SIZE(cntl->miscdev_name), + "wcd_dsp%u_control", cntl->dsp_instance); + cntl->miscdev.minor = MISC_DYNAMIC_MINOR; + cntl->miscdev.name = cntl->miscdev_name; + cntl->miscdev.fops = &wcd_miscdev_fops; + cntl->miscdev.parent = cntl->codec->dev; + + return misc_register(&cntl->miscdev); +} + +static void wcd_cntl_miscdev_destroy(struct wcd_dsp_cntl *cntl) +{ + misc_deregister(&cntl->miscdev); +} + +static int wcd_control_init(struct device *dev, void *priv_data) +{ + struct wcd_dsp_cntl *cntl = priv_data; + struct snd_soc_codec *codec = cntl->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + int ret; + bool err_irq_requested = false; + + ret = wcd9xxx_request_irq(core_res, + cntl->irqs.cpe_ipc1_irq, + wcd_cntl_ipc_irq, "CPE IPC1", + cntl); + if (IS_ERR_VALUE(ret)) { + dev_err(codec->dev, + "%s: Failed to request cpe ipc irq, err = %d\n", + __func__, ret); + goto done; + } + + /* Unmask the fatal irqs */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, + ~(cntl->irqs.fatal_irqs & 0xFF)); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, + ~((cntl->irqs.fatal_irqs >> 8) & 0xFF)); + + /* + * CPE ERR irq is used only for error reporting from WCD DSP, + * even if this request fails, DSP can be function normally. + * Continuing with init even if the CPE ERR irq request fails. + */ + if (wcd9xxx_request_irq(core_res, cntl->irqs.cpe_err_irq, + wcd_cntl_err_irq, "CPE ERR", cntl)) + dev_info(codec->dev, "%s: Failed request_irq(cpe_err_irq)", + __func__); + else + err_irq_requested = true; + + + /* Enable all the clocks */ + ret = wcd_cntl_clocks_enable(cntl); + if (IS_ERR_VALUE(ret)) { + dev_err(codec->dev, "%s: Failed to enable clocks, err = %d\n", + __func__, ret); + goto err_clk_enable; + } + wcd_cntl_cpar_ctrl(cntl, true); + + return 0; + +err_clk_enable: + /* Mask all error interrupts */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0xFF); + + /* Free the irq's requested */ + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_ipc1_irq, cntl); + + if (err_irq_requested) + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_err_irq, cntl); +done: + return ret; +} + +static int wcd_control_deinit(struct device *dev, void *priv_data) +{ + struct wcd_dsp_cntl *cntl = priv_data; + struct snd_soc_codec *codec = cntl->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + + wcd_cntl_clocks_disable(cntl); + wcd_cntl_cpar_ctrl(cntl, false); + + /* Mask all error interrupts */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0xFF); + + /* Free the irq's requested */ + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_err_irq, cntl); + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_ipc1_irq, cntl); + + return 0; +} + +static struct wdsp_cmpnt_ops control_ops = { + .init = wcd_control_init, + .deinit = wcd_control_deinit, + .event_handler = wcd_control_handler, +}; + +static int wcd_ctrl_component_bind(struct device *dev, + struct device *master, + void *data) +{ + struct wcd_dsp_cntl *cntl; + struct snd_soc_codec *codec; + struct snd_card *card; + struct snd_info_entry *entry; + char proc_name[WCD_PROCFS_ENTRY_MAX_LEN]; + char wcd_cntl_dir_name[WCD_CNTL_DIR_NAME_LEN_MAX]; + int ret = 0; + + if (!dev || !master || !data) { + pr_err("%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + cntl = tavil_get_wcd_dsp_cntl(dev); + if (!cntl) { + dev_err(dev, "%s: Failed to get cntl reference\n", + __func__); + return -EINVAL; + } + + cntl->m_dev = master; + cntl->m_ops = data; + + if (!cntl->m_ops->register_cmpnt_ops) { + dev_err(dev, "%s: invalid master callback register_cmpnt_ops\n", + __func__); + ret = -EINVAL; + goto done; + } + + ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl, &control_ops); + if (ret) { + dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_cntl_miscdev_create(cntl); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: misc dev register failed, err = %d\n", + __func__, ret); + goto done; + } + + snprintf(wcd_cntl_dir_name, WCD_CNTL_DIR_NAME_LEN_MAX, + "%s%d", "wdsp", cntl->dsp_instance); + ret = wcd_cntl_sysfs_init(wcd_cntl_dir_name, cntl); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: sysfs_init failed, err = %d\n", + __func__, ret); + goto err_sysfs_init; + } + + wcd_cntl_debugfs_init(wcd_cntl_dir_name, cntl); + + codec = cntl->codec; + card = codec->component.card->snd_card; + snprintf(proc_name, WCD_PROCFS_ENTRY_MAX_LEN, "%s%d%s", "cpe", + cntl->dsp_instance, "_state"); + entry = snd_info_create_card_entry(card, proc_name, card->proc_root); + if (!entry) { + /* Do not treat this as Fatal error */ + dev_err(dev, "%s: Failed to create procfs entry %s\n", + __func__, proc_name); + goto err_sysfs_init; + } + + cntl->ssr_entry.entry = entry; + cntl->ssr_entry.offline = 1; + entry->size = WCD_PROCFS_ENTRY_MAX_LEN; + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->c.ops = &wdsp_ssr_entry_ops; + entry->private_data = cntl; + ret = snd_info_register(entry); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: Failed to register entry %s, err = %d\n", + __func__, proc_name, ret); + snd_info_free_entry(entry); + /* Let bind still happen even if creating the entry failed */ + ret = 0; + } +done: + return ret; + +err_sysfs_init: + wcd_cntl_miscdev_destroy(cntl); + return ret; +} + +static void wcd_ctrl_component_unbind(struct device *dev, + struct device *master, + void *data) +{ + struct wcd_dsp_cntl *cntl; + + if (!dev) { + pr_err("%s: Invalid device\n", __func__); + return; + } + + cntl = tavil_get_wcd_dsp_cntl(dev); + if (!cntl) { + dev_err(dev, "%s: Failed to get cntl reference\n", + __func__); + return; + } + + cntl->m_dev = NULL; + cntl->m_ops = NULL; + + /* Remove the sysfs entries */ + wcd_cntl_sysfs_remove(cntl); + + /* Remove the debugfs entries */ + wcd_cntl_debugfs_remove(cntl); + + /* Remove the misc device */ + wcd_cntl_miscdev_destroy(cntl); +} + +static const struct component_ops wcd_ctrl_component_ops = { + .bind = wcd_ctrl_component_bind, + .unbind = wcd_ctrl_component_unbind, +}; + +/* + * wcd_dsp_ssr_event: handle the SSR event raised by caller. + * @cntl: Handle to the wcd_dsp_cntl structure + * @event: The SSR event to be handled + * + * Notifies the manager driver about the SSR event. + * Returns 0 on success and negative error code on error. + */ +int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event) +{ + int ret = 0; + + if (!cntl) { + pr_err("%s: Invalid handle to control\n", __func__); + return -EINVAL; + } + + if (!cntl->m_dev || !cntl->m_ops || !cntl->m_ops->signal_handler) { + dev_err(cntl->codec->dev, + "%s: Invalid signal_handler callback\n", __func__); + return -EINVAL; + } + + switch (event) { + case WCD_CDC_DOWN_EVENT: + ret = cntl->m_ops->signal_handler(cntl->m_dev, + WDSP_CDC_DOWN_SIGNAL, + NULL); + if (IS_ERR_VALUE(ret)) + dev_err(cntl->codec->dev, + "%s: WDSP_CDC_DOWN_SIGNAL failed, err = %d\n", + __func__, ret); + wcd_cntl_change_online_state(cntl, 0); + break; + case WCD_CDC_UP_EVENT: + ret = cntl->m_ops->signal_handler(cntl->m_dev, + WDSP_CDC_UP_SIGNAL, + NULL); + if (IS_ERR_VALUE(ret)) + dev_err(cntl->codec->dev, + "%s: WDSP_CDC_UP_SIGNAL failed, err = %d\n", + __func__, ret); + break; + default: + dev_err(cntl->codec->dev, "%s: Invalid event %d\n", + __func__, event); + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL(wcd_dsp_ssr_event); + +/* + * wcd_dsp_cntl_init: Initialize the wcd-dsp control + * @codec: pointer to the codec handle + * @params: Parameters required to initialize wcd-dsp control + * + * This API is expected to be invoked by the codec driver and + * provide information essential for the wcd dsp control to + * configure and initialize the dsp + */ +void wcd_dsp_cntl_init(struct snd_soc_codec *codec, + struct wcd_dsp_params *params, + struct wcd_dsp_cntl **cntl) +{ + struct wcd_dsp_cntl *control; + int ret; + + if (!codec || !params) { + pr_err("%s: Invalid handle to %s\n", __func__, + (!codec) ? "codec" : "params"); + *cntl = NULL; + return; + } + + if (*cntl) { + pr_err("%s: cntl is non NULL, maybe already initialized ?\n", + __func__); + return; + } + + if (!params->cb || !params->cb->cdc_clk_en || + !params->cb->cdc_vote_svs) { + dev_err(codec->dev, + "%s: clk_en and vote_svs callbacks must be provided\n", + __func__); + return; + } + + control = kzalloc(sizeof(*control), GFP_KERNEL); + if (!(control)) + return; + + control->codec = codec; + control->clk_rate = params->clk_rate; + control->cdc_cb = params->cb; + control->dsp_instance = params->dsp_instance; + memcpy(&control->irqs, ¶ms->irqs, sizeof(control->irqs)); + init_completion(&control->boot_complete); + mutex_init(&control->clk_mutex); + mutex_init(&control->ssr_mutex); + init_waitqueue_head(&control->ssr_entry.offline_poll_wait); + + /* + * The default state of WDSP is in SVS mode. + * Vote for SVS now, the vote will be removed only + * after DSP is booted up. + */ + control->cdc_cb->cdc_vote_svs(codec, true); + + /* + * If this is the last component needed by master to be ready, + * then component_bind will be called within the component_add. + * Hence, the data pointer should be assigned before component_add, + * so that we can access it during this component's bind call. + */ + *cntl = control; + ret = component_add(codec->dev, &wcd_ctrl_component_ops); + if (ret) { + dev_err(codec->dev, "%s: component_add failed, err = %d\n", + __func__, ret); + kfree(*cntl); + *cntl = NULL; + } +} +EXPORT_SYMBOL(wcd_dsp_cntl_init); + +/* + * wcd_dsp_cntl_deinit: De-initialize the wcd-dsp control + * @cntl: The struct wcd_dsp_cntl to de-initialize + * + * This API is intended to be invoked by the codec driver + * to de-initialize the wcd dsp control + */ +void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl) +{ + struct wcd_dsp_cntl *control = *cntl; + struct snd_soc_codec *codec; + + /* If control is NULL, there is nothing to de-initialize */ + if (!control) + return; + codec = control->codec; + + /* + * Calling shutdown will cleanup all register states, + * irrespective of DSP was booted up or not. + */ + wcd_cntl_do_shutdown(control); + wcd_cntl_disable_memory(control, WCD_MEM_TYPE_SWITCHABLE); + wcd_cntl_disable_memory(control, WCD_MEM_TYPE_ALWAYS_ON); + + component_del(codec->dev, &wcd_ctrl_component_ops); + + mutex_destroy(&control->clk_mutex); + mutex_destroy(&control->ssr_mutex); + kfree(*cntl); + *cntl = NULL; +} +EXPORT_SYMBOL(wcd_dsp_cntl_deinit); diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h new file mode 100644 index 0000000000000000000000000000000000000000..e934638cc4871fb4da647e179b2f001a3ac9ce1a --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD934X_DSP_CNTL_H__ +#define __WCD934X_DSP_CNTL_H__ + +#include +#include + +enum cdc_ssr_event { + WCD_CDC_DOWN_EVENT, + WCD_CDC_UP_EVENT, +}; + +struct wcd_dsp_cdc_cb { + /* Callback to enable codec clock */ + int (*cdc_clk_en)(struct snd_soc_codec *, bool); + /* Callback to vote and unvote for SVS2 mode */ + void (*cdc_vote_svs)(struct snd_soc_codec *, bool); +}; + +struct wcd_dsp_irq_info { + /* IPC interrupt */ + int cpe_ipc1_irq; + + /* CPE error summary interrupt */ + int cpe_err_irq; + + /* + * Bit mask to indicate which of the + * error interrupts are to be considered + * as fatal. + */ + u16 fatal_irqs; +}; + +struct wcd_dsp_params { + struct wcd_dsp_cdc_cb *cb; + struct wcd_dsp_irq_info irqs; + + /* Rate at which the codec clock operates */ + u32 clk_rate; + + /* + * Represents the dsp instance, will be used + * to create sysfs and debugfs entries with + * directory wdsp + */ + u32 dsp_instance; +}; + +struct wdsp_ssr_entry { + u8 offline; + u8 offline_change; + wait_queue_head_t offline_poll_wait; + struct snd_info_entry *entry; +}; + +struct wcd_dsp_cntl { + /* Handle to codec */ + struct snd_soc_codec *codec; + + /* Clk rate of the codec clock */ + u32 clk_rate; + + /* Callbacks to codec driver */ + const struct wcd_dsp_cdc_cb *cdc_cb; + + /* Completion to indicate WDSP boot done */ + struct completion boot_complete; + + struct wcd_dsp_irq_info irqs; + u32 dsp_instance; + + /* Sysfs entries related */ + int boot_reqs; + struct kobject wcd_kobj; + + /* Debugfs related */ + struct dentry *entry; + u32 debug_mode; + bool ramdump_enable; + + /* WDSP manager drivers data */ + struct device *m_dev; + struct wdsp_mgr_ops *m_ops; + + /* clk related */ + struct mutex clk_mutex; + bool is_clk_enabled; + + /* Keep track of WDSP boot status */ + bool is_wdsp_booted; + + /* SSR related */ + struct wdsp_ssr_entry ssr_entry; + struct mutex ssr_mutex; + + /* Misc device related */ + char miscdev_name[256]; + struct miscdevice miscdev; +}; + +void wcd_dsp_cntl_init(struct snd_soc_codec *codec, + struct wcd_dsp_params *params, + struct wcd_dsp_cntl **cntl); +void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl); +int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event); +#endif /* end __WCD_DSP_CONTROL_H__ */ diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.c b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c new file mode 100644 index 0000000000000000000000000000000000000000..5dbdb9a2df0040e664c8e22f7517947f98765c9e --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c @@ -0,0 +1,1047 @@ +/* + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd934x.h" +#include "wcd934x-mbhc.h" +#include "../wcdcal-hwdep.h" + +#define TAVIL_ZDET_SUPPORTED true +/* Z value defined in milliohm */ +#define TAVIL_ZDET_VAL_32 32000 +#define TAVIL_ZDET_VAL_400 400000 +#define TAVIL_ZDET_VAL_1200 1200000 +#define TAVIL_ZDET_VAL_100K 100000000 +/* Z floating defined in ohms */ +#define TAVIL_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE + +#define TAVIL_ZDET_NUM_MEASUREMENTS 150 +#define TAVIL_MBHC_GET_C1(c) ((c & 0xC000) >> 14) +#define TAVIL_MBHC_GET_X1(x) (x & 0x3FFF) +/* Z value compared in milliOhm */ +#define TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000)) +#define TAVIL_MBHC_ZDET_CONST (86 * 16384) +#define TAVIL_MBHC_MOISTURE_RREF R_24_KOHM + +static struct wcd_mbhc_register + wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN", + WCD934X_ANA_MBHC_MECH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN", + WCD934X_ANA_MBHC_MECH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE", + WCD934X_ANA_MBHC_MECH, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL", + WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x30, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE", + WCD934X_ANA_MBHC_ELECT, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL", + WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL", + WCD934X_ANA_MBHC_MECH, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE", + WCD934X_ANA_MBHC_MECH, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE", + WCD934X_ANA_MBHC_MECH, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND", + WCD934X_ANA_MBHC_MECH, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC", + WCD934X_ANA_MBHC_ELECT, 0x06, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN", + WCD934X_ANA_MBHC_ELECT, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC", + WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC", + WCD934X_MBHC_NEW_CTL_1, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF", + WCD934X_MBHC_NEW_CTL_2, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN", + WCD934X_HPH_OCP_CTL, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x07, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL", + WCD934X_ANA_MBHC_ELECT, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL", + WCD934X_ANA_MICB2, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME", + WCD934X_HPH_CNP_WG_TIME, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN", + WCD934X_ANA_HPH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN", + WCD934X_ANA_HPH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN", + WCD934X_ANA_HPH, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE", + WCD934X_ANA_MBHC_RESULT_3, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", + WCD934X_ANA_MBHC_ZDET, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", + WCD934X_MBHC_STATUS_SPARE_1, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", + WCD934X_MBHC_NEW_CTL_2, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_DET_EN", + WCD934X_HPH_L_TEST, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_DET_EN", + WCD934X_HPH_R_TEST, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_STATUS", + WCD934X_INTR_PIN1_STATUS0, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_STATUS", + WCD934X_INTR_PIN1_STATUS0, 0x08, 3, 0), +}; + +static const struct wcd_mbhc_intr intr_ids = { + .mbhc_sw_intr = WCD934X_IRQ_MBHC_SW_DET, + .mbhc_btn_press_intr = WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, + .mbhc_btn_release_intr = WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, + .mbhc_hs_ins_intr = WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + .mbhc_hs_rem_intr = WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, + .hph_left_ocp = WCD934X_IRQ_HPH_PA_OCPL_FAULT, + .hph_right_ocp = WCD934X_IRQ_HPH_PA_OCPR_FAULT, +}; + + +static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = { + "cdc-vdd-mic-bias", +}; + +struct tavil_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static int tavil_mbhc_request_irq(struct snd_soc_codec *codec, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + return wcd9xxx_request_irq(core_res, irq, handler, name, data); +} + +static void tavil_mbhc_irq_control(struct snd_soc_codec *codec, + int irq, bool enable) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + if (enable) + wcd9xxx_enable_irq(core_res, irq); + else + wcd9xxx_disable_irq(core_res, irq); +} + +static int tavil_mbhc_free_irq(struct snd_soc_codec *codec, + int irq, void *data) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, irq, data); + return 0; +} + +static void tavil_mbhc_clk_setup(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, + 0x80, 0x80); + else + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, + 0x80, 0x00); +} + +static int tavil_mbhc_btn_to_num(struct snd_soc_codec *codec) +{ + return snd_soc_read(codec, WCD934X_ANA_MBHC_RESULT_3) & 0x7; +} + +static int tavil_enable_ext_mb_source(struct wcd_mbhc *mbhc, + bool turn_on) +{ + struct wcd934x_mbhc *wcd934x_mbhc; + struct snd_soc_codec *codec = mbhc->codec; + struct wcd934x_on_demand_supply *supply; + int ret = 0; + + wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc); + + supply = &wcd934x_mbhc->on_demand_list[WCD934X_ON_DEMAND_MICBIAS]; + if (!supply->supply) { + dev_dbg(codec->dev, "%s: warning supply not present ond for %s\n", + __func__, "onDemand Micbias"); + return ret; + } + + dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on, + supply->ondemand_supply_count); + + if (turn_on) { + if (!(supply->ondemand_supply_count)) { + ret = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + } + supply->ondemand_supply_count++; + } else { + if (supply->ondemand_supply_count > 0) + supply->ondemand_supply_count--; + if (!(supply->ondemand_supply_count)) { + ret = snd_soc_dapm_disable_pin( + snd_soc_codec_get_dapm(codec), + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + } + } + + if (ret) + dev_err(codec->dev, "%s: Failed to %s external micbias source\n", + __func__, turn_on ? "enable" : "disabled"); + else + dev_dbg(codec->dev, "%s: %s external micbias source\n", + __func__, turn_on ? "Enabled" : "Disabled"); + + return ret; +} + +static void tavil_mbhc_mbhc_bias_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_ELECT, + 0x01, 0x01); + else + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_ELECT, + 0x01, 0x00); +} + +static void tavil_mbhc_program_btn_thr(struct snd_soc_codec *codec, + s16 *btn_low, s16 *btn_high, + int num_btn, bool is_micbias) +{ + int i; + int vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(codec->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + /* + * Tavil just needs one set of thresholds for button detection + * due to micbias voltage ramp to pullup upon button press. So + * btn_low and is_micbias are ignored and always program button + * thresholds using btn_high. + */ + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN0 + i, + 0xFC, vth << 2); + dev_dbg(codec->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool tavil_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + bool ret = 0; + + if (lock) + ret = wcd9xxx_lock_sleep(core_res); + else + wcd9xxx_unlock_sleep(core_res); + + return ret; +} + +static int tavil_mbhc_register_notifier(struct wcd_mbhc *mbhc, + struct notifier_block *nblock, + bool enable) +{ + struct wcd934x_mbhc *wcd934x_mbhc; + + wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc); + + if (enable) + return blocking_notifier_chain_register(&wcd934x_mbhc->notifier, + nblock); + else + return blocking_notifier_chain_unregister( + &wcd934x_mbhc->notifier, nblock); +} + +static bool tavil_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num) +{ + u8 val; + + if (micb_num == MIC_BIAS_2) { + val = (snd_soc_read(mbhc->codec, WCD934X_ANA_MICB2) >> 6); + if (val == 0x01) + return true; + } + return false; +} + +static bool tavil_mbhc_hph_pa_on_status(struct snd_soc_codec *codec) +{ + return (snd_soc_read(codec, WCD934X_ANA_HPH) & 0xC0) ? true : false; +} + +static void tavil_mbhc_hph_l_pull_up_control( + struct snd_soc_codec *codec, + enum mbhc_hs_pullup_iref pull_up_cur) +{ + /* Default pull up current to 2uA */ + if (pull_up_cur < I_OFF || pull_up_cur > I_3P0_UA || + pull_up_cur == I_DEFAULT) + pull_up_cur = I_2P0_UA; + + dev_dbg(codec->dev, "%s: HS pull up current:%d\n", + __func__, pull_up_cur); + + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, + 0xC0, pull_up_cur << 6); +} + +static int tavil_mbhc_request_micbias(struct snd_soc_codec *codec, + int micb_num, int req) +{ + int ret; + + /* + * If micbias is requested, make sure that there + * is vote to enable mclk + */ + if (req == MICB_ENABLE) + tavil_cdc_mclk_enable(codec, true); + + ret = tavil_micbias_control(codec, micb_num, req, false); + + /* + * Release vote for mclk while requesting for + * micbias disable + */ + if (req == MICB_DISABLE) + tavil_cdc_mclk_enable(codec, false); + + return ret; +} + +static void tavil_mbhc_micb_ramp_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP, + 0x1C, 0x0C); + snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP, + 0x1C, 0x00); + } +} + +static struct firmware_cal *tavil_get_hwdep_fw_cal(struct wcd_mbhc *mbhc, + enum wcd_cal_type type) +{ + struct wcd934x_mbhc *wcd934x_mbhc; + struct firmware_cal *hwdep_cal; + struct snd_soc_codec *codec = mbhc->codec; + + wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc); + + if (!codec) { + pr_err("%s: NULL codec pointer\n", __func__); + return NULL; + } + hwdep_cal = wcdcal_get_fw_cal(wcd934x_mbhc->fw_data, type); + if (!hwdep_cal) + dev_err(codec->dev, "%s: cal not sent by %d\n", + __func__, type); + + return hwdep_cal; +} + +static int tavil_mbhc_micb_ctrl_threshold_mic(struct snd_soc_codec *codec, + int micb_num, bool req_en) +{ + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + int rc, micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (pdata->micbias.micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : pdata->micbias.micb2_mv; + + rc = tavil_mbhc_micb_adjust_voltage(codec, micb_mv, MIC_BIAS_2); + + return rc; +} + +static inline void tavil_mbhc_get_result_params(struct wcd9xxx *wcd9xxx, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + int i; + int val, val1; + s16 c1; + s32 x1, d1; + int32_t denom; + int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(wcd9xxx->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < TAVIL_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(wcd9xxx->regmap, WCD934X_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(wcd9xxx->regmap, WCD934X_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(wcd9xxx->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = TAVIL_MBHC_GET_X1(val); + c1 = TAVIL_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if ((c1 < 2) && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + dev_dbg(wcd9xxx->dev, + "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (TAVIL_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = TAVIL_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(wcd9xxx->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + regmap_bulk_read(wcd9xxx->regmap, + WCD934X_ANA_MBHC_RESULT_1, (u8 *)&val, 2); + x1 = TAVIL_MBHC_GET_X1(val); + i++; + if (i == TAVIL_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void tavil_mbhc_zdet_ramp(struct snd_soc_codec *codec, + struct tavil_mbhc_zdet_param *zdet_param, + int32_t *zl, int32_t *zr, s16 *d1_a) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int32_t zdet = 0; + + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x70, + zdet_param->ldo_ctl << 4); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN5, 0xFC, + zdet_param->btn5); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN6, 0xFC, + zdet_param->btn6); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN7, 0xFC, + zdet_param->btn7); + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x0F, + zdet_param->noff); + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_RAMP_CTL, 0x0F, + zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ZDET, 0x80, 0x80); + dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + tavil_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ZDET, 0x40, 0x40); + dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + tavil_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static inline void tavil_wcd_mbhc_qfuse_cal(struct snd_soc_codec *codec, + int32_t *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (TAVIL_ZDET_VAL_400/1000)) + q1 = snd_soc_read(codec, + WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r)); + else + q1 = snd_soc_read(codec, + WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void tavil_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + s16 reg0, reg1, reg2, reg3, reg4; + int32_t z1L, z1R, z1Ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + struct tavil_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct tavil_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + reg0 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN5); + reg1 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN6); + reg2 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN7); + reg3 = snd_soc_read(codec, WCD934X_MBHC_CTL_CLK); + reg4 = snd_soc_read(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_read(codec, WCD934X_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_MECH, 0x80, 0x00); + + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_MECH, 0x01, 0x00); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + + if (!TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) + goto left_ch_impedance; + + /* Second ramp for left ch */ + if (z1L < TAVIL_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1L > TAVIL_ZDET_VAL_400) && (z1L <= TAVIL_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1L > TAVIL_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + +left_ch_impedance: + if ((z1L == TAVIL_ZDET_FLOATING_IMPEDANCE) || + (z1L > TAVIL_ZDET_VAL_100K)) { + *zl = TAVIL_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1L/1000; + tavil_wcd_mbhc_qfuse_cal(codec, zl, 0); + } + dev_dbg(codec->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* Start of right impedance ramp and calculation */ + tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + if (TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { + if (((z1R > TAVIL_ZDET_VAL_1200) && + (zdet_param_ptr->noff == 0x6)) || + ((*zl) != TAVIL_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* Second ramp for right ch */ + if (z1R < TAVIL_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1R > TAVIL_ZDET_VAL_400) && + (z1R <= TAVIL_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1R > TAVIL_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + } +right_ch_impedance: + if ((z1R == TAVIL_ZDET_FLOATING_IMPEDANCE) || + (z1R > TAVIL_ZDET_VAL_100K)) { + *zr = TAVIL_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1R/1000; + tavil_wcd_mbhc_qfuse_cal(codec, zr, 1); + } + dev_dbg(codec->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* Mono/stereo detection */ + if ((*zl == TAVIL_ZDET_FLOATING_IMPEDANCE) && + (*zr == TAVIL_ZDET_FLOATING_IMPEDANCE)) { + dev_dbg(codec->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == TAVIL_ZDET_FLOATING_IMPEDANCE) || + (*zr == TAVIL_ZDET_FLOATING_IMPEDANCE) || + ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) || + ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) { + dev_dbg(codec->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + goto zdet_complete; + } + snd_soc_update_bits(codec, WCD934X_HPH_R_ATEST, 0x02, 0x02); + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, 0x40, 0x01); + if (*zl < (TAVIL_ZDET_VAL_32/1000)) + tavil_mbhc_zdet_ramp(codec, &zdet_param[0], &z1Ls, NULL, d1); + else + tavil_mbhc_zdet_ramp(codec, &zdet_param[1], &z1Ls, NULL, d1); + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, 0x40, 0x00); + snd_soc_update_bits(codec, WCD934X_HPH_R_ATEST, 0x02, 0x00); + z1Ls /= 1000; + tavil_wcd_mbhc_qfuse_cal(codec, &z1Ls, 0); + /* Parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls); + z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl)); + if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) { + dev_dbg(codec->dev, "%s: stereo plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } else { + dev_dbg(codec->dev, "%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } + +zdet_complete: + snd_soc_write(codec, WCD934X_ANA_MBHC_BTN5, reg0); + snd_soc_write(codec, WCD934X_ANA_MBHC_BTN6, reg1); + snd_soc_write(codec, WCD934X_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_write(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_write(codec, WCD934X_MBHC_CTL_CLK, reg3); + if (is_fsm_disable) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ELECT, 0x80, 0x80); +} + +static void tavil_mbhc_gnd_det_ctrl(struct snd_soc_codec *codec, bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH, + 0x40, 0x40); + } else { + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH, + 0x02, 0x00); + } +} + +static void tavil_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, + 0x40, 0x40); + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, + 0x10, 0x10); + } else { + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, + 0x10, 0x00); + } +} +static void tavil_mbhc_moisture_config(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + if (TAVIL_MBHC_MOISTURE_RREF == R_OFF) + return; + + /* Donot enable moisture detection if jack type is NC */ + if (!mbhc->hphl_swh) { + dev_dbg(codec->dev, "%s: disable moisture detection for NC\n", + __func__); + return; + } + + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_2, + 0x0C, TAVIL_MBHC_MOISTURE_RREF << 2); +} + +static bool tavil_hph_register_recovery(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (!wcd934x_mbhc) + return false; + + wcd934x_mbhc->is_hph_recover = false; + snd_soc_dapm_force_enable_pin(snd_soc_codec_get_dapm(codec), + "RESET_HPH_REGISTERS"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + + snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec), + "RESET_HPH_REGISTERS"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + + return wcd934x_mbhc->is_hph_recover; +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .request_irq = tavil_mbhc_request_irq, + .irq_control = tavil_mbhc_irq_control, + .free_irq = tavil_mbhc_free_irq, + .clk_setup = tavil_mbhc_clk_setup, + .map_btn_code_to_num = tavil_mbhc_btn_to_num, + .enable_mb_source = tavil_enable_ext_mb_source, + .mbhc_bias = tavil_mbhc_mbhc_bias_control, + .set_btn_thr = tavil_mbhc_program_btn_thr, + .lock_sleep = tavil_mbhc_lock_sleep, + .register_notifier = tavil_mbhc_register_notifier, + .micbias_enable_status = tavil_mbhc_micb_en_status, + .hph_pa_on_status = tavil_mbhc_hph_pa_on_status, + .hph_pull_up_control = tavil_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = tavil_mbhc_request_micbias, + .mbhc_micb_ramp_control = tavil_mbhc_micb_ramp_control, + .get_hwdep_fw_cal = tavil_get_hwdep_fw_cal, + .mbhc_micb_ctrl_thr_mic = tavil_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = tavil_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = tavil_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = tavil_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = tavil_mbhc_moisture_config, + .hph_register_recovery = tavil_hph_register_recovery, +}; + +static struct regulator *tavil_codec_find_ondemand_regulator( + struct snd_soc_codec *codec, const char *name) +{ + int i; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + + for (i = 0; i < wcd9xxx->num_of_supplies; ++i) { + if (pdata->regulator[i].ondemand && + wcd9xxx->supplies[i].supply && + !strcmp(wcd9xxx->supplies[i].supply, name)) + return wcd9xxx->supplies[i].consumer; + } + + dev_dbg(codec->dev, "Warning: regulator not found:%s\n", + name); + return NULL; +} + +static int tavil_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + struct wcd_mbhc *mbhc; + + if (!wcd934x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + mbhc = &wcd934x_mbhc->wcd_mbhc; + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type); + + return 0; +} + +static int tavil_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl, zr; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (!wcd934x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(&wcd934x_mbhc->wcd_mbhc, &zl, &zr); + dev_dbg(codec->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + tavil_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + tavil_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + tavil_hph_impedance_get, NULL), +}; + +/* + * tavil_mbhc_hs_detect: starts mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + * @mbhc_cfg: handle to mbhc configuration structure + * return 0 if mbhc_start is success or error code in case of failure + */ +int tavil_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (!wcd934x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + return wcd_mbhc_start(&wcd934x_mbhc->wcd_mbhc, mbhc_cfg); +} +EXPORT_SYMBOL(tavil_mbhc_hs_detect); + +/* + * tavil_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + */ +void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (!wcd934x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return; + } + wcd_mbhc_stop(&wcd934x_mbhc->wcd_mbhc); +} +EXPORT_SYMBOL(tavil_mbhc_hs_detect_exit); + +/* + * tavil_mbhc_post_ssr_init: initialize mbhc for tavil post subsystem restart + * @mbhc: poniter to wcd934x_mbhc structure + * @codec: handle to snd_soc_codec * + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc, + struct snd_soc_codec *codec) +{ + int ret; + + if (!mbhc || !codec) + return -EINVAL; + + wcd_mbhc_deinit(&mbhc->wcd_mbhc); + ret = wcd_mbhc_init(&mbhc->wcd_mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto done; + } + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01); + +done: + return ret; +} +EXPORT_SYMBOL(tavil_mbhc_post_ssr_init); + +/* + * tavil_mbhc_init: initialize mbhc for tavil + * @mbhc: poniter to wcd934x_mbhc struct pointer to store the configs + * @codec: handle to snd_soc_codec * + * @fw_data: handle to firmware data + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, struct snd_soc_codec *codec, + struct fw_info *fw_data) +{ + struct regulator *supply; + struct wcd934x_mbhc *wcd934x_mbhc; + int ret; + + wcd934x_mbhc = devm_kzalloc(codec->dev, sizeof(struct wcd934x_mbhc), + GFP_KERNEL); + if (!wcd934x_mbhc) + return -ENOMEM; + + wcd934x_mbhc->wcd9xxx = dev_get_drvdata(codec->dev->parent); + wcd934x_mbhc->fw_data = fw_data; + BLOCKING_INIT_NOTIFIER_HEAD(&wcd934x_mbhc->notifier); + + ret = wcd_mbhc_init(&wcd934x_mbhc->wcd_mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto err; + } + + supply = tavil_codec_find_ondemand_regulator(codec, + on_demand_supply_name[WCD934X_ON_DEMAND_MICBIAS]); + if (supply) { + wcd934x_mbhc->on_demand_list[ + WCD934X_ON_DEMAND_MICBIAS].supply = + supply; + wcd934x_mbhc->on_demand_list[ + WCD934X_ON_DEMAND_MICBIAS].ondemand_supply_count = + 0; + } + + (*mbhc) = wcd934x_mbhc; + snd_soc_add_codec_controls(codec, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_codec_controls(codec, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01); + + return 0; +err: + devm_kfree(codec->dev, wcd934x_mbhc); + return ret; +} +EXPORT_SYMBOL(tavil_mbhc_init); + +/* + * tavil_mbhc_deinit: deinitialize mbhc for tavil + * @codec: handle to snd_soc_codec * + */ +void tavil_mbhc_deinit(struct snd_soc_codec *codec) +{ + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (wcd934x_mbhc) { + wcd_mbhc_deinit(&wcd934x_mbhc->wcd_mbhc); + devm_kfree(codec->dev, wcd934x_mbhc); + } +} +EXPORT_SYMBOL(tavil_mbhc_deinit); diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.h b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h new file mode 100644 index 0000000000000000000000000000000000000000..95d8e3d8e8aeb6ff29c4911944905ef56e758b06 --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __WCD934X_MBHC_H__ +#define __WCD934X_MBHC_H__ +#include "../wcd-mbhc-v2.h" + +enum wcd934x_on_demand_supply_name { + WCD934X_ON_DEMAND_MICBIAS = 0, + WCD934X_ON_DEMAND_SUPPLIES_MAX, +}; + +struct wcd934x_on_demand_supply { + struct regulator *supply; + int ondemand_supply_count; +}; + +struct wcd934x_mbhc { + struct wcd_mbhc wcd_mbhc; + struct blocking_notifier_head notifier; + struct wcd934x_on_demand_supply on_demand_list[ + WCD934X_ON_DEMAND_SUPPLIES_MAX]; + struct wcd9xxx *wcd9xxx; + struct fw_info *fw_data; + bool mbhc_started; + bool is_hph_recover; +}; + +extern int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, + struct snd_soc_codec *codec, + struct fw_info *fw_data); +extern void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec); +extern int tavil_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg); +extern void tavil_mbhc_deinit(struct snd_soc_codec *codec); +extern int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc, + struct snd_soc_codec *codec); +#endif /* __WCD934X_MBHC_H__ */ diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h new file mode 100644 index 0000000000000000000000000000000000000000..940fdf89d36194fc3ed2aa9477ec93f4989700c6 --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h @@ -0,0 +1,1154 @@ +/* + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef WCD934X_ROUTING_H +#define WCD934X_ROUTING_H + +#include + +const struct snd_soc_dapm_route tavil_slim_audio_map[] = { + + /* Virtual input widgets */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, + + /* Virtual input widget Mixer */ + {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0"}, + {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1"}, + {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2"}, + {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3"}, + {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4"}, + {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5"}, + {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6"}, + {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7"}, + {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8"}, + {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9"}, + {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10"}, + {"AIF1_CAP Mixer", "SLIM TX11", "SLIM TX11"}, + {"AIF1_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + + {"AIF2_CAP Mixer", "SLIM TX0", "SLIM TX0"}, + {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1"}, + {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2"}, + {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3"}, + {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4"}, + {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5"}, + {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6"}, + {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7"}, + {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8"}, + {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9"}, + {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10"}, + {"AIF2_CAP Mixer", "SLIM TX11", "SLIM TX11"}, + {"AIF2_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + + {"AIF3_CAP Mixer", "SLIM TX0", "SLIM TX0"}, + {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1"}, + {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2"}, + {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3"}, + {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4"}, + {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5"}, + {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6"}, + {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7"}, + {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8"}, + {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9"}, + {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10"}, + {"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11"}, + {"AIF3_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + + {"AIF4_MAD Mixer", "SLIM TX13", "SLIM TX13"}, + + {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"}, + + {"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"}, + + {"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"}, + + {"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"}, + + {"SLIM RX0", NULL, "SLIM RX0 MUX"}, + {"SLIM RX1", NULL, "SLIM RX1 MUX"}, + {"SLIM RX2", NULL, "SLIM RX2 MUX"}, + {"SLIM RX3", NULL, "SLIM RX3 MUX"}, + {"SLIM RX4", NULL, "SLIM RX4 MUX"}, + {"SLIM RX5", NULL, "SLIM RX5 MUX"}, + {"SLIM RX6", NULL, "SLIM RX6 MUX"}, + {"SLIM RX7", NULL, "SLIM RX7 MUX"}, + +}; + +const struct snd_soc_dapm_route tavil_audio_map[] = { + + /* MAD */ + {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"}, + {"MAD_SEL MUX", "MSM", "MADINPUT"}, + + {"MAD_INP MUX", "MAD", "MAD_SEL MUX"}, + {"MAD_INP MUX", "DEC1", "ADC MUX1"}, + + {"MAD_BROADCAST", "Switch", "MAD_INP MUX"}, + {"MAD_CPE1", "Switch", "MAD_INP MUX"}, + {"MAD_CPE2", "Switch", "MAD_INP MUX"}, + + {"MAD_CPE_OUT1", NULL, "MAD_CPE1"}, + {"MAD_CPE_OUT2", NULL, "MAD_CPE2"}, + + /* VI Feedback */ + {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"}, + {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"}, + {"AIF4 VI", NULL, "AIF4_VI Mixer"}, + + /* CDC Tx interface with SLIMBUS */ + {"SLIM TX0", NULL, "CDC_IF TX0 MUX"}, + {"SLIM TX1", NULL, "CDC_IF TX1 MUX"}, + {"SLIM TX2", NULL, "CDC_IF TX2 MUX"}, + {"SLIM TX3", NULL, "CDC_IF TX3 MUX"}, + {"SLIM TX4", NULL, "CDC_IF TX4 MUX"}, + {"SLIM TX5", NULL, "CDC_IF TX5 MUX"}, + {"SLIM TX6", NULL, "CDC_IF TX6 MUX"}, + {"SLIM TX7", NULL, "CDC_IF TX7 MUX"}, + {"SLIM TX8", NULL, "CDC_IF TX8 MUX"}, + {"SLIM TX9", NULL, "CDC_IF TX9 MUX"}, + {"SLIM TX10", NULL, "CDC_IF TX10 MUX"}, + {"SLIM TX11", NULL, "CDC_IF TX11 MUX"}, + {"SLIM TX13", NULL, "CDC_IF TX13 MUX"}, + + {"CDC_IF TX0 MUX", "DEC0", "ADC MUX0"}, + {"CDC_IF TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, + {"CDC_IF TX0 MUX", "DEC0_192", "ADC US MUX0"}, + + {"CDC_IF TX1 MUX", "DEC1", "ADC MUX1"}, + {"CDC_IF TX1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"}, + {"CDC_IF TX1 MUX", "DEC1_192", "ADC US MUX1"}, + + {"CDC_IF TX2 MUX", "DEC2", "ADC MUX2"}, + {"CDC_IF TX2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"}, + {"CDC_IF TX2 MUX", "DEC2_192", "ADC US MUX2"}, + + {"CDC_IF TX3 MUX", "DEC3", "ADC MUX3"}, + {"CDC_IF TX3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"}, + {"CDC_IF TX3 MUX", "DEC3_192", "ADC US MUX3"}, + + {"CDC_IF TX4 MUX", "DEC4", "ADC MUX4"}, + {"CDC_IF TX4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"}, + {"CDC_IF TX4 MUX", "DEC4_192", "ADC US MUX4"}, + + {"CDC_IF TX5 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + {"CDC_IF TX5 MUX", "DEC5_192", "ADC US MUX5"}, + + {"CDC_IF TX6 MUX", "DEC6", "ADC MUX6"}, + {"CDC_IF TX6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"}, + {"CDC_IF TX6 MUX", "DEC6_192", "ADC US MUX6"}, + + {"CDC_IF TX7 MUX", "DEC7", "ADC MUX7"}, + {"CDC_IF TX7 MUX", "RX_MIX_TX7", "RX MIX TX7 MUX"}, + {"CDC_IF TX7 MUX", "DEC7_192", "ADC US MUX7"}, + + {"CDC_IF TX8 MUX", "DEC8", "ADC MUX8"}, + {"CDC_IF TX8 MUX", "RX_MIX_TX8", "RX MIX TX8 MUX"}, + {"CDC_IF TX8 MUX", "DEC8_192", "ADC US MUX8"}, + + {"CDC_IF TX9 MUX", "DEC7", "ADC MUX7"}, + {"CDC_IF TX9 MUX", "DEC7_192", "ADC US MUX7"}, + {"CDC_IF TX10 MUX", "DEC6", "ADC MUX6"}, + {"CDC_IF TX10 MUX", "DEC6_192", "ADC US MUX6"}, + + {"CDC_IF TX11 MUX", "DEC_0_5", "CDC_IF TX11 INP1 MUX"}, + {"CDC_IF TX11 MUX", "DEC_9_12", "CDC_IF TX11 INP1 MUX"}, + {"CDC_IF TX11 INP1 MUX", "DEC0", "ADC MUX0"}, + {"CDC_IF TX11 INP1 MUX", "DEC1", "ADC MUX1"}, + {"CDC_IF TX11 INP1 MUX", "DEC2", "ADC MUX2"}, + {"CDC_IF TX11 INP1 MUX", "DEC3", "ADC MUX3"}, + {"CDC_IF TX11 INP1 MUX", "DEC4", "ADC MUX4"}, + {"CDC_IF TX11 INP1 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX11 INP1 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + + {"CDC_IF TX13 MUX", "MAD_BRDCST", "MAD_BROADCAST"}, + {"CDC_IF TX13 MUX", "CDC_DEC_5", "CDC_IF TX13 INP1 MUX"}, + {"CDC_IF TX13 INP1 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX13 INP1 MUX", "DEC5_192", "ADC US MUX5"}, + + {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX3 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX4 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX5 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX6 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX7 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX8 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"ADC US MUX0", "US_Switch", "ADC MUX0"}, + {"ADC US MUX1", "US_Switch", "ADC MUX1"}, + {"ADC US MUX2", "US_Switch", "ADC MUX2"}, + {"ADC US MUX3", "US_Switch", "ADC MUX3"}, + {"ADC US MUX4", "US_Switch", "ADC MUX4"}, + {"ADC US MUX5", "US_Switch", "ADC MUX5"}, + {"ADC US MUX6", "US_Switch", "ADC MUX6"}, + {"ADC US MUX7", "US_Switch", "ADC MUX7"}, + {"ADC US MUX8", "US_Switch", "ADC MUX8"}, + + {"ADC MUX0", "DMIC", "DMIC MUX0"}, + {"ADC MUX0", "AMIC", "AMIC MUX0"}, + {"ADC MUX1", "DMIC", "DMIC MUX1"}, + {"ADC MUX1", "AMIC", "AMIC MUX1"}, + {"ADC MUX2", "DMIC", "DMIC MUX2"}, + {"ADC MUX2", "AMIC", "AMIC MUX2"}, + {"ADC MUX3", "DMIC", "DMIC MUX3"}, + {"ADC MUX3", "AMIC", "AMIC MUX3"}, + {"ADC MUX4", "DMIC", "DMIC MUX4"}, + {"ADC MUX4", "AMIC", "AMIC MUX4"}, + {"ADC MUX5", "DMIC", "DMIC MUX5"}, + {"ADC MUX5", "AMIC", "AMIC MUX5"}, + {"ADC MUX6", "DMIC", "DMIC MUX6"}, + {"ADC MUX6", "AMIC", "AMIC MUX6"}, + {"ADC MUX7", "DMIC", "DMIC MUX7"}, + {"ADC MUX7", "AMIC", "AMIC MUX7"}, + {"ADC MUX8", "DMIC", "DMIC MUX8"}, + {"ADC MUX8", "AMIC", "AMIC MUX8"}, + {"ADC MUX10", "DMIC", "DMIC MUX10"}, + {"ADC MUX10", "AMIC", "AMIC MUX10"}, + {"ADC MUX11", "DMIC", "DMIC MUX11"}, + {"ADC MUX11", "AMIC", "AMIC MUX11"}, + {"ADC MUX12", "DMIC", "DMIC MUX12"}, + {"ADC MUX12", "AMIC", "AMIC MUX12"}, + {"ADC MUX13", "DMIC", "DMIC MUX13"}, + {"ADC MUX13", "AMIC", "AMIC MUX13"}, + + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX13"}, + + {"DMIC MUX0", "DMIC0", "DMIC0"}, + {"DMIC MUX0", "DMIC1", "DMIC1"}, + {"DMIC MUX0", "DMIC2", "DMIC2"}, + {"DMIC MUX0", "DMIC3", "DMIC3"}, + {"DMIC MUX0", "DMIC4", "DMIC4"}, + {"DMIC MUX0", "DMIC5", "DMIC5"}, + {"AMIC MUX0", "ADC1", "ADC1"}, + {"AMIC MUX0", "ADC2", "ADC2"}, + {"AMIC MUX0", "ADC3", "ADC3"}, + {"AMIC MUX0", "ADC4", "ADC4"}, + + {"DMIC MUX1", "DMIC0", "DMIC0"}, + {"DMIC MUX1", "DMIC1", "DMIC1"}, + {"DMIC MUX1", "DMIC2", "DMIC2"}, + {"DMIC MUX1", "DMIC3", "DMIC3"}, + {"DMIC MUX1", "DMIC4", "DMIC4"}, + {"DMIC MUX1", "DMIC5", "DMIC5"}, + {"AMIC MUX1", "ADC1", "ADC1"}, + {"AMIC MUX1", "ADC2", "ADC2"}, + {"AMIC MUX1", "ADC3", "ADC3"}, + {"AMIC MUX1", "ADC4", "ADC4"}, + + {"DMIC MUX2", "DMIC0", "DMIC0"}, + {"DMIC MUX2", "DMIC1", "DMIC1"}, + {"DMIC MUX2", "DMIC2", "DMIC2"}, + {"DMIC MUX2", "DMIC3", "DMIC3"}, + {"DMIC MUX2", "DMIC4", "DMIC4"}, + {"DMIC MUX2", "DMIC5", "DMIC5"}, + {"AMIC MUX2", "ADC1", "ADC1"}, + {"AMIC MUX2", "ADC2", "ADC2"}, + {"AMIC MUX2", "ADC3", "ADC3"}, + {"AMIC MUX2", "ADC4", "ADC4"}, + + {"DMIC MUX3", "DMIC0", "DMIC0"}, + {"DMIC MUX3", "DMIC1", "DMIC1"}, + {"DMIC MUX3", "DMIC2", "DMIC2"}, + {"DMIC MUX3", "DMIC3", "DMIC3"}, + {"DMIC MUX3", "DMIC4", "DMIC4"}, + {"DMIC MUX3", "DMIC5", "DMIC5"}, + {"AMIC MUX3", "ADC1", "ADC1"}, + {"AMIC MUX3", "ADC2", "ADC2"}, + {"AMIC MUX3", "ADC3", "ADC3"}, + {"AMIC MUX3", "ADC4", "ADC4"}, + + {"DMIC MUX4", "DMIC0", "DMIC0"}, + {"DMIC MUX4", "DMIC1", "DMIC1"}, + {"DMIC MUX4", "DMIC2", "DMIC2"}, + {"DMIC MUX4", "DMIC3", "DMIC3"}, + {"DMIC MUX4", "DMIC4", "DMIC4"}, + {"DMIC MUX4", "DMIC5", "DMIC5"}, + {"AMIC MUX4", "ADC1", "ADC1"}, + {"AMIC MUX4", "ADC2", "ADC2"}, + {"AMIC MUX4", "ADC3", "ADC3"}, + {"AMIC MUX4", "ADC4", "ADC4"}, + + {"DMIC MUX5", "DMIC0", "DMIC0"}, + {"DMIC MUX5", "DMIC1", "DMIC1"}, + {"DMIC MUX5", "DMIC2", "DMIC2"}, + {"DMIC MUX5", "DMIC3", "DMIC3"}, + {"DMIC MUX5", "DMIC4", "DMIC4"}, + {"DMIC MUX5", "DMIC5", "DMIC5"}, + {"AMIC MUX5", "ADC1", "ADC1"}, + {"AMIC MUX5", "ADC2", "ADC2"}, + {"AMIC MUX5", "ADC3", "ADC3"}, + {"AMIC MUX5", "ADC4", "ADC4"}, + + {"DMIC MUX6", "DMIC0", "DMIC0"}, + {"DMIC MUX6", "DMIC1", "DMIC1"}, + {"DMIC MUX6", "DMIC2", "DMIC2"}, + {"DMIC MUX6", "DMIC3", "DMIC3"}, + {"DMIC MUX6", "DMIC4", "DMIC4"}, + {"DMIC MUX6", "DMIC5", "DMIC5"}, + {"AMIC MUX6", "ADC1", "ADC1"}, + {"AMIC MUX6", "ADC2", "ADC2"}, + {"AMIC MUX6", "ADC3", "ADC3"}, + {"AMIC MUX6", "ADC4", "ADC4"}, + + {"DMIC MUX7", "DMIC0", "DMIC0"}, + {"DMIC MUX7", "DMIC1", "DMIC1"}, + {"DMIC MUX7", "DMIC2", "DMIC2"}, + {"DMIC MUX7", "DMIC3", "DMIC3"}, + {"DMIC MUX7", "DMIC4", "DMIC4"}, + {"DMIC MUX7", "DMIC5", "DMIC5"}, + {"AMIC MUX7", "ADC1", "ADC1"}, + {"AMIC MUX7", "ADC2", "ADC2"}, + {"AMIC MUX7", "ADC3", "ADC3"}, + {"AMIC MUX7", "ADC4", "ADC4"}, + + {"DMIC MUX8", "DMIC0", "DMIC0"}, + {"DMIC MUX8", "DMIC1", "DMIC1"}, + {"DMIC MUX8", "DMIC2", "DMIC2"}, + {"DMIC MUX8", "DMIC3", "DMIC3"}, + {"DMIC MUX8", "DMIC4", "DMIC4"}, + {"DMIC MUX8", "DMIC5", "DMIC5"}, + {"AMIC MUX8", "ADC1", "ADC1"}, + {"AMIC MUX8", "ADC2", "ADC2"}, + {"AMIC MUX8", "ADC3", "ADC3"}, + {"AMIC MUX8", "ADC4", "ADC4"}, + + {"DMIC MUX10", "DMIC0", "DMIC0"}, + {"DMIC MUX10", "DMIC1", "DMIC1"}, + {"DMIC MUX10", "DMIC2", "DMIC2"}, + {"DMIC MUX10", "DMIC3", "DMIC3"}, + {"DMIC MUX10", "DMIC4", "DMIC4"}, + {"DMIC MUX10", "DMIC5", "DMIC5"}, + {"AMIC MUX10", "ADC1", "ADC1"}, + {"AMIC MUX10", "ADC2", "ADC2"}, + {"AMIC MUX10", "ADC3", "ADC3"}, + {"AMIC MUX10", "ADC4", "ADC4"}, + + {"DMIC MUX11", "DMIC0", "DMIC0"}, + {"DMIC MUX11", "DMIC1", "DMIC1"}, + {"DMIC MUX11", "DMIC2", "DMIC2"}, + {"DMIC MUX11", "DMIC3", "DMIC3"}, + {"DMIC MUX11", "DMIC4", "DMIC4"}, + {"DMIC MUX11", "DMIC5", "DMIC5"}, + {"AMIC MUX11", "ADC1", "ADC1"}, + {"AMIC MUX11", "ADC2", "ADC2"}, + {"AMIC MUX11", "ADC3", "ADC3"}, + {"AMIC MUX11", "ADC4", "ADC4"}, + + {"DMIC MUX12", "DMIC0", "DMIC0"}, + {"DMIC MUX12", "DMIC1", "DMIC1"}, + {"DMIC MUX12", "DMIC2", "DMIC2"}, + {"DMIC MUX12", "DMIC3", "DMIC3"}, + {"DMIC MUX12", "DMIC4", "DMIC4"}, + {"DMIC MUX12", "DMIC5", "DMIC5"}, + {"AMIC MUX12", "ADC1", "ADC1"}, + {"AMIC MUX12", "ADC2", "ADC2"}, + {"AMIC MUX12", "ADC3", "ADC3"}, + {"AMIC MUX12", "ADC4", "ADC4"}, + + {"DMIC MUX13", "DMIC0", "DMIC0"}, + {"DMIC MUX13", "DMIC1", "DMIC1"}, + {"DMIC MUX13", "DMIC2", "DMIC2"}, + {"DMIC MUX13", "DMIC3", "DMIC3"}, + {"DMIC MUX13", "DMIC4", "DMIC4"}, + {"DMIC MUX13", "DMIC5", "DMIC5"}, + {"AMIC MUX13", "ADC1", "ADC1"}, + {"AMIC MUX13", "ADC2", "ADC2"}, + {"AMIC MUX13", "ADC3", "ADC3"}, + {"AMIC MUX13", "ADC4", "ADC4"}, + + {"AMIC4_5 SEL", "AMIC4", "AMIC4"}, + {"AMIC4_5 SEL", "AMIC5", "AMIC5"}, + + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "AMIC2"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "AMIC4_5 SEL"}, + + /* CDC Rx interface with SLIMBUS */ + {"CDC_IF RX0 MUX", "SLIM RX0", "SLIM RX0"}, + {"CDC_IF RX1 MUX", "SLIM RX1", "SLIM RX1"}, + {"CDC_IF RX2 MUX", "SLIM RX2", "SLIM RX2"}, + {"CDC_IF RX3 MUX", "SLIM RX3", "SLIM RX3"}, + {"CDC_IF RX4 MUX", "SLIM RX4", "SLIM RX4"}, + {"CDC_IF RX5 MUX", "SLIM RX5", "SLIM RX5"}, + {"CDC_IF RX6 MUX", "SLIM RX6", "SLIM RX6"}, + {"CDC_IF RX7 MUX", "SLIM RX7", "SLIM RX7"}, + + {"RX INT0_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT1_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT3_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT3_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT3_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT3_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT3_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT3_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT4_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT4_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT4_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT4_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT4_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT4_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT7_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT7_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT7_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT8_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT8_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT8_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP0"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP1"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP2"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP0"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP1"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP2"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP0"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP1"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP2"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP0"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP1"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP2"}, + + /* Mixing path INT0 */ + {"RX INT0_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_2 MUX"}, + + /* Mixing path INT1 */ + {"RX INT1_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT1_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT1_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT1_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT1_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT1_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT1_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT1_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT1_2 INTERP", NULL, "RX INT1_2 MUX"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_2 INTERP"}, + + /* Mixing path INT2 */ + {"RX INT2_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT2_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT2_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT2_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT2_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT2_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT2_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT2_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT2_2 INTERP", NULL, "RX INT2_2 MUX"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_2 INTERP"}, + + /* Mixing path INT3 */ + {"RX INT3_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT3_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT3_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT3_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT3_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT3_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT3_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT3_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT3_2 INTERP", NULL, "RX INT3_2 MUX"}, + {"RX INT3 SEC MIX", NULL, "RX INT3_2 INTERP"}, + + /* Mixing path INT4 */ + {"RX INT4_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT4_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT4_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT4_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT4_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT4_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT4_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT4_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT4_2 INTERP", NULL, "RX INT4_2 MUX"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_2 INTERP"}, + + /* Mixing path INT7 */ + {"RX INT7_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_2 INTERP", NULL, "RX INT7_2 MUX"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_2 INTERP"}, + + /* Mixing path INT8 */ + {"RX INT8_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_2 INTERP", NULL, "RX INT8_2 MUX"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_2 INTERP"}, + + {"RX INT0_1 INTERP", NULL, "RX INT0_1 MIX1"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_1 INTERP"}, + {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"}, + {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"}, + {"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"}, + + {"RX INT1_1 INTERP", NULL, "RX INT1_1 MIX1"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_1 INTERP"}, + {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"}, + {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"}, + {"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"}, + + {"RX INT2_1 INTERP", NULL, "RX INT2_1 MIX1"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_1 INTERP"}, + {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"}, + {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"}, + {"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"}, + + {"RX INT3_1 INTERP", NULL, "RX INT3_1 MIX1"}, + {"RX INT3 SEC MIX", NULL, "RX INT3_1 INTERP"}, + {"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"}, + {"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"}, + {"RX INT3 DAC", NULL, "RX INT3 MIX2"}, + {"RX INT3 DAC", NULL, "RX_BIAS"}, + {"LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"LINEOUT1", NULL, "LINEOUT1 PA"}, + + {"RX INT4_1 INTERP", NULL, "RX INT4_1 MIX1"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_1 INTERP"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_1 MIX1"}, + {"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"}, + {"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"}, + {"RX INT4 DAC", NULL, "RX INT4 MIX2"}, + {"RX INT4 DAC", NULL, "RX_BIAS"}, + {"LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"LINEOUT2", NULL, "LINEOUT2 PA"}, + + {"RX INT7_1 INTERP", NULL, "RX INT7_1 MIX1"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_1 INTERP"}, + {"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"}, + {"RX INT7 MIX2", NULL, "RX INT7 MIX2 INP"}, + {"RX INT7 CHAIN", NULL, "RX INT7 MIX2"}, + {"RX INT7 CHAIN", NULL, "RX_BIAS"}, + {"SPK1 OUT", NULL, "RX INT7 CHAIN"}, + + {"RX INT8_1 INTERP", NULL, "RX INT8_1 MIX1"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_1 INTERP"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_1 MIX1"}, + {"RX INT8 CHAIN", NULL, "RX INT8 SEC MIX"}, + {"RX INT8 CHAIN", NULL, "RX_BIAS"}, + {"SPK2 OUT", NULL, "RX INT8 CHAIN"}, + + /* ANC Routing */ + {"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_HPHL", "RX INT1 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_LO1", "RX INT3 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_HPHR", "RX INT2 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_LO2", "RX INT4 MIX2"}, + + {"ANC OUT EAR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR Enable", "Switch", "ADC MUX11"}, + {"RX INT0 MIX2", NULL, "ANC OUT EAR Enable"}, + + {"ANC EAR PA", NULL, "RX INT0 DAC"}, + {"ANC EAR", NULL, "ANC EAR PA"}, + + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"}, + {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"}, + + {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"}, + {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"}, + {"SPK1 OUT", NULL, "ANC SPK1 PA"}, + + /* + * SRC0, SRC1 inputs to Sidetone RX Mixer + * on RX0, RX1, RX2, RX3, RX4 and RX7 chains + */ + {"IIR0", NULL, "IIR0 INP0 MUX"}, + {"IIR0 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP0 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP0 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP0 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP0 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP0 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP0 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP0 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP0 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR0", NULL, "IIR0 INP1 MUX"}, + {"IIR0 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP1 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP1 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP1 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP1 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP1 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP1 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP1 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP1 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR0", NULL, "IIR0 INP2 MUX"}, + {"IIR0 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR0", NULL, "IIR0 INP3 MUX"}, + {"IIR0 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP3 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP3 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP3 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP3 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP3 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP3 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP3 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP3 MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"IIR1", NULL, "IIR1 INP0 MUX"}, + {"IIR1 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP0 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR1 INP0 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR1 INP0 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR1 INP0 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR1 INP0 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR1 INP0 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR1 INP0 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR1 INP0 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP1 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR1 INP1 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR1 INP1 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR1 INP1 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR1 INP1 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR1 INP1 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR1 INP1 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR1 INP1 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR1", NULL, "IIR1 INP2 MUX"}, + {"IIR1 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR1 INP2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR1 INP2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR1 INP2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR1 INP2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR1 INP2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR1 INP2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR1 INP2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR1", NULL, "IIR1 INP3 MUX"}, + {"IIR1 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP3 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR1 INP3 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR1 INP3 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR1 INP3 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR1 INP3 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR1 INP3 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR1 INP3 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR1 INP3 MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"SRC0", NULL, "IIR0"}, + {"SRC1", NULL, "IIR1"}, + {"RX INT0 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT0 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT1 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT1 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT2 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT2 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT3 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT3 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT4 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT4 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT7 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT7 MIX2 INP", "SRC1", "SRC1"}, + + /* Native clk main path routing */ + {"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"}, + {"RX INT1_1 INTERP", NULL, "RX INT1_1 NATIVE MUX"}, + {"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"}, + + {"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"}, + {"RX INT2_1 INTERP", NULL, "RX INT2_1 NATIVE MUX"}, + {"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"}, + + {"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"}, + {"RX INT3_1 INTERP", NULL, "RX INT3_1 NATIVE MUX"}, + {"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"}, + + {"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"}, + {"RX INT4_1 INTERP", NULL, "RX INT4_1 NATIVE MUX"}, + {"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"}, + + /* Native clk mix path routing */ + {"RX INT1_2 NATIVE MUX", "ON", "RX INT1_2 MUX"}, + {"RX INT1_2 INTERP", NULL, "RX INT1_2 NATIVE MUX"}, + {"RX INT1_2 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"}, + + {"RX INT2_2 NATIVE MUX", "ON", "RX INT2_2 MUX"}, + {"RX INT2_2 INTERP", NULL, "RX INT2_2 NATIVE MUX"}, + {"RX INT2_2 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"}, + + {"RX INT3_2 NATIVE MUX", "ON", "RX INT3_2 MUX"}, + {"RX INT3_2 INTERP", NULL, "RX INT3_2 NATIVE MUX"}, + {"RX INT3_2 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"}, + + {"RX INT4_2 NATIVE MUX", "ON", "RX INT4_2 MUX"}, + {"RX INT4_2 INTERP", NULL, "RX INT4_2 NATIVE MUX"}, + {"RX INT4_2 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"}, + + {"RX INT7_2 NATIVE MUX", "ON", "RX INT7_2 MUX"}, + {"RX INT7_2 INTERP", NULL, "RX INT7_2 NATIVE MUX"}, + {"RX INT7_2 NATIVE MUX", NULL, "RX INT7 NATIVE SUPPLY"}, + + {"RX INT8_2 NATIVE MUX", "ON", "RX INT8_2 MUX"}, + {"RX INT8_2 INTERP", NULL, "RX INT8_2 NATIVE MUX"}, + {"RX INT8_2 NATIVE MUX", NULL, "RX INT8 NATIVE SUPPLY"}, + + /* ASRC Routing */ + {"ASRC0 MUX", "ASRC_IN_HPHL", "RX INT1_2 INTERP"}, + {"RX INT1 SEC MIX", "HPHL Switch", "ASRC0 MUX"}, + + {"ASRC1 MUX", "ASRC_IN_HPHR", "RX INT2_2 INTERP"}, + {"RX INT2 SEC MIX", "HPHR Switch", "ASRC1 MUX"}, + + {"ASRC0 MUX", "ASRC_IN_LO1", "RX INT3_2 INTERP"}, + {"RX INT3 SEC MIX", "LO1 Switch", "ASRC0 MUX"}, + + {"ASRC1 MUX", "ASRC_IN_LO2", "RX INT4_2 INTERP"}, + {"RX INT4 SEC MIX", "LO2 Switch", "ASRC1 MUX"}, + + {"ASRC2 MUX", "ASRC_IN_SPKR1", "RX INT7_2 INTERP"}, + {"RX INT7 SEC MIX", NULL, "ASRC2 MUX"}, + + {"ASRC3 MUX", "ASRC_IN_SPKR2", "RX INT8_2 INTERP"}, + {"RX INT8 SEC MIX", NULL, "ASRC3 MUX"}, +}; + +#endif diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c new file mode 100644 index 0000000000000000000000000000000000000000..bb877335c56f82ef6e0026e08346ad309a0cb582 --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -0,0 +1,9747 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd934x.h" +#include "wcd934x-mbhc.h" +#include "wcd934x-routing.h" +#include "wcd934x-dsp-cntl.h" +#include "../wcd9xxx-common-v2.h" +#include "../wcd9xxx-resmgr-v2.h" +#include "../wcdcal-hwdep.h" +#include "wcd934x-dsd.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 |\ + SNDRV_PCM_RATE_384000) +/* 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) + +#define WCD934X_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define WCD934X_FORMATS_S16_LE (SNDRV_PCM_FMTBIT_S16_LE) + +/* Macros for packing register writes into a U32 */ +#define WCD934X_PACKED_REG_SIZE sizeof(u32) +#define WCD934X_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xffff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0) + +#define STRING(name) #name +#define WCD_DAPM_ENUM(name, reg, offset, text) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM(STRING(name), name##_enum) + +#define WCD_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname) + +#define WCD_DAPM_MUX(name, shift, kctl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux) + +/* + * Timeout in milli seconds and it is the wait time for + * slim channel removal interrupt to receive. + */ +#define WCD934X_SLIM_CLOSE_TIMEOUT 1000 +#define WCD934X_SLIM_IRQ_OVERFLOW (1 << 0) +#define WCD934X_SLIM_IRQ_UNDERFLOW (1 << 1) +#define WCD934X_SLIM_IRQ_PORT_CLOSED (1 << 2) +#define WCD934X_MCLK_CLK_12P288MHZ 12288000 +#define WCD934X_MCLK_CLK_9P6MHZ 9600000 + +#define WCD934X_INTERP_MUX_NUM_INPUTS 3 +#define WCD934X_NUM_INTERPOLATORS 9 +#define WCD934X_NUM_DECIMATORS 9 +#define WCD934X_RX_PATH_CTL_OFFSET 20 + +#define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE)) + +#define WCD934X_REG_BITS 8 +#define WCD934X_MAX_VALID_ADC_MUX 13 +#define WCD934X_INVALID_ADC_MUX 9 + +#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_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_STRING_LEN 100 + +#define WCD934X_DIG_CORE_REG_MIN WCD934X_CDC_ANC0_CLK_RESET_CTL +#define WCD934X_DIG_CORE_REG_MAX 0xFFF + +#define WCD934X_MAX_MICBIAS 4 +#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone" +#define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone" +#define DAPM_MICBIAS3_STANDALONE "MIC BIAS3 Standalone" +#define DAPM_MICBIAS4_STANDALONE "MIC BIAS4 Standalone" + +#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 CPE_ERR_WDOG_BITE BIT(0) +#define CPE_FATAL_IRQS CPE_ERR_WDOG_BITE + +#define WCD934X_MAD_AUDIO_FIRMWARE_PATH "wcd934x/wcd934x_mad_audio.bin" + +#define TAVIL_VERSION_ENTRY_SIZE 17 + +#define WCD934X_DIG_CORE_COLLAPSE_TIMER_MS (5 * 1000) + +enum { + POWER_COLLAPSE, + POWER_RESUME, +}; + +static int dig_core_collapse_enable = 1; +module_param(dig_core_collapse_enable, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_enable, "enable/disable power gating"); + +/* dig_core_collapse timer in seconds */ +static int dig_core_collapse_timer = (WCD934X_DIG_CORE_COLLAPSE_TIMER_MS/1000); +module_param(dig_core_collapse_timer, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_timer, "timer for power gating"); + +#define TAVIL_HPH_REG_RANGE_1 (WCD934X_HPH_R_DAC_CTL - WCD934X_HPH_CNP_EN + 1) +#define TAVIL_HPH_REG_RANGE_2 (WCD934X_HPH_NEW_ANA_HPH3 -\ + WCD934X_HPH_NEW_ANA_HPH2 + 1) +#define TAVIL_HPH_REG_RANGE_3 (WCD934X_HPH_NEW_INT_PA_RDAC_MISC3 -\ + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL + 1) +#define TAVIL_HPH_TOTAL_REG (TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2 +\ + TAVIL_HPH_REG_RANGE_3) + +enum { + VI_SENSE_1, + VI_SENSE_2, + AUDIO_NOMINAL, + HPH_PA_DELAY, +}; + +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 tavil_idle_detect_config { + u8 hph_idle_thr; + u8 hph_idle_detect_en; +}; + +static const struct intr_data wcd934x_intr_table[] = { + {WCD9XXX_IRQ_SLIMBUS, false}, + {WCD934X_IRQ_MBHC_SW_DET, true}, + {WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, true}, + {WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, true}, + {WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, true}, + {WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true}, + {WCD934X_IRQ_MISC, false}, + {WCD934X_IRQ_HPH_PA_CNPL_COMPLETE, false}, + {WCD934X_IRQ_HPH_PA_CNPR_COMPLETE, false}, + {WCD934X_IRQ_EAR_PA_CNP_COMPLETE, false}, + {WCD934X_IRQ_LINE_PA1_CNP_COMPLETE, false}, + {WCD934X_IRQ_LINE_PA2_CNP_COMPLETE, false}, + {WCD934X_IRQ_SLNQ_ANALOG_ERROR, false}, + {WCD934X_IRQ_RESERVED_3, false}, + {WCD934X_IRQ_HPH_PA_OCPL_FAULT, false}, + {WCD934X_IRQ_HPH_PA_OCPR_FAULT, false}, + {WCD934X_IRQ_EAR_PA_OCP_FAULT, false}, + {WCD934X_IRQ_SOUNDWIRE, false}, + {WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE, false}, + {WCD934X_IRQ_RCO_ERROR, false}, + {WCD934X_IRQ_CPE_ERROR, false}, + {WCD934X_IRQ_MAD_AUDIO, false}, + {WCD934X_IRQ_MAD_BEACON, false}, + {WCD934X_IRQ_CPE1_INTR, true}, + {WCD934X_IRQ_RESERVED_4, false}, + {WCD934X_IRQ_MAD_ULTRASOUND, false}, + {WCD934X_IRQ_VBAT_ATTACK, false}, + {WCD934X_IRQ_VBAT_RESTORE, false}, +}; + +struct tavil_cpr_reg_defaults { + int wr_data; + int wr_addr; +}; + +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}, +}; + +static const struct wcd9xxx_ch tavil_rx_chs[WCD934X_RX_MAX] = { + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER, 0), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 1, 1), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 2, 2), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 3, 3), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 4, 4), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 5, 5), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 6, 6), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 7, 7), +}; + +static const struct wcd9xxx_ch tavil_tx_chs[WCD934X_TX_MAX] = { + WCD9XXX_CH(0, 0), + WCD9XXX_CH(1, 1), + WCD9XXX_CH(2, 2), + WCD9XXX_CH(3, 3), + WCD9XXX_CH(4, 4), + WCD9XXX_CH(5, 5), + WCD9XXX_CH(6, 6), + WCD9XXX_CH(7, 7), + WCD9XXX_CH(8, 8), + WCD9XXX_CH(9, 9), + WCD9XXX_CH(10, 10), + WCD9XXX_CH(11, 11), + WCD9XXX_CH(12, 12), + WCD9XXX_CH(13, 13), + WCD9XXX_CH(14, 14), + WCD9XXX_CH(15, 15), +}; + +static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = { + 0, /* AIF1_PB */ + BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */ + 0, /* AIF2_PB */ + BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */ + 0, /* AIF3_PB */ + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */ + 0, /* AIF4_PB */ +}; + +/* 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 { + ASRC_IN_HPHL, + ASRC_IN_LO1, + ASRC_IN_HPHR, + ASRC_IN_LO2, + ASRC_IN_SPKR1, + ASRC_IN_SPKR2, + ASRC_INVALID, +}; + +enum { + ASRC0, + ASRC1, + ASRC2, + ASRC3, + ASRC_MAX, +}; + +enum { + CONV_88P2K_TO_384K, + CONV_96K_TO_352P8K, + CONV_352P8K_TO_384K, + CONV_384K_TO_352P8K, + CONV_384K_TO_384K, + CONV_96K_TO_384K, +}; + +static struct afe_param_slimbus_slave_port_cfg tavil_slimbus_slave_port_cfg = { + .minor_version = 1, + .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1, + .slave_dev_pgd_la = 0, + .slave_dev_intfdev_la = 0, + .bit_width = 16, + .data_format = 0, + .num_channels = 1 +}; + +static struct afe_param_cdc_reg_page_cfg tavil_cdc_reg_page_cfg = { + .minor_version = AFE_API_VERSION_CDC_REG_PAGE_CFG, + .enable = 1, + .proc_id = AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1, +}; + +static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = { + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_MAIN_CTL_1), + HW_MAD_AUDIO_ENABLE, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_AUDIO_CTL_3), + HW_MAD_AUDIO_SLEEP_TIME, 0xF, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_AUDIO_CTL_4), + HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_CFG), + MAD_AUDIO_INT_DEST_SELECT_REG, 0x2, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_MASK3), + MAD_AUDIO_INT_MASK_REG, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_STATUS3), + MAD_AUDIO_INT_STATUS_REG, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_CLEAR3), + MAD_AUDIO_INT_CLEAR_REG, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_WATERMARK_N, 0x1E, WCD934X_REG_BITS, 0x1 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_ENABLE_N, 0x1, WCD934X_REG_BITS, 0x1 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_WATERMARK_N, 0x1E, WCD934X_REG_BITS, 0x1 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD934X_REG_BITS, 0x1 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + WCD934X_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FF_GAIN_ADAPTIVE, 0x4, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + WCD934X_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + WCD934X_CDC_ANC0_FF_A_GAIN_CTL), + AANC_GAIN_CONTROL, 0xFF, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + SB_PGD_TX_PORT_MULTI_CHANNEL_0(0)), + SB_PGD_TX_PORTn_MULTI_CHNL_0, 0xFF, WCD934X_REG_BITS, 0x4 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + SB_PGD_TX_PORT_MULTI_CHANNEL_1(0)), + SB_PGD_TX_PORTn_MULTI_CHNL_1, 0xFF, WCD934X_REG_BITS, 0x4 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x180, 0)), + SB_PGD_RX_PORTn_MULTI_CHNL_0, 0xFF, WCD934X_REG_BITS, 0x4 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x181, 0)), + SB_PGD_RX_PORTn_MULTI_CHNL_1, 0xFF, WCD934X_REG_BITS, 0x4 + }, +}; + +static struct afe_param_cdc_reg_cfg_data tavil_audio_reg_cfg = { + .num_registers = ARRAY_SIZE(audio_reg_cfg), + .reg_data = audio_reg_cfg, +}; + +static struct afe_param_id_cdc_aanc_version tavil_cdc_aanc_version = { + .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION, + .aanc_hw_version = AANC_HW_BLOCK_VERSION_2, +}; + +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); + +#define WCD934X_TX_UNMUTE_DELAY_MS 25 + +static int tx_unmute_delay = WCD934X_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + + +/* Hold instance to soundwire platform device */ +struct tavil_swr_ctrl_data { + struct platform_device *swr_pdev; +}; + +struct wcd_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, void *data), + void *swrm_handle, int action); +}; + +/* Holds all Soundwire and speaker related information */ +struct wcd934x_swr { + struct tavil_swr_ctrl_data *ctrl_data; + struct wcd_swr_ctrl_platform_data plat_data; + struct mutex read_mutex; + struct mutex write_mutex; + struct mutex clk_mutex; + int spkr_gain_offset; + int spkr_mode; + int clk_users; + int rx_7_count; + int rx_8_count; +}; + +struct tx_mute_work { + struct tavil_priv *tavil; + u8 decimator; + struct delayed_work dwork; +}; + +#define WCD934X_SPK_ANC_EN_DELAY_MS 350 +static int spk_anc_en_delay = WCD934X_SPK_ANC_EN_DELAY_MS; +module_param(spk_anc_en_delay, int, 0664); +MODULE_PARM_DESC(spk_anc_en_delay, "delay to enable anc in speaker path"); + +struct spk_anc_work { + struct tavil_priv *tavil; + struct delayed_work dwork; +}; + +struct hpf_work { + struct tavil_priv *tavil; + u8 decimator; + u8 hpf_cut_off_freq; + struct delayed_work dwork; +}; + +struct tavil_priv { + struct device *dev; + struct wcd9xxx *wcd9xxx; + struct snd_soc_codec *codec; + u32 rx_bias_count; + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + s32 micb_ref[TAVIL_MAX_MICBIAS]; + s32 pullup_ref[TAVIL_MAX_MICBIAS]; + + /* ANC related */ + u32 anc_slot; + bool anc_func; + + /* compander */ + int comp_enabled[COMPANDER_MAX]; + int ear_spkr_gain; + + /* class h specific data */ + struct wcd_clsh_cdc_data clsh_d; + /* Tavil Interpolator Mode Select for EAR, HPH_L and HPH_R */ + u32 hph_mode; + + /* Mad switch reference count */ + int mad_switch_cnt; + + /* track tavil interface type */ + u8 intf_type; + + /* to track the status */ + unsigned long status_mask; + + struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg; + + /* num of slim ports required */ + struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS]; + /* Port values for Rx and Tx codec_dai */ + unsigned int rx_port_value[WCD934X_RX_MAX]; + unsigned int tx_port_value; + + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd934x_swr swr; + struct mutex micb_lock; + + struct delayed_work power_gate_work; + struct mutex power_lock; + + struct clk *wcd_ext_clk; + + /* mbhc module */ + struct wcd934x_mbhc *mbhc; + + struct mutex codec_mutex; + struct work_struct tavil_add_child_devices_work; + struct hpf_work tx_hpf_work[WCD934X_NUM_DECIMATORS]; + struct tx_mute_work tx_mute_dwork[WCD934X_NUM_DECIMATORS]; + struct spk_anc_work spk_anc_dwork; + + unsigned int vi_feed_value; + + /* DSP control */ + struct wcd_dsp_cntl *wdsp_cntl; + + /* cal info for codec */ + struct fw_info *fw_data; + + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + + /* SVS voting related */ + struct mutex svs_mutex; + int svs_ref_cnt; + + int native_clk_users; + /* ASRC users count */ + int asrc_users[ASRC_MAX]; + int asrc_output_mode[ASRC_MAX]; + /* Main path clock users count */ + int main_clk_users[WCD934X_NUM_INTERPOLATORS]; + struct tavil_dsd_config *dsd_config; + struct tavil_idle_detect_config idle_det_cfg; + + int power_active_ref; +}; + +static const struct tavil_reg_mask_val tavil_spkr_default[] = { + {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x50}, + {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x50}, +}; + +static const struct tavil_reg_mask_val tavil_spkr_mode1[] = { + {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x00}, + {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x00}, + {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x00}, + {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x00}, + {WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x44}, + {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44}, +}; + +static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil); + +/* + * wcd934x_get_codec_info: Get codec specific information + * + * @wcd9xxx: pointer to wcd9xxx structure + * @wcd_type: pointer to wcd9xxx_codec_type structure + * + * Returns 0 for success or negative error code for failure + */ +int wcd934x_get_codec_info(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_codec_type *wcd_type) +{ + u16 id_minor, id_major; + struct regmap *wcd_regmap; + int rc, version = -1; + + if (!wcd9xxx || !wcd_type) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null\n", __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + rc = regmap_bulk_read(wcd_regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, + (u8 *)&id_minor, sizeof(u16)); + if (rc) + return -EINVAL; + + rc = regmap_bulk_read(wcd_regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2, + (u8 *)&id_major, sizeof(u16)); + if (rc) + return -EINVAL; + + dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n", + __func__, id_major, id_minor); + + if (id_major != TAVIL_MAJOR) + goto version_unknown; + + /* + * As fine version info cannot be retrieved before tavil probe. + * Assign coarse versions for possible future use before tavil probe. + */ + if (id_minor == cpu_to_le16(0)) + version = TAVIL_VERSION_1_0; + else if (id_minor == cpu_to_le16(0x01)) + version = TAVIL_VERSION_1_1; + +version_unknown: + if (version < 0) + dev_err(wcd9xxx->dev, "%s: wcd934x version unknown\n", + __func__); + + /* Fill codec type info */ + wcd_type->id_major = id_major; + wcd_type->id_minor = id_minor; + wcd_type->num_irqs = WCD934X_NUM_IRQS; + wcd_type->version = version; + wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1; + wcd_type->i2c_chip_status = 0x01; + wcd_type->intr_tbl = wcd934x_intr_table; + wcd_type->intr_tbl_size = ARRAY_SIZE(wcd934x_intr_table); + + wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] = + WCD934X_INTR_PIN1_STATUS0; + wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] = + WCD934X_INTR_PIN1_CLEAR0; + wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] = + WCD934X_INTR_PIN1_MASK0; + wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] = + WCD934X_INTR_LEVEL0; + wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] = + WCD934X_INTR_CLR_COMMIT; + + return rc; +} +EXPORT_SYMBOL(wcd934x_get_codec_info); + +/* + * wcd934x_bringdown: Bringdown WCD Codec + * + * @wcd9xxx: Pointer to wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +int wcd934x_bringdown(struct wcd9xxx *wcd9xxx) +{ + if (!wcd9xxx || !wcd9xxx->regmap) + return -EINVAL; + + regmap_write(wcd9xxx->regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x04); + + return 0; +} +EXPORT_SYMBOL(wcd934x_bringdown); + +/* + * wcd934x_bringup: Bringup WCD Codec + * + * @wcd9xxx: Pointer to the wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +int wcd934x_bringup(struct wcd9xxx *wcd9xxx) +{ + struct regmap *wcd_regmap; + + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n", + __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x19); + regmap_write(wcd_regmap, WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x15); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x3); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x7); + + return 0; +} +EXPORT_SYMBOL(wcd934x_bringup); + +/** + * tavil_set_spkr_gain_offset - offset the speaker path + * gain with the given offset value. + * + * @codec: codec instance + * @offset: Indicates speaker path gain offset value. + * + * Returns 0 on success or -EINVAL on error. + */ +int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) +{ + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (!priv) + return -EINVAL; + + priv->swr.spkr_gain_offset = offset; + return 0; +} +EXPORT_SYMBOL(tavil_set_spkr_gain_offset); + +/** + * tavil_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @codec: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + int i; + const struct tavil_reg_mask_val *regs; + int size; + + if (!priv) + return -EINVAL; + + switch (mode) { + case WCD934X_SPKR_MODE_1: + regs = tavil_spkr_mode1; + size = ARRAY_SIZE(tavil_spkr_mode1); + break; + default: + regs = tavil_spkr_default; + size = ARRAY_SIZE(tavil_spkr_default); + break; + } + + priv->swr.spkr_mode = mode; + for (i = 0; i < size; i++) + snd_soc_update_bits(codec, regs[i].reg, + regs[i].mask, regs[i].val); + return 0; +} +EXPORT_SYMBOL(tavil_set_spkr_mode); + +/** + * tavil_get_afe_config - returns specific codec configuration to afe to write + * + * @codec: codec instance + * @config_type: Indicates type of configuration to write. + */ +void *tavil_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type) +{ + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (config_type) { + case AFE_SLIMBUS_SLAVE_CONFIG: + return &priv->slimbus_slave_cfg; + case AFE_CDC_REGISTERS_CONFIG: + return &tavil_audio_reg_cfg; + case AFE_SLIMBUS_SLAVE_PORT_CONFIG: + return &tavil_slimbus_slave_port_cfg; + case AFE_AANC_VERSION: + return &tavil_cdc_aanc_version; + case AFE_CDC_REGISTER_PAGE_CONFIG: + return &tavil_cdc_reg_page_cfg; + default: + dev_info(codec->dev, "%s: Unknown config_type 0x%x\n", + __func__, config_type); + return NULL; + } +} +EXPORT_SYMBOL(tavil_get_afe_config); + +static bool is_tavil_playback_dai(int dai_id) +{ + if ((dai_id == AIF1_PB) || (dai_id == AIF2_PB) || + (dai_id == AIF3_PB) || (dai_id == AIF4_PB)) + return true; + + return false; +} + +static int tavil_find_playback_dai_id_for_port(int port_id, + struct tavil_priv *tavil) +{ + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx_ch *ch; + int i, slv_port_id; + + for (i = AIF1_PB; i < NUM_CODEC_DAIS; i++) { + if (!is_tavil_playback_dai(i)) + continue; + + dai = &tavil->dai[i]; + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + slv_port_id = wcd9xxx_get_slave_port(ch->ch_num); + if ((slv_port_id > 0) && (slv_port_id == port_id)) + return i; + } + } + + return -EINVAL; +} + +static void tavil_vote_svs(struct tavil_priv *tavil, bool vote) +{ + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tavil->wcd9xxx; + + mutex_lock(&tavil->svs_mutex); + if (vote) { + tavil->svs_ref_cnt++; + if (tavil->svs_ref_cnt == 1) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, + 0x01, 0x01); + } else { + /* Do not decrement ref count if it is already 0 */ + if (tavil->svs_ref_cnt == 0) + goto done; + + tavil->svs_ref_cnt--; + if (tavil->svs_ref_cnt == 0) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, + 0x01, 0x00); + } +done: + dev_dbg(tavil->dev, "%s: vote = %s, updated ref cnt = %u\n", __func__, + vote ? "vote" : "Unvote", tavil->svs_ref_cnt); + mutex_unlock(&tavil->svs_mutex); +} + +static int tavil_get_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->anc_slot; + return 0; +} + +static int tavil_put_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + tavil->anc_slot = ucontrol->value.integer.value[0]; + return 0; +} + +static int tavil_get_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = (tavil->anc_func == true ? 1 : 0); + return 0; +} + +static int tavil_put_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + mutex_lock(&tavil->codec_mutex); + tavil->anc_func = (!ucontrol->value.integer.value[0] ? false : true); + dev_dbg(codec->dev, "%s: anc_func %x", __func__, tavil->anc_func); + + if (tavil->anc_func == true) { + snd_soc_dapm_enable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_disable_pin(dapm, "EAR PA"); + snd_soc_dapm_disable_pin(dapm, "EAR"); + } else { + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_enable_pin(dapm, "EAR PA"); + snd_soc_dapm_enable_pin(dapm, "EAR"); + } + mutex_unlock(&tavil->codec_mutex); + + snd_soc_dapm_sync(dapm); + return 0; +} + +static int tavil_codec_enable_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + const char *filename; + const struct firmware *fw; + int i; + int ret = 0; + int num_anc_slots; + struct wcd9xxx_anc_header *anc_head; + struct firmware_cal *hwdep_cal = NULL; + u32 anc_writes_size = 0; + u32 anc_cal_size = 0; + int anc_size_remaining; + u32 *anc_ptr; + u16 reg; + u8 mask, val; + size_t cal_size; + const void *data; + + if (!tavil->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_ANC_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration, cal_size %zd", + __func__, cal_size); + } else { + filename = "WCD934X/WCD934X_anc.bin"; + ret = request_firmware(&fw, filename, codec->dev); + if (IS_ERR_VALUE(ret)) { + dev_err(codec->dev, "%s: Failed to acquire ANC data: %d\n", + __func__, ret); + return ret; + } + if (!fw) { + dev_err(codec->dev, "%s: Failed to get anc fw\n", + __func__); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + if (cal_size < sizeof(struct wcd9xxx_anc_header)) { + dev_err(codec->dev, "%s: Invalid cal_size %zd\n", + __func__, cal_size); + ret = -EINVAL; + goto err; + } + /* First number is the number of register writes */ + anc_head = (struct wcd9xxx_anc_header *)(data); + anc_ptr = (u32 *)(data + sizeof(struct wcd9xxx_anc_header)); + anc_size_remaining = cal_size - + sizeof(struct wcd9xxx_anc_header); + num_anc_slots = anc_head->num_anc_slots; + + if (tavil->anc_slot >= num_anc_slots) { + dev_err(codec->dev, "%s: Invalid ANC slot selected\n", + __func__); + ret = -EINVAL; + goto err; + } + for (i = 0; i < num_anc_slots; i++) { + if (anc_size_remaining < WCD934X_PACKED_REG_SIZE) { + dev_err(codec->dev, "%s: Invalid register format\n", + __func__); + ret = -EINVAL; + goto err; + } + anc_writes_size = (u32)(*anc_ptr); + anc_size_remaining -= sizeof(u32); + anc_ptr += 1; + + if ((anc_writes_size * WCD934X_PACKED_REG_SIZE) > + anc_size_remaining) { + dev_err(codec->dev, "%s: Invalid register format\n", + __func__); + ret = -EINVAL; + goto err; + } + + if (tavil->anc_slot == i) + break; + + anc_size_remaining -= (anc_writes_size * + WCD934X_PACKED_REG_SIZE); + anc_ptr += anc_writes_size; + } + if (i == num_anc_slots) { + dev_err(codec->dev, "%s: Selected ANC slot not present\n", + __func__); + ret = -EINVAL; + goto err; + } + + anc_cal_size = anc_writes_size; + for (i = 0; i < anc_writes_size; i++) { + WCD934X_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val); + snd_soc_write(codec, reg, (val & mask)); + } + + /* Rate converter clk enable and set bypass mode */ + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_RC_COMMON_CTL, + 0x05, 0x05); + if (!hwdep_cal) + release_firmware(fw); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_RC_COMMON_CTL, + 0x05, 0x00); + if (!strcmp(w->name, "ANC EAR PA") || + !strcmp(w->name, "ANC SPK1 PA")) { + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL, + 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x38, 0x38); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x07, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x38, 0x00); + } + break; + } + + return 0; +err: + if (!hwdep_cal) + release_firmware(fw); + return ret; +} + +static int tavil_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil_p->vi_feed_value; + + return 0; +} + +static int tavil_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n", + __func__, enable, port_id, dai_id); + + tavil_p->vi_feed_value = ucontrol->value.integer.value[0]; + + mutex_lock(&tavil_p->codec_mutex); + if (enable) { + if (port_id == WCD934X_TX14 && !test_bit(VI_SENSE_1, + &tavil_p->status_mask)) { + list_add_tail(&core->tx_chs[WCD934X_TX14].list, + &tavil_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_1, &tavil_p->status_mask); + } + if (port_id == WCD934X_TX15 && !test_bit(VI_SENSE_2, + &tavil_p->status_mask)) { + list_add_tail(&core->tx_chs[WCD934X_TX15].list, + &tavil_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_2, &tavil_p->status_mask); + } + } else { + if (port_id == WCD934X_TX14 && test_bit(VI_SENSE_1, + &tavil_p->status_mask)) { + list_del_init(&core->tx_chs[WCD934X_TX14].list); + clear_bit(VI_SENSE_1, &tavil_p->status_mask); + } + if (port_id == WCD934X_TX15 && test_bit(VI_SENSE_2, + &tavil_p->status_mask)) { + list_del_init(&core->tx_chs[WCD934X_TX15].list); + clear_bit(VI_SENSE_2, &tavil_p->status_mask); + } + } + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil_p->tx_port_value; + return 0; +} + +static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + u32 vtable; + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, + widget->name, ucontrol->id.name, tavil_p->tx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tavil_p->codec_mutex); + if (dai_id >= ARRAY_SIZE(vport_slim_check_table)) { + dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n", + __func__, dai_id); + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; + } + vtable = vport_slim_check_table[dai_id]; + + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set */ + if (enable && !(tavil_p->tx_port_value & 1 << port_id)) { + if (wcd9xxx_tx_vport_validation(vtable, port_id, + tavil_p->dai, NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n", + __func__, port_id); + mutex_unlock(&tavil_p->codec_mutex); + return 0; + } + tavil_p->tx_port_value |= 1 << port_id; + list_add_tail(&core->tx_chs[port_id].list, + &tavil_p->dai[dai_id].wcd9xxx_ch_list); + } else if (!enable && (tavil_p->tx_port_value & + 1 << port_id)) { + tavil_p->tx_port_value &= ~(1 << port_id); + list_del_init(&core->tx_chs[port_id].list); + } else { + if (enable) + dev_dbg(codec->dev, "%s: TX%u port is used by\n" + "this virtual port\n", + __func__, port_id); + else + dev_dbg(codec->dev, "%s: TX%u port is not used by\n" + "this virtual port\n", + __func__, port_id); + /* avoid update power function */ + mutex_unlock(&tavil_p->codec_mutex); + return 0; + } + break; + case AIF4_MAD_TX: + break; + default: + dev_err(codec->dev, "Unknown AIF %d\n", dai_id); + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: name %s sname %s updated value %u shift %d\n", + __func__, widget->name, widget->sname, tavil_p->tx_port_value, + widget->shift); + + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int slim_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = + tavil_p->rx_port_value[widget->shift]; + return 0; +} + +static int slim_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + unsigned int rx_port_value; + u32 port_id = widget->shift; + + tavil_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + rx_port_value = tavil_p->rx_port_value[port_id]; + + mutex_lock(&tavil_p->codec_mutex); + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, widget->name, ucontrol->id.name, + rx_port_value, widget->shift, + ucontrol->value.integer.value[0]); + + /* value need to match the Virtual port and AIF number */ + switch (rx_port_value) { + case 0: + list_del_init(&core->rx_chs[port_id].list); + break; + case 1: + if (wcd9xxx_rx_vport_validation(port_id + + WCD934X_RX_PORT_START_NUMBER, + &tavil_p->dai[AIF1_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tavil_p->dai[AIF1_PB].wcd9xxx_ch_list); + break; + case 2: + if (wcd9xxx_rx_vport_validation(port_id + + WCD934X_RX_PORT_START_NUMBER, + &tavil_p->dai[AIF2_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tavil_p->dai[AIF2_PB].wcd9xxx_ch_list); + break; + case 3: + if (wcd9xxx_rx_vport_validation(port_id + + WCD934X_RX_PORT_START_NUMBER, + &tavil_p->dai[AIF3_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tavil_p->dai[AIF3_PB].wcd9xxx_ch_list); + break; + case 4: + if (wcd9xxx_rx_vport_validation(port_id + + WCD934X_RX_PORT_START_NUMBER, + &tavil_p->dai[AIF4_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tavil_p->dai[AIF4_PB].wcd9xxx_ch_list); + break; + default: + dev_err(codec->dev, "Unknown AIF %d\n", rx_port_value); + goto err; + } +rtn: + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + + return 0; +err: + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; +} + +static void tavil_codec_enable_slim_port_intr( + struct wcd9xxx_codec_dai_data *dai, + struct snd_soc_codec *codec) +{ + struct wcd9xxx_ch *ch; + int port_num = 0; + unsigned short reg = 0; + u8 val = 0; + struct tavil_priv *tavil_p; + + if (!dai || !codec) { + pr_err("%s: Invalid params\n", __func__); + return; + } + + tavil_p = snd_soc_codec_get_drvdata(codec); + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + if (ch->port >= WCD934X_RX_PORT_START_NUMBER) { + port_num = ch->port - WCD934X_RX_PORT_START_NUMBER; + reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(tavil_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write( + tavil_p->wcd9xxx, reg, val); + val = wcd9xxx_interface_reg_read( + tavil_p->wcd9xxx, reg); + } + } else { + port_num = ch->port; + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(tavil_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write(tavil_p->wcd9xxx, + reg, val); + val = wcd9xxx_interface_reg_read( + tavil_p->wcd9xxx, reg); + } + } + } +} + +static int tavil_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai, + bool up) +{ + int ret = 0; + struct wcd9xxx_ch *ch; + + if (up) { + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + ret = wcd9xxx_get_slave_port(ch->ch_num); + if (ret < 0) { + pr_err("%s: Invalid slave port ID: %d\n", + __func__, ret); + ret = -EINVAL; + } else { + set_bit(ret, &dai->ch_mask); + } + } + } else { + ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0), + msecs_to_jiffies( + WCD934X_SLIM_CLOSE_TIMEOUT)); + if (!ret) { + pr_err("%s: Slim close tx/rx wait timeout, ch_mask:0x%lx\n", + __func__, dai->ch_mask); + ret = -ETIMEDOUT; + } else { + ret = 0; + } + } + return ret; +} + +static void tavil_codec_mute_dsd(struct snd_soc_codec *codec, + struct list_head *ch_list) +{ + u8 dsd0_in; + u8 dsd1_in; + struct wcd9xxx_ch *ch; + + /* Read DSD Input Ports */ + dsd0_in = (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x3C) >> 2; + dsd1_in = (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & 0x3C) >> 2; + + if ((dsd0_in == 0) && (dsd1_in == 0)) + return; + + /* + * Check if the ports getting disabled are connected to DSD inputs. + * If connected, enable DSD mute to avoid DC entering into DSD Filter + */ + list_for_each_entry(ch, ch_list, list) { + if (ch->port == (dsd0_in + WCD934X_RX_PORT_START_NUMBER - 1)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, + 0x04, 0x04); + if (ch->port == (dsd1_in + WCD934X_RX_PORT_START_NUMBER - 1)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, + 0x04, 0x04); + } +} + +static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + int ret = 0; + struct wcd9xxx_codec_dai_data *dai; + struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config; + + core = dev_get_drvdata(codec->dev->parent); + + dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d\n" + "stream name %s event %d\n", + __func__, codec->component.name, + codec->component.num_dai, w->sname, event); + + dai = &tavil_p->dai[w->shift]; + dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n", + __func__, w->name, w->shift, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tavil_codec_enable_slim_port_intr(dai, codec); + (void) tavil_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + if (dsd_conf) + tavil_codec_mute_dsd(codec, &dai->wcd9xxx_ch_list); + + ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n", + __func__, ret); + + if (!dai->bus_down_in_recovery) + ret = tavil_codec_enable_slim_chmask(dai, false); + else + dev_dbg(codec->dev, + "%s: bus in recovery skip enable slim_chmask", + __func__); + ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->grph); + break; + } + return ret; +} + +static int tavil_codec_enable_slimtx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx *core; + int ret = 0; + + dev_dbg(codec->dev, + "%s: w->name %s, w->shift = %d, num_dai %d stream name %s\n", + __func__, w->name, w->shift, + codec->component.num_dai, w->sname); + + dai = &tavil_p->dai[w->shift]; + core = dev_get_drvdata(codec->dev->parent); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tavil_codec_enable_slim_port_intr(dai, codec); + (void) tavil_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (!dai->bus_down_in_recovery) + ret = tavil_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n", + __func__, ret); + } + break; + } + return ret; +} + +static int tavil_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core = NULL; + struct snd_soc_codec *codec = NULL; + struct tavil_priv *tavil_p = NULL; + int ret = 0; + struct wcd9xxx_codec_dai_data *dai = NULL; + + codec = snd_soc_dapm_to_codec(w->dapm); + tavil_p = snd_soc_codec_get_drvdata(codec); + core = dev_get_drvdata(codec->dev->parent); + + dev_dbg(codec->dev, + "%s: num_dai %d stream name %s w->name %s event %d shift %d\n", + __func__, codec->component.num_dai, w->sname, + w->name, event, w->shift); + + if (w->shift != AIF4_VIFEED) { + pr_err("%s Error in enabling the tx path\n", __func__); + ret = -EINVAL; + goto done; + } + dai = &tavil_p->dai[w->shift]; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (test_bit(VI_SENSE_1, &tavil_p->status_mask)) { + dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + if (test_bit(VI_SENSE_2, &tavil_p->status_mask)) { + pr_debug("%s: spkr2 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + dai->bus_down_in_recovery = false; + tavil_codec_enable_slim_port_intr(dai, codec); + (void) tavil_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (ret) + dev_err(codec->dev, "%s error in close_slim_sch_tx %d\n", + __func__, ret); + if (!dai->bus_down_in_recovery) + ret = tavil_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect TX port, ret = %d\n", + __func__, ret); + } + if (test_bit(VI_SENSE_1, &tavil_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + if (test_bit(VI_SENSE_2, &tavil_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + break; + } +done: + return ret; +} + +static int tavil_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil->rx_bias_count++; + if (tavil->rx_bias_count == 1) { + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x01, 0x01); + } + break; + case SND_SOC_DAPM_POST_PMD: + tavil->rx_bias_count--; + if (!tavil->rx_bias_count) + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x01, 0x00); + break; + }; + dev_dbg(codec->dev, "%s: Current RX BIAS user count: %d\n", __func__, + tavil->rx_bias_count); + + return 0; +} + +static void tavil_spk_anc_update_callback(struct work_struct *work) +{ + struct spk_anc_work *spk_anc_dwork; + struct tavil_priv *tavil; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + + delayed_work = to_delayed_work(work); + spk_anc_dwork = container_of(delayed_work, struct spk_anc_work, dwork); + tavil = spk_anc_dwork->tavil; + codec = tavil->codec; + + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0, 0x10, 0x10); +} + +static int tavil_codec_enable_spkr_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil->anc_func) + return 0; + + dev_dbg(codec->dev, "%s: w: %s event: %d anc: %d\n", __func__, + w->name, event, tavil->anc_func); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tavil_codec_enable_anc(w, kcontrol, event); + schedule_delayed_work(&tavil->spk_anc_dwork.dwork, + msecs_to_jiffies(spk_anc_en_delay)); + break; + case SND_SOC_DAPM_POST_PMD: + cancel_delayed_work_sync(&tavil->spk_anc_dwork.dwork); + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0, + 0x10, 0x00); + ret = tavil_codec_enable_anc(w, kcontrol, event); + break; + } + return ret; +} + +static int tavil_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* + * 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD934X_CDC_RX0_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX0_RX_PATH_MIX_CTL, + 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + + if (!(strcmp(w->name, "ANC EAR PA"))) { + ret = tavil_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0, + 0x10, 0x00); + } + break; + }; + + return ret; +} + +static void tavil_codec_override(struct snd_soc_codec *codec, int mode, + int event) +{ + if (mode == CLS_AB || mode == CLS_AB_HIFI) { + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMU: + if (!(snd_soc_read(codec, + WCD934X_CDC_RX2_RX_PATH_CTL) & 0x10) && + (!(snd_soc_read(codec, + WCD934X_CDC_RX1_RX_PATH_CTL) & 0x10))) + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x02); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x00); + break; + } + } +} + +static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, (0x03 << 1)); + set_bit(HPH_PA_DELAY, &tavil->status_mask); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) { + /* Set regulator mode to AB if DSD is enabled */ + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x02, 0x02); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement + */ + if (test_bit(HPH_PA_DELAY, &tavil->status_mask)) { + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &tavil->status_mask); + } + + snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x01); + + /* Remove mute */ + snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + /* Enable GM3 boost */ + snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL, + 0x80, 0x80); + /* Enable AutoChop timer at the end of power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x02); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD934X_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, + 0x04, 0x00); + tavil_codec_override(codec, tavil->hph_mode, event); + break; + case SND_SOC_DAPM_PRE_PMD: + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_PRE_HPHR_PA_OFF, + &tavil->mbhc->wcd_mbhc); + /* Enable DSD Mute before PA disable */ + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA disable */ + usleep_range(5000, 5100); + tavil_codec_override(codec, tavil->hph_mode, event); + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_POST_HPHR_PA_OFF, + &tavil->mbhc->wcd_mbhc); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, 0x0); + break; + }; + + return 0; +} + +static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, (0x03 << 1)); + set_bit(HPH_PA_DELAY, &tavil->status_mask); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) { + /* Set regulator mode to AB if DSD is enabled */ + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x02, 0x02); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement + */ + if (test_bit(HPH_PA_DELAY, &tavil->status_mask)) { + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &tavil->status_mask); + } + snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x01); + /* Remove Mute on primary path */ + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + /* Enable GM3 boost */ + snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL, + 0x80, 0x80); + /* Enable AutoChop timer at the end of power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x02); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD934X_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, + 0x04, 0x00); + tavil_codec_override(codec, tavil->hph_mode, event); + break; + case SND_SOC_DAPM_PRE_PMD: + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_PRE_HPHL_PA_OFF, + &tavil->mbhc->wcd_mbhc); + /* Enable DSD Mute before PA disable */ + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, + 0x04, 0x04); + + snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA disable */ + usleep_range(5000, 5100); + tavil_codec_override(codec, tavil->hph_mode, event); + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_POST_HPHL_PA_OFF, + &tavil->mbhc->wcd_mbhc); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, 0x0); + break; + }; + + return 0; +} + +static int tavil_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 lineout_vol_reg = 0, lineout_mix_vol_reg = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (w->reg == WCD934X_ANA_LO_1_2) { + if (w->shift == 7) { + lineout_vol_reg = WCD934X_CDC_RX3_RX_PATH_CTL; + lineout_mix_vol_reg = WCD934X_CDC_RX3_RX_PATH_MIX_CTL; + } else if (w->shift == 6) { + lineout_vol_reg = WCD934X_CDC_RX4_RX_PATH_CTL; + lineout_mix_vol_reg = WCD934X_CDC_RX4_RX_PATH_MIX_CTL; + } + } else { + dev_err(codec->dev, "%s: Error enabling lineout PA\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_override(codec, CLS_AB, event); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, lineout_vol_reg, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, lineout_mix_vol_reg)) & 0x10) + snd_soc_update_bits(codec, + lineout_mix_vol_reg, + 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + tavil_codec_override(codec, CLS_AB, event); + default: + break; + }; + + return 0; +} + +static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disable AutoChop timer during power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + + if (tavil->anc_func) + ret = tavil_codec_enable_anc(w, kcontrol, event); + + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, + CLS_H_NORMAL); + if (tavil->anc_func) + snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, + CLS_H_NORMAL); + break; + default: + break; + }; + + return ret; +} + +static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int hph_mode = tavil->hph_mode; + u8 dem_inp; + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, 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)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control enable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x01); + /* Disable AutoChop timer during power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + /* Set RDAC gain */ + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x40); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) + hph_mode = CLS_H_HIFI; + + wcd_clsh_fsm(codec, &tavil->clsh_d, + 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_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, + hph_mode); + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control disable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x0); + /* Re-set RDAC gain */ + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x0); + break; + default: + break; + }; + + return 0; +} + +static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int hph_mode = tavil->hph_mode; + u8 dem_inp; + int ret = 0; + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, 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)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control enable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x01); + /* Disable AutoChop timer during power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + /* Set RDAC gain */ + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x40); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) + hph_mode = CLS_H_HIFI; + + wcd_clsh_fsm(codec, &tavil->clsh_d, + 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_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, + hph_mode); + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control disable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x0); + /* Re-set RDAC gain */ + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x0); + break; + default: + break; + }; + + return ret; +} + +static int tavil_codec_lineout_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_LO, + CLS_AB); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_LO, + CLS_AB); + break; + } + + return 0; +} + +static int tavil_codec_spk_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg, reg_mix; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (!strcmp(w->name, "RX INT7 CHAIN")) { + boost_path_ctl = WCD934X_CDC_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = WCD934X_CDC_RX7_RX_PATH_CFG1; + reg = WCD934X_CDC_RX7_RX_PATH_CTL; + reg_mix = WCD934X_CDC_RX7_RX_PATH_MIX_CTL; + } else if (!strcmp(w->name, "RX INT8 CHAIN")) { + boost_path_ctl = WCD934X_CDC_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = WCD934X_CDC_RX8_RX_PATH_CFG1; + reg = WCD934X_CDC_RX8_RX_PATH_CTL; + reg_mix = WCD934X_CDC_RX8_RX_PATH_MIX_CTL; + } else { + dev_err(codec->dev, "%s: unknown widget: %s\n", + __func__, w->name); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01); + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10); + snd_soc_update_bits(codec, reg, 0x10, 0x00); + if ((snd_soc_read(codec, reg_mix)) & 0x10) + snd_soc_update_bits(codec, reg_mix, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00); + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00); + break; + }; + + return 0; +} + +static int __tavil_codec_enable_swr(struct snd_soc_dapm_widget *w, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil; + int ch_cnt = 0; + + tavil = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) || + (strnstr(w->name, "INT7 MIX2", + sizeof("RX INT7 MIX2"))))) + tavil->swr.rx_7_count++; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + !tavil->swr.rx_8_count) + tavil->swr.rx_8_count++; + ch_cnt = !!(tavil->swr.rx_7_count) + tavil->swr.rx_8_count; + + swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev, + SWR_DEVICE_UP, NULL); + swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + break; + case SND_SOC_DAPM_POST_PMD: + if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) || + (strnstr(w->name, "INT7 MIX2", + sizeof("RX INT7 MIX2")))) + tavil->swr.rx_7_count--; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + tavil->swr.rx_8_count) + tavil->swr.rx_8_count--; + ch_cnt = !!(tavil->swr.rx_7_count) + tavil->swr.rx_8_count; + + swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + + break; + } + dev_dbg(tavil->dev, "%s: %s: current swr ch cnt: %d\n", + __func__, w->name, ch_cnt); + + return 0; +} + +static int tavil_codec_enable_swr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return __tavil_codec_enable_swr(w, event); +} + +static int tavil_codec_config_mad(struct snd_soc_codec *codec) +{ + int ret = 0; + int idx; + const struct firmware *fw; + struct firmware_cal *hwdep_cal = NULL; + struct wcd_mad_audio_cal *mad_cal = NULL; + const void *data; + const char *filename = WCD934X_MAD_AUDIO_FIRMWARE_PATH; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + size_t cal_size; + + hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_MAD_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + ret = request_firmware(&fw, filename, codec->dev); + if (ret || !fw) { + dev_err(codec->dev, + "%s: MAD firmware acquire failed, err = %d\n", + __func__, ret); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + + if (cal_size < sizeof(*mad_cal)) { + dev_err(codec->dev, + "%s: Incorrect size %zd for MAD Cal, expected %zd\n", + __func__, cal_size, sizeof(*mad_cal)); + ret = -ENOMEM; + goto done; + } + + mad_cal = (struct wcd_mad_audio_cal *) (data); + if (!mad_cal) { + dev_err(codec->dev, + "%s: Invalid calibration data\n", + __func__); + ret = -EINVAL; + goto done; + } + + snd_soc_write(codec, WCD934X_SOC_MAD_MAIN_CTL_2, + mad_cal->microphone_info.cycle_time); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_MAIN_CTL_1, 0xFF << 3, + ((uint16_t)mad_cal->microphone_info.settle_time) + << 3); + + /* Audio */ + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_8, + mad_cal->audio_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_1, + 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, 0x03 << 2, + mad_cal->audio_info.detection_mechanism << 2); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_7, + mad_cal->audio_info.rms_diff_threshold & 0x3F); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_5, + mad_cal->audio_info.rms_threshold_lsb); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_6, + mad_cal->audio_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL, + mad_cal->audio_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->audio_info.iir_coefficients[idx]); + } + + /* Beacon */ + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_8, + mad_cal->beacon_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_CTL_1, + 0x07 << 4, mad_cal->beacon_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_CTL_2, 0x03 << 2, + mad_cal->beacon_info.detection_mechanism << 2); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_7, + mad_cal->beacon_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_5, + mad_cal->beacon_info.rms_threshold_lsb); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_6, + mad_cal->beacon_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->beacon_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL, + mad_cal->beacon_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Beacon IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->beacon_info.iir_coefficients[idx]); + } + + /* Ultrasound */ + snd_soc_update_bits(codec, WCD934X_SOC_MAD_ULTR_CTL_1, + 0x07 << 4, + mad_cal->ultrasound_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_ULTR_CTL_2, 0x03 << 2, + mad_cal->ultrasound_info.detection_mechanism << 2); + snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_7, + mad_cal->ultrasound_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_5, + mad_cal->ultrasound_info.rms_threshold_lsb); + snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_6, + mad_cal->ultrasound_info.rms_threshold_msb); + +done: + if (!hwdep_cal) + release_firmware(fw); + + return ret; +} + +static int __tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable) +{ + int rc = 0; + + /* Return if CPE INPUT is DEC1 */ + if (snd_soc_read(codec, WCD934X_CPE_SS_SVA_CFG) & 0x04) { + dev_dbg(codec->dev, "%s: MAD is bypassed, skip mad %s\n", + __func__, enable ? "enable" : "disable"); + return rc; + } + + dev_dbg(codec->dev, "%s: enable = %s\n", __func__, + enable ? "enable" : "disable"); + + if (enable) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x03); + rc = tavil_codec_config_mad(codec); + if (IS_ERR_VALUE(rc)) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x00); + goto done; + } + + /* Turn on MAD clk */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x01, 0x01); + + /* Undo reset for MAD */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x02, 0x00); + } else { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x00); + /* Reset the MAD block */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x02, 0x02); + /* Turn off MAD clk */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x01, 0x00); + } +done: + return rc; +} + +static int tavil_codec_ape_enable_mad(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int rc = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x40); + rc = __tavil_codec_enable_mad(codec, true); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x00); + __tavil_codec_enable_mad(codec, false); + break; + } + + dev_dbg(tavil->dev, "%s: event = %d\n", __func__, event); + return rc; +} + +static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int rc = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil->mad_switch_cnt++; + if (tavil->mad_switch_cnt != 1) + goto done; + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x20); + rc = __tavil_codec_enable_mad(codec, true); + if (IS_ERR_VALUE(rc)) { + tavil->mad_switch_cnt--; + goto done; + } + + break; + case SND_SOC_DAPM_PRE_PMD: + tavil->mad_switch_cnt--; + if (tavil->mad_switch_cnt != 0) + goto done; + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x00); + __tavil_codec_enable_mad(codec, false); + break; + } +done: + dev_dbg(tavil->dev, "%s: event = %d, mad_switch_cnt = %d\n", + __func__, event, tavil->mad_switch_cnt); + return rc; +} + +static int tavil_get_asrc_mode(struct tavil_priv *tavil, int asrc, + u8 main_sr, u8 mix_sr) +{ + u8 asrc_output_mode; + int asrc_mode = CONV_88P2K_TO_384K; + + if ((asrc < 0) || (asrc >= ASRC_MAX)) + return 0; + + asrc_output_mode = tavil->asrc_output_mode[asrc]; + + if (asrc_output_mode) { + /* + * If Mix sample rate is < 96KHz, use 96K to 352.8K + * conversion, or else use 384K to 352.8K conversion + */ + if (mix_sr < 5) + asrc_mode = CONV_96K_TO_352P8K; + else + asrc_mode = CONV_384K_TO_352P8K; + } else { + /* Integer main and Fractional mix path */ + if (main_sr < 8 && mix_sr > 9) { + asrc_mode = CONV_352P8K_TO_384K; + } else if (main_sr > 8 && mix_sr < 8) { + /* Fractional main and Integer mix path */ + if (mix_sr < 5) + asrc_mode = CONV_96K_TO_352P8K; + else + asrc_mode = CONV_384K_TO_352P8K; + } else if (main_sr < 8 && mix_sr < 8) { + /* Integer main and Integer mix path */ + asrc_mode = CONV_96K_TO_384K; + } + } + + return asrc_mode; +} + +static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, + int asrc_in, int event) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 cfg_reg, ctl_reg, clk_reg, asrc_ctl, mix_ctl_reg; + int asrc, ret = 0; + u8 main_sr, mix_sr, asrc_mode = 0; + + switch (asrc_in) { + case ASRC_IN_HPHL: + cfg_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX1_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC0_CTL1; + asrc = ASRC0; + break; + case ASRC_IN_LO1: + cfg_reg = WCD934X_CDC_RX3_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX3_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC0_CTL1; + asrc = ASRC0; + break; + case ASRC_IN_HPHR: + cfg_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX2_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC1_CTL1; + asrc = ASRC1; + break; + case ASRC_IN_LO2: + cfg_reg = WCD934X_CDC_RX4_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX4_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC1_CTL1; + asrc = ASRC1; + break; + case ASRC_IN_SPKR1: + cfg_reg = WCD934X_CDC_RX7_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX7_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC2_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC2_CTL1; + asrc = ASRC2; + break; + case ASRC_IN_SPKR2: + cfg_reg = WCD934X_CDC_RX8_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX8_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC3_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC3_CTL1; + asrc = ASRC3; + break; + default: + dev_err(codec->dev, "%s: Invalid asrc input :%d\n", __func__, + asrc_in); + ret = -EINVAL; + goto done; + }; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tavil->asrc_users[asrc] == 0) { + snd_soc_update_bits(codec, cfg_reg, 0x80, 0x80); + snd_soc_update_bits(codec, clk_reg, 0x01, 0x01); + main_sr = snd_soc_read(codec, ctl_reg) & 0x0F; + mix_ctl_reg = ctl_reg + 5; + mix_sr = snd_soc_read(codec, mix_ctl_reg) & 0x0F; + asrc_mode = tavil_get_asrc_mode(tavil, asrc, + main_sr, mix_sr); + dev_dbg(codec->dev, "%s: main_sr:%d mix_sr:%d asrc_mode %d\n", + __func__, main_sr, mix_sr, asrc_mode); + snd_soc_update_bits(codec, asrc_ctl, 0x07, asrc_mode); + } + tavil->asrc_users[asrc]++; + break; + case SND_SOC_DAPM_POST_PMD: + tavil->asrc_users[asrc]--; + if (tavil->asrc_users[asrc] <= 0) { + tavil->asrc_users[asrc] = 0; + snd_soc_update_bits(codec, asrc_ctl, 0x07, 0x00); + snd_soc_update_bits(codec, cfg_reg, 0x80, 0x00); + snd_soc_update_bits(codec, clk_reg, 0x01, 0x00); + } + break; + }; + + dev_dbg(codec->dev, "%s: ASRC%d, users: %d\n", + __func__, asrc, tavil->asrc_users[asrc]); + +done: + return ret; +} + +static int tavil_codec_enable_asrc_resampler(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + u8 cfg, asrc_in; + + cfg = snd_soc_read(codec, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0); + if (!(cfg & 0xFF)) { + dev_err(codec->dev, "%s: ASRC%u input not selected\n", + __func__, w->shift); + return -EINVAL; + } + + switch (w->shift) { + case ASRC0: + asrc_in = ((cfg & 0x03) == 1) ? ASRC_IN_HPHL : ASRC_IN_LO1; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC1: + asrc_in = ((cfg & 0x0C) == 4) ? ASRC_IN_HPHR : ASRC_IN_LO2; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC2: + asrc_in = ((cfg & 0x30) == 0x20) ? ASRC_IN_SPKR1 : ASRC_INVALID; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC3: + asrc_in = ((cfg & 0xC0) == 0x80) ? ASRC_IN_SPKR2 : ASRC_INVALID; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + default: + dev_err(codec->dev, "%s: Invalid asrc:%u\n", __func__, + w->shift); + ret = -EINVAL; + break; + }; + + return ret; +} + +static int tavil_enable_native_supply(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (++tavil->native_clk_users == 1) { + snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES, + 0x01, 0x01); + usleep_range(100, 120); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x06, 0x02); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, + 0x04, 0x00); + usleep_range(30, 50); + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x02); + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x10, 0x10); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if (tavil->native_clk_users && + (--tavil->native_clk_users == 0)) { + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x10, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x06, 0x00); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES, + 0x01, 0x00); + } + break; + } + + dev_dbg(codec->dev, "%s: native_clk_users: %d, event: %d\n", + __func__, tavil->native_clk_users, event); + + return 0; +} + +static void tavil_codec_hphdelay_lutbypass(struct snd_soc_codec *codec, + u16 interp_idx, int event) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + 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_update_bits(codec, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, 0x0); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x80); + if (tavil->hph_mode == CLS_H_ULP) + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x20); + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, hph_dly_mask); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00); + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x0); + } +} + +static void tavil_codec_hd2_control(struct tavil_priv *priv, + u16 interp_idx, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + struct snd_soc_codec *codec = priv->codec; + + if (TAVIL_IS_1_1(priv->wcd9xxx)) + return; + + 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_update_bits(codec, hd2_scale_reg, 0x3C, 0x14); + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); + } +} + +static int tavil_codec_config_ear_spkr_gain(struct snd_soc_codec *codec, + int event, int gain_reg) +{ + int comp_gain_offset, val; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + switch (tavil->swr.spkr_mode) { + /* Compander gain in SPKR_MODE1 case is 12 dB */ + case WCD934X_SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (tavil->comp_enabled[COMPANDER_7] && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) && + (tavil->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + tavil->ear_spkr_gain - 1; + snd_soc_write(codec, gain_reg, val); + + dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n", + __func__, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX7 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (tavil->comp_enabled[COMPANDER_7] && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) && + (tavil->ear_spkr_gain != 0)) { + snd_soc_write(codec, gain_reg, 0x0); + + dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n", + __func__); + } + break; + } + + return 0; +} + +static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n, + int event) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int comp; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* EAR does not have compander */ + if (!interp_n) + return 0; + + comp = interp_n - 1; + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, tavil->comp_enabled[comp]); + + if (!tavil->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = WCD934X_CDC_COMPANDER1_CTL0 + (comp * 8); + rx_path_cfg0_reg = WCD934X_CDC_RX1_RX_PATH_CFG0 + (comp * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +static void tavil_codec_idle_detect_control(struct snd_soc_codec *codec, + int interp, int event) +{ + int reg = 0, mask, val; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil->idle_det_cfg.hph_idle_detect_en) + return; + + if (interp == INTERP_HPHL) { + reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL; + mask = 0x01; + val = 0x01; + } + if (interp == INTERP_HPHR) { + reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL; + mask = 0x02; + val = 0x02; + } + + if (reg && SND_SOC_DAPM_EVENT_ON(event)) + snd_soc_update_bits(codec, reg, mask, val); + + if (reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, reg, mask, 0x00); + tavil->idle_det_cfg.hph_idle_thr = 0; + snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, 0x0); + } +} + +/** + * tavil_codec_enable_interp_clk - Enable main path Interpolator + * clock. + * + * @codec: Codec instance + * @event: Indicates speaker path gain offset value + * @intp_idx: Interpolator index + * Returns number of main clock users + */ +int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, + int event, int interp_idx) +{ + struct tavil_priv *tavil; + u16 main_reg; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + + tavil = snd_soc_codec_get_drvdata(codec); + main_reg = WCD934X_CDC_RX0_RX_PATH_CTL + (interp_idx * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (tavil->main_clk_users[interp_idx] == 0) { + /* Main path PGA mute enable */ + snd_soc_update_bits(codec, main_reg, 0x10, 0x10); + /* Clk enable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x20); + tavil_codec_idle_detect_control(codec, interp_idx, + event); + tavil_codec_hd2_control(tavil, interp_idx, event); + tavil_codec_hphdelay_lutbypass(codec, interp_idx, + event); + tavil_config_compander(codec, interp_idx, event); + } + tavil->main_clk_users[interp_idx]++; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + tavil->main_clk_users[interp_idx]--; + if (tavil->main_clk_users[interp_idx] <= 0) { + tavil->main_clk_users[interp_idx] = 0; + tavil_config_compander(codec, interp_idx, event); + tavil_codec_hphdelay_lutbypass(codec, interp_idx, + event); + tavil_codec_hd2_control(tavil, interp_idx, event); + tavil_codec_idle_detect_control(codec, interp_idx, + event); + /* Clk Disable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x00); + /* Reset enable and disable */ + snd_soc_update_bits(codec, main_reg, 0x40, 0x40); + snd_soc_update_bits(codec, main_reg, 0x40, 0x00); + /* Reset rate to 48K*/ + snd_soc_update_bits(codec, main_reg, 0x0F, 0x04); + } + } + + dev_dbg(codec->dev, "%s event %d main_clk_users %d\n", + __func__, event, tavil->main_clk_users[interp_idx]); + + return tavil->main_clk_users[interp_idx]; +} +EXPORT_SYMBOL(tavil_codec_enable_interp_clk); + +static int tavil_codec_set_idle_detect_thr(struct snd_soc_codec *codec, + int interp, int path_type) +{ + int port_id[4] = { 0, 0, 0, 0 }; + int *port_ptr, num_ports; + int bit_width = 0, i; + int mux_reg, mux_reg_val; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int dai_id, idle_thr; + + if ((interp != INTERP_HPHL) && (interp != INTERP_HPHR)) + return 0; + + if (!tavil->idle_det_cfg.hph_idle_detect_en) + return 0; + + port_ptr = &port_id[0]; + num_ports = 0; + + /* + * Read interpolator MUX input registers and find + * which slimbus port is connected and store the port + * numbers in port_id array. + */ + if (path_type == INTERP_MIX_PATH) { + mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1 + + 2 * (interp - 1); + mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f; + + if ((mux_reg_val >= INTn_2_INP_SEL_RX0) && + (mux_reg_val < INTn_2_INP_SEL_PROXIMITY)) { + *port_ptr++ = mux_reg_val + + WCD934X_RX_PORT_START_NUMBER - 1; + num_ports++; + } + } + + if (path_type == INTERP_MAIN_PATH) { + mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0 + + 2 * (interp - 1); + mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f; + i = WCD934X_INTERP_MUX_NUM_INPUTS; + + while (i) { + if ((mux_reg_val >= INTn_1_INP_SEL_RX0) && + (mux_reg_val <= INTn_1_INP_SEL_RX7)) { + *port_ptr++ = mux_reg_val + + WCD934X_RX_PORT_START_NUMBER - + INTn_1_INP_SEL_RX0; + num_ports++; + } + mux_reg_val = (snd_soc_read(codec, mux_reg) & + 0xf0) >> 4; + mux_reg += 1; + i--; + } + } + + dev_dbg(codec->dev, "%s: num_ports: %d, ports[%d %d %d %d]\n", + __func__, num_ports, port_id[0], port_id[1], + port_id[2], port_id[3]); + + i = 0; + while (num_ports) { + dai_id = tavil_find_playback_dai_id_for_port(port_id[i++], + tavil); + + if ((dai_id >= 0) && (dai_id < NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: dai_id: %d bit_width: %d\n", + __func__, dai_id, + tavil->dai[dai_id].bit_width); + + if (tavil->dai[dai_id].bit_width > bit_width) + bit_width = tavil->dai[dai_id].bit_width; + } + + num_ports--; + } + + switch (bit_width) { + case 16: + idle_thr = 0xff; /* F16 */ + break; + case 24: + case 32: + idle_thr = 0x03; /* F22 */ + break; + default: + idle_thr = 0x00; + break; + } + + dev_dbg(codec->dev, "%s: (new) idle_thr: %d, (cur) idle_thr: %d\n", + __func__, idle_thr, tavil->idle_det_cfg.hph_idle_thr); + + if ((tavil->idle_det_cfg.hph_idle_thr == 0) || + (idle_thr < tavil->idle_det_cfg.hph_idle_thr)) { + snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, idle_thr); + tavil->idle_det_cfg.hph_idle_thr = idle_thr; + } + + return 0; +} + +static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 gain_reg, mix_reg; + int offset_val = 0; + int val = 0; + + if (w->shift >= WCD934X_NUM_INTERPOLATORS || + w->shift == INTERP_LO3_NA || w->shift == INTERP_LO4_NA) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + }; + + 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); + + if (w->shift == INTERP_SPKR1 || w->shift == INTERP_SPKR2) + __tavil_codec_enable_swr(w, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_set_idle_detect_thr(codec, w->shift, + INTERP_MIX_PATH); + tavil_codec_enable_interp_clk(codec, event, w->shift); + /* Clk enable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x20); + break; + case SND_SOC_DAPM_POST_PMU: + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + /* Clk Disable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x00); + tavil_codec_enable_interp_clk(codec, event, w->shift); + /* Reset enable and disable */ + snd_soc_update_bits(codec, mix_reg, 0x40, 0x40); + snd_soc_update_bits(codec, mix_reg, 0x40, 0x00); + + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + dev_dbg(codec->dev, "%s event %d name %s\n", __func__, event, w->name); + + return 0; +} + +/** + * tavil_get_dsd_config - Get pointer to dsd config structure + * + * @codec: pointer to snd_soc_codec structure + * + * Returns pointer to tavil_dsd_config structure + */ +struct tavil_dsd_config *tavil_get_dsd_config(struct snd_soc_codec *codec) +{ + struct tavil_priv *tavil; + + if (!codec) + return NULL; + + tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil) + return NULL; + + return tavil->dsd_config; +} +EXPORT_SYMBOL(tavil_get_dsd_config); + +static int tavil_codec_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (w->shift >= WCD934X_NUM_INTERPOLATORS || + w->shift == INTERP_LO3_NA || w->shift == INTERP_LO4_NA) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + }; + + reg = WCD934X_CDC_RX0_RX_PATH_CTL + (w->shift * + WCD934X_RX_PATH_CTL_OFFSET); + gain_reg = WCD934X_CDC_RX0_RX_VOL_CTL + (w->shift * + WCD934X_RX_PATH_CTL_OFFSET); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_set_idle_detect_thr(codec, w->shift, + INTERP_MAIN_PATH); + tavil_codec_enable_interp_clk(codec, event, w->shift); + break; + case SND_SOC_DAPM_POST_PMU: + /* apply gain after int clk is enabled */ + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + tavil_codec_enable_interp_clk(codec, event, w->shift); + + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + + return 0; +} + +static int tavil_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: /* fall through */ + case SND_SOC_DAPM_PRE_PMD: + if (strnstr(w->name, "IIR0", sizeof("IIR0"))) { + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)); + } else { + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)); + } + break; + } + return 0; +} + +static int tavil_codec_find_amic_input(struct snd_soc_codec *codec, + 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_read(codec, adc_mux_in_reg) & mask) >> shift) + == 1); + if (!is_amic) + return 0; + + return snd_soc_read(codec, amic_mux_sel_reg) & 0x07; +} + +static void tavil_codec_set_tx_hold(struct snd_soc_codec *codec, + 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_update_bits(codec, WCD934X_ANA_AMIC2, mask, val); + break; + case WCD934X_ANA_AMIC3: + case WCD934X_ANA_AMIC4: + snd_soc_update_bits(codec, WCD934X_ANA_AMIC4, mask, val); + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic_reg); + break; + } +} + +static int tavil_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int adc_mux_n = w->shift; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int amic_n; + u16 amic_reg; + + dev_dbg(codec->dev, "%s: event: %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + amic_n = tavil_codec_find_amic_input(codec, adc_mux_n); + if (amic_n) { + amic_reg = WCD934X_ANA_AMIC1 + amic_n - 1; + tavil_codec_set_tx_hold(codec, amic_reg, false); + } + break; + default: + break; + } + + return 0; +} + +static u16 tavil_codec_get_amic_pwlvl_reg(struct snd_soc_codec *codec, 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: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic); + break; + } + + return pwr_level_reg; +} + +#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 + +static void tavil_tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct tavil_priv *tavil; + struct snd_soc_codec *codec; + u16 dec_cfg_reg, amic_reg, go_bit_reg; + u8 hpf_cut_off_freq; + int amic_n; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + tavil = hpf_work->tavil; + codec = tavil->codec; + hpf_cut_off_freq = hpf_work->hpf_cut_off_freq; + + dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator; + go_bit_reg = dec_cfg_reg + 7; + + dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, hpf_cut_off_freq); + + amic_n = tavil_codec_find_amic_input(codec, hpf_work->decimator); + if (amic_n) { + amic_reg = WCD934X_ANA_AMIC1 + amic_n - 1; + tavil_codec_set_tx_hold(codec, amic_reg, false); + } + snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x02); + /* Minimum 1 clk cycle delay is required as per HW spec */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x00); +} + +static void tavil_tx_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork; + struct tavil_priv *tavil; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + u16 tx_vol_ctl_reg, hpf_gate_reg; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + tavil = tx_mute_dwork->tavil; + codec = tavil->codec; + + tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + + 16 * tx_mute_dwork->decimator; + hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 + + 16 * tx_mute_dwork->decimator; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); +} + +static int tavil_codec_enable_rx_path_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 sidetone_reg; + + dev_dbg(codec->dev, "%s %d %d\n", __func__, event, w->shift); + sidetone_reg = WCD934X_CDC_RX0_RX_PATH_CFG1 + 0x14*(w->shift); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (!strcmp(w->name, "RX INT7 MIX2 INP")) + __tavil_codec_enable_swr(w, event); + tavil_codec_enable_interp_clk(codec, event, w->shift); + snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x00); + tavil_codec_enable_interp_clk(codec, event, w->shift); + if (!strcmp(w->name, "RX INT7 MIX2 INP")) + __tavil_codec_enable_swr(w, event); + break; + default: + break; + }; + return 0; +} + +static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + 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_cut_off_freq; + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + 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(codec->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(codec->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, wname); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__, + w->name, decimator); + + 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 = tavil_codec_find_amic_input(codec, decimator); + if (amic_n) + pwr_level_reg = tavil_codec_get_amic_pwlvl_reg(codec, + amic_n); + + if (pwr_level_reg) { + switch ((snd_soc_read(codec, pwr_level_reg) & + WCD934X_AMIC_PWR_LVL_MASK) >> + WCD934X_AMIC_PWR_LVL_SHIFT) { + case WCD934X_AMIC_PWR_LEVEL_LP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_LP); + break; + + case WCD934X_AMIC_PWR_LEVEL_HP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_HP); + break; + case WCD934X_AMIC_PWR_LEVEL_DEFAULT: + default: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); + break; + } + } + /* Enable TX PGA Mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMU: + hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + + tavil->tx_hpf_work[decimator].hpf_cut_off_freq = + hpf_cut_off_freq; + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x00); + } + /* schedule work queue to Remove Mute */ + schedule_delayed_work(&tavil->tx_mute_dwork[decimator].dwork, + msecs_to_jiffies(tx_unmute_delay)); + if (tavil->tx_hpf_work[decimator].hpf_cut_off_freq != + CF_MIN_3DB_150HZ) + schedule_delayed_work( + &tavil->tx_hpf_work[decimator].dwork, + msecs_to_jiffies(300)); + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_cut_off_freq = + tavil->tx_hpf_work[decimator].hpf_cut_off_freq; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + if (cancel_delayed_work_sync( + &tavil->tx_hpf_work[decimator].dwork)) { + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x00); + } + } + cancel_delayed_work_sync( + &tavil->tx_mute_dwork[decimator].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); + break; + }; +out: + kfree(wname); + return ret; +} + +static u32 tavil_get_dmic_sample_rate(struct snd_soc_codec *codec, + unsigned int dmic, + struct wcd9xxx_pdata *pdata) +{ + 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_read(codec, 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_read(codec, tx_fs_reg) & 0x0F; + if (tx_stream_fs <= 4) { + if (pdata->dmic_sample_rate <= + WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ) + dmic_fs = pdata->dmic_sample_rate; + else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ; + } else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + } else { + dmic_fs = pdata->dmic_sample_rate; + } + + return dmic_fs; +} + +static u8 tavil_get_dmic_clk_val(struct snd_soc_codec *codec, + u32 mclk_rate, u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val; + + dev_dbg(codec->dev, + "%s: mclk_rate = %d, dmic_sample_rate = %d\n", + __func__, mclk_rate, dmic_clk_rate); + + /* 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(codec->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(codec->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 tavil_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event:%d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_set_tx_hold(codec, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static int tavil_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + 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(codec->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(tavil->dmic_0_1_clk_cnt); + dmic_clk_reg = WCD934X_CPE_SS_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(tavil->dmic_2_3_clk_cnt); + dmic_clk_reg = WCD934X_CPE_SS_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(tavil->dmic_4_5_clk_cnt); + dmic_clk_reg = WCD934X_CPE_SS_DMIC2_CTL; + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + }; + dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dmic_sample_rate = tavil_get_dmic_sample_rate(codec, dmic, + pdata); + dmic_rate_val = + tavil_get_dmic_clk_val(codec, + pdata->mclk_rate, + dmic_sample_rate); + + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + dmic_rate_val = + tavil_get_dmic_clk_val(codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + } + break; + }; + + return 0; +} + +/* + * tavil_mbhc_micb_adjust_voltage: adjust specific micbias voltage + * @codec: handle to snd_soc_codec * + * @req_volt: micbias voltage to be set + * @micb_num: micbias to be set, e.g. micbias1 or micbias2 + * + * return 0 if adjustment is success or error code in case of failure + */ +int tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, int micb_num) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int cur_vout_ctl, req_vout_ctl; + int micb_reg, micb_val, micb_en; + int ret = 0; + + 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: + return -EINVAL; + } + mutex_lock(&tavil->micb_lock); + + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_val = snd_soc_read(codec, micb_reg); + micb_en = (micb_val & 0xC0) >> 6; + cur_vout_ctl = micb_val & 0x3F; + + req_vout_ctl = wcd934x_get_micb_vout_ctl_val(req_volt); + if (IS_ERR_VALUE(req_vout_ctl)) { + ret = -EINVAL; + goto exit; + } + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + dev_dbg(codec->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), + req_volt, micb_en); + + if (micb_en == 0x1) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + + snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl); + + if (micb_en == 0x1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } +exit: + mutex_unlock(&tavil->micb_lock); + return ret; +} +EXPORT_SYMBOL(tavil_mbhc_micb_adjust_voltage); + +/* + * tavil_micbias_control: enable/disable micbias + * @codec: handle to snd_soc_codec * + * @micb_num: micbias to be enabled/disabled, e.g. micbias1 or micbias2 + * @req: control requested, enable/disable or pullup enable/disable + * @is_dapm: triggered by dapm or not + * + * return 0 if control is success or error code in case of failure + */ +int tavil_micbias_control(struct snd_soc_codec *codec, + int micb_num, int req, bool is_dapm) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int micb_index = micb_num - 1; + u16 micb_reg; + int pre_off_event = 0, post_off_event = 0; + int post_on_event = 0, post_dapm_off = 0; + int post_dapm_on = 0; + + if ((micb_index < 0) || (micb_index > TAVIL_MAX_MICBIAS - 1)) { + dev_err(codec->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; + pre_off_event = WCD_EVENT_PRE_MICBIAS_2_OFF; + post_off_event = WCD_EVENT_POST_MICBIAS_2_OFF; + post_on_event = WCD_EVENT_POST_MICBIAS_2_ON; + post_dapm_on = WCD_EVENT_POST_DAPM_MICBIAS_2_ON; + post_dapm_off = WCD_EVENT_POST_DAPM_MICBIAS_2_OFF; + break; + case MIC_BIAS_3: + micb_reg = WCD934X_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD934X_ANA_MICB4; + break; + default: + dev_err(codec->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + mutex_lock(&tavil->micb_lock); + + switch (req) { + case MICB_PULLUP_ENABLE: + tavil->pullup_ref[micb_index]++; + if ((tavil->pullup_ref[micb_index] == 1) && + (tavil->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + break; + case MICB_PULLUP_DISABLE: + if (tavil->pullup_ref[micb_index] > 0) + tavil->pullup_ref[micb_index]--; + if ((tavil->pullup_ref[micb_index] == 0) && + (tavil->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + break; + case MICB_ENABLE: + tavil->micb_ref[micb_index]++; + if (tavil->micb_ref[micb_index] == 1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + if (post_on_event && tavil->mbhc) + blocking_notifier_call_chain( + &tavil->mbhc->notifier, + post_on_event, + &tavil->mbhc->wcd_mbhc); + } + if (is_dapm && post_dapm_on && tavil->mbhc) + blocking_notifier_call_chain(&tavil->mbhc->notifier, + post_dapm_on, &tavil->mbhc->wcd_mbhc); + break; + case MICB_DISABLE: + if (tavil->micb_ref[micb_index] > 0) + tavil->micb_ref[micb_index]--; + if ((tavil->micb_ref[micb_index] == 0) && + (tavil->pullup_ref[micb_index] > 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + else if ((tavil->micb_ref[micb_index] == 0) && + (tavil->pullup_ref[micb_index] == 0)) { + if (pre_off_event && tavil->mbhc) + blocking_notifier_call_chain( + &tavil->mbhc->notifier, + pre_off_event, + &tavil->mbhc->wcd_mbhc); + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + if (post_off_event && tavil->mbhc) + blocking_notifier_call_chain( + &tavil->mbhc->notifier, + post_off_event, + &tavil->mbhc->wcd_mbhc); + } + if (is_dapm && post_dapm_off && tavil->mbhc) + blocking_notifier_call_chain(&tavil->mbhc->notifier, + post_dapm_off, &tavil->mbhc->wcd_mbhc); + break; + }; + + dev_dbg(codec->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n", + __func__, micb_num, tavil->micb_ref[micb_index], + tavil->pullup_ref[micb_index]); + + mutex_unlock(&tavil->micb_lock); + + return 0; +} +EXPORT_SYMBOL(tavil_micbias_control); + +static int __tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int micb_num; + + dev_dbg(codec->dev, "%s: wname: %s, event: %d\n", + __func__, w->name, event); + + 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 + */ + tavil_micbias_control(codec, 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: + tavil_micbias_control(codec, micb_num, MICB_DISABLE, true); + break; + }; + + return 0; +} + +/* + * tavil_codec_enable_standalone_micbias - enable micbias standalone + * @codec: pointer to codec instance + * @micb_num: number of micbias to be enabled + * @enable: true to enable micbias or false to disable + * + * This function is used to enable micbias (1, 2, 3 or 4) during + * standalone independent of whether TX use-case is running or not + * + * Return: error code in case of failure or 0 for success + */ +int tavil_codec_enable_standalone_micbias(struct snd_soc_codec *codec, + int micb_num, + bool enable) +{ + const char * const micb_names[] = { + DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE, + DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE + }; + int micb_index = micb_num - 1; + int rc; + + if (!codec) { + pr_err("%s: Codec memory is NULL\n", __func__); + return -EINVAL; + } + + if ((micb_index < 0) || (micb_index > TAVIL_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + + if (enable) + rc = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + else + rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + + if (!rc) + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + else + dev_err(codec->dev, "%s: micbias%d force %s pin failed\n", + __func__, micb_num, (enable ? "enable" : "disable")); + + return rc; +} +EXPORT_SYMBOL(tavil_codec_enable_standalone_micbias); + +static int tavil_codec_force_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_resmgr_enable_master_bias(tavil->resmgr); + tavil_cdc_mclk_enable(codec, true); + ret = __tavil_codec_enable_micbias(w, SND_SOC_DAPM_PRE_PMU); + /* Wait for 1ms for better cnp */ + usleep_range(1000, 1100); + tavil_cdc_mclk_enable(codec, false); + break; + case SND_SOC_DAPM_POST_PMD: + ret = __tavil_codec_enable_micbias(w, SND_SOC_DAPM_POST_PMD); + wcd_resmgr_disable_master_bias(tavil->resmgr); + break; + } + + return ret; +} + +static int tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return __tavil_codec_enable_micbias(w, event); +} + + +static const struct reg_sequence tavil_hph_reset_tbl[] = { + { WCD934X_HPH_CNP_EN, 0x80 }, + { WCD934X_HPH_CNP_WG_CTL, 0x9A }, + { WCD934X_HPH_CNP_WG_TIME, 0x14 }, + { WCD934X_HPH_OCP_CTL, 0x28 }, + { WCD934X_HPH_AUTO_CHOP, 0x16 }, + { WCD934X_HPH_CHOP_CTL, 0x83 }, + { WCD934X_HPH_PA_CTL1, 0x46 }, + { WCD934X_HPH_PA_CTL2, 0x50 }, + { WCD934X_HPH_L_EN, 0x80 }, + { WCD934X_HPH_L_TEST, 0xE0 }, + { WCD934X_HPH_L_ATEST, 0x50 }, + { WCD934X_HPH_R_EN, 0x80 }, + { WCD934X_HPH_R_TEST, 0xE0 }, + { WCD934X_HPH_R_ATEST, 0x54 }, + { WCD934X_HPH_RDAC_CLK_CTL1, 0x99 }, + { WCD934X_HPH_RDAC_CLK_CTL2, 0x9B }, + { WCD934X_HPH_RDAC_LDO_CTL, 0x33 }, + { WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD934X_HPH_REFBUFF_UHQA_CTL, 0xA8 }, +}; + +static const struct reg_sequence tavil_hph_reset_tbl_1_0[] = { + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0A }, + { WCD934X_HPH_L_DAC_CTL, 0x00 }, + { WCD934X_HPH_R_DAC_CTL, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0xA0 }, + { WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 }, + { WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xFE }, + { WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x2 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e}, + { WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, +}; + +static const struct reg_sequence tavil_hph_reset_tbl_1_1[] = { + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0E }, + { WCD934X_HPH_L_DAC_CTL, 0x00 }, + { WCD934X_HPH_R_DAC_CTL, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0x81 }, + { WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 }, + { WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x81 }, + { WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xFE }, + { WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x2 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e}, + { WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, +}; + +static const struct tavil_reg_mask_val tavil_pa_disable[] = { + { WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x10 }, /* RX1 mute enable */ + { WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x10 }, /* RX2 mute enable */ + { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, /* GM3 boost disable */ + { WCD934X_ANA_HPH, 0x80, 0x00 }, /* HPHL PA disable */ + { WCD934X_ANA_HPH, 0x40, 0x00 }, /* HPHR PA disable */ + { WCD934X_ANA_HPH, 0x20, 0x00 }, /* HPHL REF dsable */ + { WCD934X_ANA_HPH, 0x10, 0x00 }, /* HPHR REF disable */ +}; + +static const struct tavil_reg_mask_val tavil_ocp_en_seq[] = { + { WCD934X_RX_OCP_CTL, 0x0F, 0x02 }, /* OCP number of attempts is 2 */ + { WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */ + { WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */ + { WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */ +}; + +static const struct tavil_reg_mask_val tavil_ocp_en_seq_1[] = { + { WCD934X_RX_OCP_CTL, 0x0F, 0x02 }, /* OCP number of attempts is 2 */ + { WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */ +}; + +/* LO-HIFI */ +static const struct tavil_reg_mask_val tavil_pre_pa_en_lohifi[] = { + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 }, + { WCD934X_FLYBACK_VNEG_CTRL_4, 0xf0, 0x80 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x20 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 }, + { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, + { WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0xc0 }, + { WCD934X_HPH_PA_CTL1, 0x0e, 0x02 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 }, +}; + +static const struct tavil_reg_mask_val tavil_pre_pa_en[] = { + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x0 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 }, + { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, + { WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0x80 }, + { WCD934X_HPH_PA_CTL1, 0x0e, 0x06 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 }, +}; + +static const struct tavil_reg_mask_val tavil_post_pa_en[] = { + { WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */ + { WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */ + { WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x20 }, /* RX1 mute disable */ + { WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x20 }, /* RX2 mute disable */ + { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x80 }, /* GM3 boost enable */ + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x02 }, +}; + +static void tavil_codec_hph_reg_range_read(struct regmap *map, u8 *buf) +{ + regmap_bulk_read(map, WCD934X_HPH_CNP_EN, buf, TAVIL_HPH_REG_RANGE_1); + regmap_bulk_read(map, WCD934X_HPH_NEW_ANA_HPH2, + buf + TAVIL_HPH_REG_RANGE_1, TAVIL_HPH_REG_RANGE_2); + regmap_bulk_read(map, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + buf + TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2, + TAVIL_HPH_REG_RANGE_3); +} + +static void tavil_codec_hph_reg_recover(struct tavil_priv *tavil, + struct regmap *map, int pa_status) +{ + int i; + unsigned int reg; + + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_OCP_OFF, + &tavil->mbhc->wcd_mbhc); + + if (pa_status & 0xC0) + goto pa_en_restore; + + dev_dbg(tavil->dev, "%s: HPH PA in disable state (0x%x)\n", + __func__, pa_status); + + regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x10); + regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x10); + regmap_write_bits(map, WCD934X_ANA_HPH, 0xC0, 0x00); + regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x00); + regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x00); + regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x00); + + /* Restore to HW defaults */ + regmap_multi_reg_write(map, tavil_hph_reset_tbl, + ARRAY_SIZE(tavil_hph_reset_tbl)); + if (TAVIL_IS_1_1(tavil->wcd9xxx)) + regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_1, + ARRAY_SIZE(tavil_hph_reset_tbl_1_1)); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_0, + ARRAY_SIZE(tavil_hph_reset_tbl_1_0)); + + for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq); i++) + regmap_write_bits(map, tavil_ocp_en_seq[i].reg, + tavil_ocp_en_seq[i].mask, + tavil_ocp_en_seq[i].val); + goto end; + + +pa_en_restore: + dev_dbg(tavil->dev, "%s: HPH PA in enable state (0x%x)\n", + __func__, pa_status); + + /* Disable PA and other registers before restoring */ + for (i = 0; i < ARRAY_SIZE(tavil_pa_disable); i++) { + if (TAVIL_IS_1_1(tavil->wcd9xxx) && + (tavil_pa_disable[i].reg == WCD934X_HPH_CNP_WG_CTL)) + continue; + regmap_write_bits(map, tavil_pa_disable[i].reg, + tavil_pa_disable[i].mask, + tavil_pa_disable[i].val); + } + + regmap_multi_reg_write(map, tavil_hph_reset_tbl, + ARRAY_SIZE(tavil_hph_reset_tbl)); + if (TAVIL_IS_1_1(tavil->wcd9xxx)) + regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_1, + ARRAY_SIZE(tavil_hph_reset_tbl_1_1)); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_0, + ARRAY_SIZE(tavil_hph_reset_tbl_1_0)); + + for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq_1); i++) + regmap_write_bits(map, tavil_ocp_en_seq_1[i].reg, + tavil_ocp_en_seq_1[i].mask, + tavil_ocp_en_seq_1[i].val); + + if (tavil->hph_mode == CLS_H_LOHIFI) { + for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en_lohifi); i++) { + reg = tavil_pre_pa_en_lohifi[i].reg; + if ((TAVIL_IS_1_1(tavil->wcd9xxx)) && + ((reg == WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL) || + (reg == WCD934X_HPH_CNP_WG_CTL) || + (reg == WCD934X_HPH_REFBUFF_LP_CTL))) + continue; + regmap_write_bits(map, + tavil_pre_pa_en_lohifi[i].reg, + tavil_pre_pa_en_lohifi[i].mask, + tavil_pre_pa_en_lohifi[i].val); + } + } else { + for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en); i++) { + reg = tavil_pre_pa_en[i].reg; + if ((TAVIL_IS_1_1(tavil->wcd9xxx)) && + ((reg == WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL) || + (reg == WCD934X_HPH_CNP_WG_CTL) || + (reg == WCD934X_HPH_REFBUFF_LP_CTL))) + continue; + regmap_write_bits(map, tavil_pre_pa_en[i].reg, + tavil_pre_pa_en[i].mask, + tavil_pre_pa_en[i].val); + } + } + + if (TAVIL_IS_1_1(tavil->wcd9xxx)) { + regmap_write(map, WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x84); + regmap_write(map, WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x84); + } + + regmap_write_bits(map, WCD934X_ANA_HPH, 0x0C, pa_status & 0x0C); + regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x30); + /* wait for 100usec after HPH DAC is enabled */ + usleep_range(100, 110); + regmap_write(map, WCD934X_ANA_HPH, pa_status); + /* Sleep for 7msec after PA is enabled */ + usleep_range(7000, 7100); + + for (i = 0; i < ARRAY_SIZE(tavil_post_pa_en); i++) { + if ((TAVIL_IS_1_1(tavil->wcd9xxx)) && + (tavil_post_pa_en[i].reg == WCD934X_HPH_CNP_WG_CTL)) + continue; + regmap_write_bits(map, tavil_post_pa_en[i].reg, + tavil_post_pa_en[i].mask, + tavil_post_pa_en[i].val); + } + +end: + tavil->mbhc->is_hph_recover = true; + blocking_notifier_call_chain( + &tavil->mbhc->notifier, + WCD_EVENT_OCP_ON, + &tavil->mbhc->wcd_mbhc); +} + +static int tavil_codec_reset_hph_registers(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + u8 cache_val[TAVIL_HPH_TOTAL_REG]; + u8 hw_val[TAVIL_HPH_TOTAL_REG]; + int pa_status; + int ret; + + dev_dbg(wcd9xxx->dev, "%s: event: %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + memset(cache_val, 0, TAVIL_HPH_TOTAL_REG); + memset(hw_val, 0, TAVIL_HPH_TOTAL_REG); + + regmap_read(wcd9xxx->regmap, WCD934X_ANA_HPH, &pa_status); + + tavil_codec_hph_reg_range_read(wcd9xxx->regmap, cache_val); + + /* Read register values from HW directly */ + regcache_cache_bypass(wcd9xxx->regmap, true); + tavil_codec_hph_reg_range_read(wcd9xxx->regmap, hw_val); + regcache_cache_bypass(wcd9xxx->regmap, false); + + /* compare both the registers to know if there is corruption */ + ret = memcmp(cache_val, hw_val, TAVIL_HPH_TOTAL_REG); + + /* If both the values are same, it means no corruption */ + if (ret) { + dev_dbg(codec->dev, "%s: cache and hw reg are not same\n", + __func__); + tavil_codec_hph_reg_recover(tavil, wcd9xxx->regmap, + pa_status); + } else { + dev_dbg(codec->dev, "%s: cache and hw reg are same\n", + __func__); + tavil->mbhc->is_hph_recover = false; + } + break; + default: + break; + }; + + return 0; +} + +static int tavil_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + /* IIR filter band registers are at integer multiples of 16 */ + u16 iir_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int tavil_iir_enable_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + bool iir_band_en_status; + int value = ucontrol->value.integer.value[0]; + u16 iir_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, iir_reg, (1 << band_idx), + (value << band_idx)); + + iir_band_en_status = ((snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0); + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, iir_band_en_status); + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx)); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 8); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 16); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) & 0x3F) << 24); + + return value; +} + +static int tavil_iir_band_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 24) & 0x3F); +} + +static int tavil_iir_band_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + /* + * Mask top bit it is reserved + * Updates addr automatically for each B2 write + */ + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[0]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[1]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[2]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[3]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[4]); + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static int tavil_compander_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->comp_enabled[comp]; + return 0; +} + +static int tavil_compander_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, tavil->comp_enabled[comp], value); + tavil->comp_enabled[comp] = value; + + /* Any specific register configuration for compander */ + switch (comp) { + case COMPANDER_1: + /* Set Gain Source Select based on compander enable/disable */ + snd_soc_update_bits(codec, WCD934X_HPH_L_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_2: + snd_soc_update_bits(codec, WCD934X_HPH_R_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_3: + case COMPANDER_4: + case COMPANDER_7: + case COMPANDER_8: + break; + default: + /* + * if compander is not enabled for any interpolator, + * it does not cause any audio failure, so do not + * return error in this case, but just print a log + */ + dev_warn(codec->dev, "%s: unknown compander: %d\n", + __func__, comp); + }; + return 0; +} + +static int tavil_hph_asrc_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int index = -EINVAL; + + if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode")) + index = ASRC0; + if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode")) + index = ASRC1; + + if (tavil && (index >= 0) && (index < ASRC_MAX)) + tavil->asrc_output_mode[index] = + ucontrol->value.integer.value[0]; + + return 0; +} + +static int tavil_hph_asrc_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int val = 0; + int index = -EINVAL; + + if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode")) + index = ASRC0; + if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode")) + index = ASRC1; + + if (tavil && (index >= 0) && (index < ASRC_MAX)) + val = tavil->asrc_output_mode[index]; + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int tavil_hph_idle_detect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int val = 0; + + if (tavil) + val = tavil->idle_det_cfg.hph_idle_detect_en; + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int tavil_hph_idle_detect_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + if (tavil) + tavil->idle_det_cfg.hph_idle_detect_en = + ucontrol->value.integer.value[0]; + + return 0; +} + +static int tavil_dmic_pin_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 dmic_pin; + u8 reg_val, pinctl_position; + + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + dmic_pin = pinctl_position & 0x07; + reg_val = snd_soc_read(codec, + WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1); + + ucontrol->value.integer.value[0] = !!reg_val; + + return 0; +} + +static int tavil_dmic_pin_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 ctl_reg, cfg_reg, dmic_pin; + u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask; + + /* 0- high or low; 1- high Z */ + pinctl_mode = ucontrol->value.integer.value[0]; + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + switch (pinctl_position >> 3) { + case 0: + ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_0; + break; + case 1: + ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_1; + break; + case 2: + ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_2; + break; + case 3: + ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_3; + break; + default: + dev_err(codec->dev, "%s: Invalid pinctl position = %d\n", + __func__, pinctl_position); + return -EINVAL; + } + + ctl_val = ~(pinctl_mode << (pinctl_position & 0x07)); + mask = 1 << (pinctl_position & 0x07); + snd_soc_update_bits(codec, ctl_reg, mask, ctl_val); + + dmic_pin = pinctl_position & 0x07; + cfg_reg = WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1; + if (pinctl_mode) { + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + cfg_val = 0x6; + else + cfg_val = 0xD; + } else + cfg_val = 0; + snd_soc_update_bits(codec, cfg_reg, 0x1F, cfg_val); + + dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n", + __func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val); + + return 0; +} + +static int tavil_amic_pwr_lvl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 amic_reg; + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD934X_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD934X_ANA_AMIC3; + else + goto ret; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, amic_reg) & WCD934X_AMIC_PWR_LVL_MASK) >> + WCD934X_AMIC_PWR_LVL_SHIFT; +ret: + return 0; +} + +static int tavil_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u32 mode_val; + u16 amic_reg; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val); + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD934X_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD934X_ANA_AMIC3; + else + goto ret; + + snd_soc_update_bits(codec, amic_reg, WCD934X_AMIC_PWR_LVL_MASK, + mode_val << WCD934X_AMIC_PWR_LVL_SHIFT); +ret: + return 0; +} + +static const char *const tavil_conn_mad_text[] = { + "NOTUSED1", "ADC1", "ADC2", "ADC3", "ADC4", "NOTUSED5", + "NOTUSED6", "NOTUSED2", "DMIC0", "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "NOTUSED3", "NOTUSED4" +}; + +static const struct soc_enum tavil_conn_mad_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tavil_conn_mad_text), + tavil_conn_mad_text); + +static int tavil_mad_input_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u8 tavil_mad_input; + + tavil_mad_input = snd_soc_read(codec, WCD934X_SOC_MAD_INP_SEL) & 0x0F; + ucontrol->value.integer.value[0] = tavil_mad_input; + + dev_dbg(codec->dev, "%s: tavil_mad_input = %s\n", __func__, + tavil_conn_mad_text[tavil_mad_input]); + + return 0; +} + +static int tavil_mad_input_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + u8 tavil_mad_input; + char mad_amic_input_widget[6]; + const char *mad_input_widget; + const char *source_widget = NULL; + u32 adc, i, mic_bias_found = 0; + int ret = 0; + char *mad_input; + bool is_adc2_input = false; + + tavil_mad_input = ucontrol->value.integer.value[0]; + + if (strnstr(tavil_conn_mad_text[tavil_mad_input], "NOTUSED", + sizeof("NOTUSED"))) { + dev_dbg(codec->dev, + "%s: Unsupported tavil_mad_input = %s\n", + __func__, tavil_conn_mad_text[tavil_mad_input]); + /* Make sure the MAD register is updated */ + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x88, 0x00); + return -EINVAL; + } + + if (strnstr(tavil_conn_mad_text[tavil_mad_input], + "ADC", sizeof("ADC"))) { + mad_input = strpbrk(tavil_conn_mad_text[tavil_mad_input], + "1234"); + if (!mad_input) { + dev_err(codec->dev, "%s: Invalid MAD input %s\n", + __func__, tavil_conn_mad_text[tavil_mad_input]); + return -EINVAL; + } + + ret = kstrtouint(mad_input, 10, &adc); + if ((ret < 0) || (adc > 4)) { + dev_err(codec->dev, "%s: Invalid ADC = %s\n", __func__, + tavil_conn_mad_text[tavil_mad_input]); + return -EINVAL; + } + + /*AMIC4 and AMIC5 share ADC4*/ + if ((adc == 4) && + (snd_soc_read(codec, WCD934X_TX_NEW_AMIC_4_5_SEL) & 0x10)) + adc = 5; + + snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc); + + mad_input_widget = mad_amic_input_widget; + if (adc == 2) + is_adc2_input = true; + } else { + /* DMIC type input widget*/ + mad_input_widget = tavil_conn_mad_text[tavil_mad_input]; + } + + dev_dbg(codec->dev, + "%s: tavil input widget = %s, adc_input = %s\n", __func__, + mad_input_widget, is_adc2_input ? "true" : "false"); + + for (i = 0; i < card->num_of_dapm_routes; i++) { + if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) { + source_widget = card->of_dapm_routes[i].source; + if (!source_widget) { + dev_err(codec->dev, + "%s: invalid source widget\n", + __func__); + return -EINVAL; + } + + if (strnstr(source_widget, + "MIC BIAS1", sizeof("MIC BIAS1"))) { + mic_bias_found = 1; + break; + } else if (strnstr(source_widget, + "MIC BIAS2", sizeof("MIC BIAS2"))) { + mic_bias_found = 2; + break; + } else if (strnstr(source_widget, + "MIC BIAS3", sizeof("MIC BIAS3"))) { + mic_bias_found = 3; + break; + } else if (strnstr(source_widget, + "MIC BIAS4", sizeof("MIC BIAS4"))) { + mic_bias_found = 4; + break; + } + } + } + + if (!mic_bias_found) { + dev_err(codec->dev, "%s: mic bias not found for input %s\n", + __func__, mad_input_widget); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: mic_bias found = %d\n", __func__, + mic_bias_found); + + snd_soc_update_bits(codec, WCD934X_SOC_MAD_INP_SEL, + 0x0F, tavil_mad_input); + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x07, mic_bias_found); + /* for adc2 input, mad should be in micbias mode with BG enabled */ + if (is_adc2_input) + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x88, 0x88); + else + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x88, 0x00); + return 0; +} + +static int tavil_ear_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ear_pa_gain = snd_soc_read(codec, WCD934X_ANA_EAR); + + ear_pa_gain = (ear_pa_gain & 0x70) >> 4; + + ucontrol->value.integer.value[0] = ear_pa_gain; + + dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__, + ear_pa_gain); + + return 0; +} + +static int tavil_ear_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + ear_pa_gain = ucontrol->value.integer.value[0] << 4; + + snd_soc_update_bits(codec, WCD934X_ANA_EAR, 0x70, ear_pa_gain); + return 0; +} + +static int tavil_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->ear_spkr_gain; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int tavil_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + tavil->ear_spkr_gain = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: gain = %d\n", __func__, tavil->ear_spkr_gain); + + return 0; +} + +static int tavil_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->hph_mode; + return 0; +} + +static int tavil_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val); + + if (mode_val == 0) { + dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H LOHiFi\n", + __func__); + mode_val = CLS_H_LOHIFI; + } + tavil->hph_mode = mode_val; + return 0; +} + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", + "CLS_H_ULP", "CLS_AB_HIFI", +}; + +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 char *const tavil_anc_func_text[] = {"OFF", "ON"}; +static const struct soc_enum tavil_anc_func_enum = + SOC_ENUM_SINGLE_EXT(2, tavil_anc_func_text); + +/* 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 amic_pwr_lvl_text[] = { + "LOW_PWR", "DEFAULT", "HIGH_PERF" +}; + +static const char * const hph_idle_detect_text[] = { + "OFF", "ON" +}; + +static const char * const asrc_mode_text[] = { + "INT", "FRAC" +}; + +static const char * const tavil_ear_pa_gain_text[] = { + "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", + "G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB" +}; + +static const char * const tavil_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", + "G_4_DB", "G_5_DB", "G_6_DB" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_pa_gain_enum, tavil_ear_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_spkr_pa_gain_enum, + tavil_ear_spkr_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(amic_pwr_lvl_enum, amic_pwr_lvl_text); +static SOC_ENUM_SINGLE_EXT_DECL(hph_idle_detect_enum, hph_idle_detect_text); +static SOC_ENUM_SINGLE_EXT_DECL(asrc_mode_enum, asrc_mode_text); +static SOC_ENUM_SINGLE_DECL(cf_dec0_enum, WCD934X_CDC_TX0_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec1_enum, WCD934X_CDC_TX1_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec2_enum, WCD934X_CDC_TX2_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec3_enum, WCD934X_CDC_TX3_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec4_enum, WCD934X_CDC_TX4_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec5_enum, WCD934X_CDC_TX5_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec6_enum, WCD934X_CDC_TX6_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec7_enum, WCD934X_CDC_TX7_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec8_enum, WCD934X_CDC_TX8_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int0_1_enum, WCD934X_CDC_RX0_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int1_1_enum, WCD934X_CDC_RX1_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int2_1_enum, WCD934X_CDC_RX2_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int3_1_enum, WCD934X_CDC_RX3_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int4_1_enum, WCD934X_CDC_RX4_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int7_1_enum, WCD934X_CDC_RX7_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int8_1_enum, WCD934X_CDC_RX8_RX_PATH_CFG2, 0, + 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 snd_kcontrol_new tavil_snd_controls[] = { + SOC_ENUM_EXT("EAR PA Gain", tavil_ear_pa_gain_enum, + tavil_ear_pa_gain_get, tavil_ear_pa_gain_put), + SOC_ENUM_EXT("EAR SPKR PA Gain", tavil_ear_spkr_pa_gain_enum, + tavil_ear_spkr_pa_gain_get, tavil_ear_spkr_pa_gain_put), + SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 20, 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_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tavil_get_anc_slot, + tavil_put_anc_slot), + SOC_ENUM_EXT("ANC Function", tavil_anc_func_enum, tavil_get_anc_func, + tavil_put_anc_func), + + 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, + tavil_rx_hph_mode_get, tavil_rx_hph_mode_put), + + SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + + SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, + tavil_compander_get, tavil_compander_put), + + SOC_ENUM_EXT("ASRC0 Output Mode", asrc_mode_enum, + tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put), + SOC_ENUM_EXT("ASRC1 Output Mode", asrc_mode_enum, + tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put), + + SOC_ENUM_EXT("HPH Idle Detect", hph_idle_detect_enum, + tavil_hph_idle_detect_get, tavil_hph_idle_detect_put), + + SOC_ENUM_EXT("MAD Input", tavil_conn_mad_enum, + tavil_mad_input_get, tavil_mad_input_put), + + SOC_SINGLE_EXT("DMIC1_CLK_PIN_MODE", SND_SOC_NOPM, 17, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC1_DATA_PIN_MODE", SND_SOC_NOPM, 18, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC2_CLK_PIN_MODE", SND_SOC_NOPM, 19, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC2_DATA_PIN_MODE", SND_SOC_NOPM, 20, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC3_CLK_PIN_MODE", SND_SOC_NOPM, 21, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC3_DATA_PIN_MODE", SND_SOC_NOPM, 22, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + SOC_ENUM_EXT("AMIC_1_2 PWR MODE", amic_pwr_lvl_enum, + tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_3_4 PWR MODE", amic_pwr_lvl_enum, + tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_5_6 PWR MODE", amic_pwr_lvl_enum, + tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put), +}; + +static int tavil_dec_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg = 0; + u8 mic_sel; + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + 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(codec->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_update_bits(codec, mic_sel_reg, 1 << 7, mic_sel << 7); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int tavil_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + unsigned short look_ahead_dly_reg = WCD934X_CDC_RX0_RX_PATH_CFG0; + + val = ucontrol->value.enumerated.item[0]; + if (val >= e->items) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + if (e->reg == WCD934X_CDC_RX0_RX_PATH_SEC0) + look_ahead_dly_reg = WCD934X_CDC_RX0_RX_PATH_CFG0; + else if (e->reg == WCD934X_CDC_RX1_RX_PATH_SEC0) + look_ahead_dly_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + else if (e->reg == WCD934X_CDC_RX2_RX_PATH_SEC0) + look_ahead_dly_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + + /* Set Look Ahead Delay */ + snd_soc_update_bits(codec, look_ahead_dly_reg, + 0x08, (val ? 0x08 : 0x00)); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +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 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 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 mad_sel_txt[] = { + "SPE", "MSM" +}; + +static const char * const mad_inp_mux_txt[] = { + "MAD", "DEC1" +}; + +static const char * const adc_mux_text[] = { + "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2" +}; + +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 anc0_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHL", "ANC_IN_EAR", "ANC_IN_EAR_SPKR", + "ANC_IN_LO1" +}; + +static const char * const anc1_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHR", "ANC_IN_LO2" +}; + +static const char * const rx_echo_mux_text[] = { + "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2", "RX_MIX3", "RX_MIX4", + "RX_MIX5", "RX_MIX6", "RX_MIX7", "RX_MIX8" +}; + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB" +}; + +static const char *const cdc_if_rx0_mux_text[] = { + "SLIM RX0", "I2S_0 RX0" +}; +static const char *const cdc_if_rx1_mux_text[] = { + "SLIM RX1", "I2S_0 RX1" +}; +static const char *const cdc_if_rx2_mux_text[] = { + "SLIM RX2", "I2S_0 RX2" +}; +static const char *const cdc_if_rx3_mux_text[] = { + "SLIM RX3", "I2S_0 RX3" +}; +static const char *const cdc_if_rx4_mux_text[] = { + "SLIM RX4", "I2S_0 RX4" +}; +static const char *const cdc_if_rx5_mux_text[] = { + "SLIM RX5", "I2S_0 RX5" +}; +static const char *const cdc_if_rx6_mux_text[] = { + "SLIM RX6", "I2S_0 RX6" +}; +static const char *const cdc_if_rx7_mux_text[] = { + "SLIM RX7", "I2S_0 RX7" +}; + +static const char * const asrc0_mux_text[] = { + "ZERO", "ASRC_IN_HPHL", "ASRC_IN_LO1", +}; + +static const char * const asrc1_mux_text[] = { + "ZERO", "ASRC_IN_HPHR", "ASRC_IN_LO2", +}; + +static const char * const asrc2_mux_text[] = { + "ZERO", "ASRC_IN_SPKR1", +}; + +static const char * const asrc3_mux_text[] = { + "ZERO", "ASRC_IN_SPKR2", +}; + +static const char * const native_mux_text[] = { + "OFF", "ON", +}; + +static const struct snd_kcontrol_new aif4_vi_mixer[] = { + SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, WCD934X_TX14, 1, 0, + tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put), + SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, WCD934X_TX15, 1, 0, + tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put), +}; + +static const struct snd_kcontrol_new aif1_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_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_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 aif4_mad_mixer[] = { + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +WCD_DAPM_ENUM_EXT(slim_rx0, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx1, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx2, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx3, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx4, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx5, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx6, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx7, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); + +WCD_DAPM_ENUM(cdc_if_rx0, SND_SOC_NOPM, 0, cdc_if_rx0_mux_text); +WCD_DAPM_ENUM(cdc_if_rx1, SND_SOC_NOPM, 0, cdc_if_rx1_mux_text); +WCD_DAPM_ENUM(cdc_if_rx2, SND_SOC_NOPM, 0, cdc_if_rx2_mux_text); +WCD_DAPM_ENUM(cdc_if_rx3, SND_SOC_NOPM, 0, cdc_if_rx3_mux_text); +WCD_DAPM_ENUM(cdc_if_rx4, SND_SOC_NOPM, 0, cdc_if_rx4_mux_text); +WCD_DAPM_ENUM(cdc_if_rx5, SND_SOC_NOPM, 0, cdc_if_rx5_mux_text); +WCD_DAPM_ENUM(cdc_if_rx6, SND_SOC_NOPM, 0, cdc_if_rx6_mux_text); +WCD_DAPM_ENUM(cdc_if_rx7, SND_SOC_NOPM, 0, cdc_if_rx7_mux_text); + +WCD_DAPM_ENUM(rx_int0_2, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, + rx_int0_7_mix_mux_text); +WCD_DAPM_ENUM(rx_int1_2, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int2_2, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int3_2, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int4_2, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int7_2, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, + rx_int0_7_mix_mux_text); +WCD_DAPM_ENUM(rx_int8_2, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, + rx_int_mix_mux_text); + +WCD_DAPM_ENUM(rx_int0_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int0_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int0_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int1_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int1_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int1_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int2_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int2_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int2_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int3_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int3_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int3_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int4_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int4_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int4_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int7_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int7_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int7_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int8_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int8_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int8_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, + rx_prim_mix_text); + +WCD_DAPM_ENUM(rx_int0_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int1_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int2_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int3_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int4_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int7_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, + rx_sidetone_mix_text); + +WCD_DAPM_ENUM(tx_adc_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 4, + adc_mux_text); +WCD_DAPM_ENUM(tx_adc_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 4, + adc_mux_text); +WCD_DAPM_ENUM(tx_adc_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 4, + adc_mux_text); +WCD_DAPM_ENUM(tx_adc_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 6, + adc_mux_text); + + +WCD_DAPM_ENUM(tx_dmic_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 3, + dmic_mux_text); + + +WCD_DAPM_ENUM(tx_amic_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0, + amic_mux_text); + +WCD_DAPM_ENUM(tx_amic4_5, WCD934X_TX_NEW_AMIC_4_5_SEL, 7, amic4_5_sel_text); + +WCD_DAPM_ENUM(cdc_if_tx0, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 0, + cdc_if_tx0_mux_text); +WCD_DAPM_ENUM(cdc_if_tx1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 2, + cdc_if_tx1_mux_text); +WCD_DAPM_ENUM(cdc_if_tx2, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 4, + cdc_if_tx2_mux_text); +WCD_DAPM_ENUM(cdc_if_tx3, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 6, + cdc_if_tx3_mux_text); +WCD_DAPM_ENUM(cdc_if_tx4, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 0, + cdc_if_tx4_mux_text); +WCD_DAPM_ENUM(cdc_if_tx5, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 2, + cdc_if_tx5_mux_text); +WCD_DAPM_ENUM(cdc_if_tx6, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 4, + cdc_if_tx6_mux_text); +WCD_DAPM_ENUM(cdc_if_tx7, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 6, + cdc_if_tx7_mux_text); +WCD_DAPM_ENUM(cdc_if_tx8, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 0, + cdc_if_tx8_mux_text); +WCD_DAPM_ENUM(cdc_if_tx9, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 2, + cdc_if_tx9_mux_text); +WCD_DAPM_ENUM(cdc_if_tx10, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 4, + cdc_if_tx10_mux_text); +WCD_DAPM_ENUM(cdc_if_tx11_inp1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 0, + cdc_if_tx11_inp1_mux_text); +WCD_DAPM_ENUM(cdc_if_tx11, WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0, + cdc_if_tx11_mux_text); +WCD_DAPM_ENUM(cdc_if_tx13_inp1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 4, + cdc_if_tx13_inp1_mux_text); +WCD_DAPM_ENUM(cdc_if_tx13, WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0, + cdc_if_tx13_mux_text); + +WCD_DAPM_ENUM(rx_mix_tx0, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx1, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx2, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx3, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx4, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx5, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx6, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx7, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx8, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4, 0, + rx_echo_mux_text); + +WCD_DAPM_ENUM(iir0_inp0, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir0_inp1, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir0_inp2, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir0_inp3, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir1_inp0, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir1_inp1, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir1_inp2, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir1_inp3, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0, + iir_inp_mux_text); + +WCD_DAPM_ENUM(rx_int0_1_interp, SND_SOC_NOPM, 0, rx_int0_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int1_1_interp, SND_SOC_NOPM, 0, rx_int1_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int2_1_interp, SND_SOC_NOPM, 0, rx_int2_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int3_1_interp, SND_SOC_NOPM, 0, rx_int3_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int4_1_interp, SND_SOC_NOPM, 0, rx_int4_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int7_1_interp, SND_SOC_NOPM, 0, rx_int7_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int8_1_interp, SND_SOC_NOPM, 0, rx_int8_1_interp_mux_text); + +WCD_DAPM_ENUM(rx_int0_2_interp, SND_SOC_NOPM, 0, rx_int0_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int1_2_interp, SND_SOC_NOPM, 0, rx_int1_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int2_2_interp, SND_SOC_NOPM, 0, rx_int2_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int3_2_interp, SND_SOC_NOPM, 0, rx_int3_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int4_2_interp, SND_SOC_NOPM, 0, rx_int4_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int7_2_interp, SND_SOC_NOPM, 0, rx_int7_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int8_2_interp, SND_SOC_NOPM, 0, rx_int8_2_interp_mux_text); + +WCD_DAPM_ENUM(mad_sel, WCD934X_CPE_SS_SVA_CFG, 0, + mad_sel_txt); + +WCD_DAPM_ENUM(mad_inp_mux, WCD934X_CPE_SS_SVA_CFG, 2, + mad_inp_mux_txt); + +WCD_DAPM_ENUM_EXT(rx_int0_dem_inp, WCD934X_CDC_RX0_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + tavil_int_dem_inp_mux_put); +WCD_DAPM_ENUM_EXT(rx_int1_dem_inp, WCD934X_CDC_RX1_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + tavil_int_dem_inp_mux_put); +WCD_DAPM_ENUM_EXT(rx_int2_dem_inp, WCD934X_CDC_RX2_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + tavil_int_dem_inp_mux_put); + +WCD_DAPM_ENUM_EXT(tx_adc_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 4, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); + +WCD_DAPM_ENUM(asrc0, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 0, + asrc0_mux_text); +WCD_DAPM_ENUM(asrc1, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 2, + asrc1_mux_text); +WCD_DAPM_ENUM(asrc2, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 4, + asrc2_mux_text); +WCD_DAPM_ENUM(asrc3, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 6, + asrc3_mux_text); + +WCD_DAPM_ENUM(int1_1_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int2_1_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int3_1_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int4_1_native, SND_SOC_NOPM, 0, native_mux_text); + +WCD_DAPM_ENUM(int1_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int2_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int3_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int4_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int7_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int8_2_native, SND_SOC_NOPM, 0, native_mux_text); + +WCD_DAPM_ENUM(anc0_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0, anc0_fb_mux_text); +WCD_DAPM_ENUM(anc1_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 3, anc1_fb_mux_text); + +static const struct snd_kcontrol_new anc_ear_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_ear_spkr_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_spkr_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_cpe1_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_cpe2_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_brdcst_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux0_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux1_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux2_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux3_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux4_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux5_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux6_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux7_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux8_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +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 int tavil_dsd_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config; + int val; + + val = tavil_dsd_get_current_mixer_value(dsd_conf, mc->shift); + + ucontrol->value.integer.value[0] = ((val < 0) ? 0 : val); + + return 0; +} + +static int tavil_dsd_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + unsigned int wval = ucontrol->value.integer.value[0]; + struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config; + + if (!dsd_conf) + return 0; + + mutex_lock(&tavil_p->codec_mutex); + + tavil_dsd_set_out_select(dsd_conf, mc->shift); + tavil_dsd_set_mixer_value(dsd_conf, mc->shift, wval); + + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mixer_update_power(dapm, kcontrol, wval, NULL); + + return 0; +} + +static const struct snd_kcontrol_new hphl_mixer[] = { + SOC_SINGLE_EXT("DSD HPHL Switch", SND_SOC_NOPM, INTERP_HPHL, 1, 0, + tavil_dsd_mixer_get, tavil_dsd_mixer_put), +}; + +static const struct snd_kcontrol_new hphr_mixer[] = { + SOC_SINGLE_EXT("DSD HPHR Switch", SND_SOC_NOPM, INTERP_HPHR, 1, 0, + tavil_dsd_mixer_get, tavil_dsd_mixer_put), +}; + +static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, tavil_codec_enable_slimrx, + 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, tavil_codec_enable_slimrx, + 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, tavil_codec_enable_slimrx, + 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, tavil_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("SLIM RX0 MUX", WCD934X_RX0, slim_rx0), + WCD_DAPM_MUX("SLIM RX1 MUX", WCD934X_RX1, slim_rx1), + WCD_DAPM_MUX("SLIM RX2 MUX", WCD934X_RX2, slim_rx2), + WCD_DAPM_MUX("SLIM RX3 MUX", WCD934X_RX3, slim_rx3), + WCD_DAPM_MUX("SLIM RX4 MUX", WCD934X_RX4, slim_rx4), + WCD_DAPM_MUX("SLIM RX5 MUX", WCD934X_RX5, slim_rx5), + WCD_DAPM_MUX("SLIM RX6 MUX", WCD934X_RX6, slim_rx6), + WCD_DAPM_MUX("SLIM RX7 MUX", WCD934X_RX7, slim_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), + + WCD_DAPM_MUX("CDC_IF RX0 MUX", WCD934X_RX0, cdc_if_rx0), + WCD_DAPM_MUX("CDC_IF RX1 MUX", WCD934X_RX1, cdc_if_rx1), + WCD_DAPM_MUX("CDC_IF RX2 MUX", WCD934X_RX2, cdc_if_rx2), + WCD_DAPM_MUX("CDC_IF RX3 MUX", WCD934X_RX3, cdc_if_rx3), + WCD_DAPM_MUX("CDC_IF RX4 MUX", WCD934X_RX4, cdc_if_rx4), + WCD_DAPM_MUX("CDC_IF RX5 MUX", WCD934X_RX5, cdc_if_rx5), + WCD_DAPM_MUX("CDC_IF RX6 MUX", WCD934X_RX6, cdc_if_rx6), + WCD_DAPM_MUX("CDC_IF RX7 MUX", WCD934X_RX7, cdc_if_rx7), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_2_mux, tavil_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, tavil_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, tavil_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, tavil_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, tavil_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, tavil_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, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("RX INT0_1 MIX1 INP0", 0, rx_int0_1_mix_inp0), + WCD_DAPM_MUX("RX INT0_1 MIX1 INP1", 0, rx_int0_1_mix_inp1), + WCD_DAPM_MUX("RX INT0_1 MIX1 INP2", 0, rx_int0_1_mix_inp2), + WCD_DAPM_MUX("RX INT1_1 MIX1 INP0", 0, rx_int1_1_mix_inp0), + WCD_DAPM_MUX("RX INT1_1 MIX1 INP1", 0, rx_int1_1_mix_inp1), + WCD_DAPM_MUX("RX INT1_1 MIX1 INP2", 0, rx_int1_1_mix_inp2), + WCD_DAPM_MUX("RX INT2_1 MIX1 INP0", 0, rx_int2_1_mix_inp0), + WCD_DAPM_MUX("RX INT2_1 MIX1 INP1", 0, rx_int2_1_mix_inp1), + WCD_DAPM_MUX("RX INT2_1 MIX1 INP2", 0, rx_int2_1_mix_inp2), + WCD_DAPM_MUX("RX INT3_1 MIX1 INP0", 0, rx_int3_1_mix_inp0), + WCD_DAPM_MUX("RX INT3_1 MIX1 INP1", 0, rx_int3_1_mix_inp1), + WCD_DAPM_MUX("RX INT3_1 MIX1 INP2", 0, rx_int3_1_mix_inp2), + WCD_DAPM_MUX("RX INT4_1 MIX1 INP0", 0, rx_int4_1_mix_inp0), + WCD_DAPM_MUX("RX INT4_1 MIX1 INP1", 0, rx_int4_1_mix_inp1), + WCD_DAPM_MUX("RX INT4_1 MIX1 INP2", 0, rx_int4_1_mix_inp2), + + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp0_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp1_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp2_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp0_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp1_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp2_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + 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, hphl_mixer, + ARRAY_SIZE(hphl_mixer)), + 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, hphr_mixer, + ARRAY_SIZE(hphr_mixer)), + SND_SOC_DAPM_MIXER("RX INT3 MIX2", 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 INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, tavil_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, tavil_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", SND_SOC_NOPM, INTERP_EAR, + 0, &rx_int0_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", SND_SOC_NOPM, INTERP_HPHL, + 0, &rx_int1_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", SND_SOC_NOPM, INTERP_HPHR, + 0, &rx_int2_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3 MIX2 INP", SND_SOC_NOPM, INTERP_LO1, + 0, &rx_int3_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4 MIX2 INP", SND_SOC_NOPM, INTERP_LO2, + 0, &rx_int4_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7 MIX2 INP", SND_SOC_NOPM, INTERP_SPKR1, + 0, &rx_int7_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("CDC_IF TX0 MUX", WCD934X_TX0, cdc_if_tx0), + WCD_DAPM_MUX("CDC_IF TX1 MUX", WCD934X_TX1, cdc_if_tx1), + WCD_DAPM_MUX("CDC_IF TX2 MUX", WCD934X_TX2, cdc_if_tx2), + WCD_DAPM_MUX("CDC_IF TX3 MUX", WCD934X_TX3, cdc_if_tx3), + WCD_DAPM_MUX("CDC_IF TX4 MUX", WCD934X_TX4, cdc_if_tx4), + WCD_DAPM_MUX("CDC_IF TX5 MUX", WCD934X_TX5, cdc_if_tx5), + WCD_DAPM_MUX("CDC_IF TX6 MUX", WCD934X_TX6, cdc_if_tx6), + WCD_DAPM_MUX("CDC_IF TX7 MUX", WCD934X_TX7, cdc_if_tx7), + WCD_DAPM_MUX("CDC_IF TX8 MUX", WCD934X_TX8, cdc_if_tx8), + WCD_DAPM_MUX("CDC_IF TX9 MUX", WCD934X_TX9, cdc_if_tx9), + WCD_DAPM_MUX("CDC_IF TX10 MUX", WCD934X_TX10, cdc_if_tx10), + WCD_DAPM_MUX("CDC_IF TX11 MUX", WCD934X_TX11, cdc_if_tx11), + WCD_DAPM_MUX("CDC_IF TX11 INP1 MUX", WCD934X_TX11, cdc_if_tx11_inp1), + WCD_DAPM_MUX("CDC_IF TX13 MUX", WCD934X_TX13, cdc_if_tx13), + WCD_DAPM_MUX("CDC_IF TX13 INP1 MUX", WCD934X_TX13, cdc_if_tx13_inp1), + + SND_SOC_DAPM_MUX_E("ADC MUX0", WCD934X_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc_mux0_mux, tavil_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, tavil_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, tavil_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, tavil_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, tavil_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, tavil_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, tavil_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, tavil_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, tavil_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 MUX10", SND_SOC_NOPM, 10, 0, &tx_adc_mux10_mux, + tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX11", SND_SOC_NOPM, 11, 0, &tx_adc_mux11_mux, + tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX12", SND_SOC_NOPM, 12, 0, &tx_adc_mux12_mux, + tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX13", SND_SOC_NOPM, 13, 0, &tx_adc_mux13_mux, + tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + WCD_DAPM_MUX("DMIC MUX0", 0, tx_dmic_mux0), + WCD_DAPM_MUX("DMIC MUX1", 0, tx_dmic_mux1), + WCD_DAPM_MUX("DMIC MUX2", 0, tx_dmic_mux2), + WCD_DAPM_MUX("DMIC MUX3", 0, tx_dmic_mux3), + WCD_DAPM_MUX("DMIC MUX4", 0, tx_dmic_mux4), + WCD_DAPM_MUX("DMIC MUX5", 0, tx_dmic_mux5), + WCD_DAPM_MUX("DMIC MUX6", 0, tx_dmic_mux6), + WCD_DAPM_MUX("DMIC MUX7", 0, tx_dmic_mux7), + WCD_DAPM_MUX("DMIC MUX8", 0, tx_dmic_mux8), + WCD_DAPM_MUX("DMIC MUX10", 0, tx_dmic_mux10), + WCD_DAPM_MUX("DMIC MUX11", 0, tx_dmic_mux11), + WCD_DAPM_MUX("DMIC MUX12", 0, tx_dmic_mux12), + WCD_DAPM_MUX("DMIC MUX13", 0, tx_dmic_mux13), + + WCD_DAPM_MUX("AMIC MUX0", 0, tx_amic_mux0), + WCD_DAPM_MUX("AMIC MUX1", 0, tx_amic_mux1), + WCD_DAPM_MUX("AMIC MUX2", 0, tx_amic_mux2), + WCD_DAPM_MUX("AMIC MUX3", 0, tx_amic_mux3), + WCD_DAPM_MUX("AMIC MUX4", 0, tx_amic_mux4), + WCD_DAPM_MUX("AMIC MUX5", 0, tx_amic_mux5), + WCD_DAPM_MUX("AMIC MUX6", 0, tx_amic_mux6), + WCD_DAPM_MUX("AMIC MUX7", 0, tx_amic_mux7), + WCD_DAPM_MUX("AMIC MUX8", 0, tx_amic_mux8), + WCD_DAPM_MUX("AMIC MUX10", 0, tx_amic_mux10), + WCD_DAPM_MUX("AMIC MUX11", 0, tx_amic_mux11), + WCD_DAPM_MUX("AMIC MUX12", 0, tx_amic_mux12), + WCD_DAPM_MUX("AMIC MUX13", 0, tx_amic_mux13), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD934X_ANA_AMIC1, 7, 0, + tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD934X_ANA_AMIC2, 7, 0, + tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD934X_ANA_AMIC3, 7, 0, + tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD934X_ANA_AMIC4, 7, 0, + tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + + WCD_DAPM_MUX("AMIC4_5 SEL", 0, tx_amic4_5), + + WCD_DAPM_MUX("ANC0 FB MUX", 0, anc0_fb), + WCD_DAPM_MUX("ANC1 FB MUX", 0, anc1_fb), + + 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_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS2", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS3", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS4", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* + * Not supply widget, this is used to recover HPH registers. + * It is not connected to any other widgets + */ + SND_SOC_DAPM_SUPPLY("RESET_HPH_REGISTERS", SND_SOC_NOPM, + 0, 0, tavil_codec_reset_hph_registers, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0, + tavil_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_STANDALONE, SND_SOC_NOPM, 0, 0, + tavil_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS3_STANDALONE, SND_SOC_NOPM, 0, 0, + tavil_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS4_STANDALONE, SND_SOC_NOPM, 0, 0, + tavil_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, tavil_codec_enable_slimtx, + 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, tavil_codec_enable_slimtx, + 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, tavil_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, + aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)), + + SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM, + AIF4_VIFEED, 0, tavil_codec_enable_slimvi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0, + aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)), + SND_SOC_DAPM_INPUT("VIINPUT"), + + 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, + tavil_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, + tavil_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, + tavil_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, + tavil_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, + tavil_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, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("IIR0 INP0 MUX", 0, iir0_inp0), + WCD_DAPM_MUX("IIR0 INP1 MUX", 0, iir0_inp1), + WCD_DAPM_MUX("IIR0 INP2 MUX", 0, iir0_inp2), + WCD_DAPM_MUX("IIR0 INP3 MUX", 0, iir0_inp3), + WCD_DAPM_MUX("IIR1 INP0 MUX", 0, iir1_inp0), + WCD_DAPM_MUX("IIR1 INP1 MUX", 0, iir1_inp1), + WCD_DAPM_MUX("IIR1 INP2 MUX", 0, iir1_inp2), + WCD_DAPM_MUX("IIR1 INP3 MUX", 0, iir1_inp3), + + SND_SOC_DAPM_MIXER_E("IIR0", WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL, + 4, 0, NULL, 0, tavil_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER_E("IIR1", WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL, + 4, 0, NULL, 0, tavil_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + 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), + + WCD_DAPM_MUX("RX MIX TX0 MUX", 0, rx_mix_tx0), + WCD_DAPM_MUX("RX MIX TX1 MUX", 0, rx_mix_tx1), + WCD_DAPM_MUX("RX MIX TX2 MUX", 0, rx_mix_tx2), + WCD_DAPM_MUX("RX MIX TX3 MUX", 0, rx_mix_tx3), + WCD_DAPM_MUX("RX MIX TX4 MUX", 0, rx_mix_tx4), + WCD_DAPM_MUX("RX MIX TX5 MUX", 0, rx_mix_tx5), + WCD_DAPM_MUX("RX MIX TX6 MUX", 0, rx_mix_tx6), + WCD_DAPM_MUX("RX MIX TX7 MUX", 0, rx_mix_tx7), + WCD_DAPM_MUX("RX MIX TX8 MUX", 0, rx_mix_tx8), + WCD_DAPM_MUX("RX INT0 DEM MUX", 0, rx_int0_dem_inp), + WCD_DAPM_MUX("RX INT1 DEM MUX", 0, rx_int1_dem_inp), + WCD_DAPM_MUX("RX INT2 DEM MUX", 0, rx_int2_dem_inp), + + SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_1_interp_mux, tavil_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, tavil_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, tavil_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, tavil_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, tavil_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, tavil_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, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("RX INT0_2 INTERP", 0, rx_int0_2_interp), + WCD_DAPM_MUX("RX INT1_2 INTERP", 0, rx_int1_2_interp), + WCD_DAPM_MUX("RX INT2_2 INTERP", 0, rx_int2_2_interp), + WCD_DAPM_MUX("RX INT3_2 INTERP", 0, rx_int3_2_interp), + WCD_DAPM_MUX("RX INT4_2 INTERP", 0, rx_int4_2_interp), + WCD_DAPM_MUX("RX INT7_2 INTERP", 0, rx_int7_2_interp), + WCD_DAPM_MUX("RX INT8_2 INTERP", 0, rx_int8_2_interp), + + SND_SOC_DAPM_SWITCH("ADC US MUX0", WCD934X_CDC_TX0_TX_PATH_192_CTL, 0, + 0, &adc_us_mux0_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX1", WCD934X_CDC_TX1_TX_PATH_192_CTL, 0, + 0, &adc_us_mux1_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX2", WCD934X_CDC_TX2_TX_PATH_192_CTL, 0, + 0, &adc_us_mux2_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX3", WCD934X_CDC_TX3_TX_PATH_192_CTL, 0, + 0, &adc_us_mux3_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX4", WCD934X_CDC_TX4_TX_PATH_192_CTL, 0, + 0, &adc_us_mux4_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX5", WCD934X_CDC_TX5_TX_PATH_192_CTL, 0, + 0, &adc_us_mux5_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX6", WCD934X_CDC_TX6_TX_PATH_192_CTL, 0, + 0, &adc_us_mux6_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX7", WCD934X_CDC_TX7_TX_PATH_192_CTL, 0, + 0, &adc_us_mux7_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX8", WCD934X_CDC_TX8_TX_PATH_192_CTL, 0, + 0, &adc_us_mux8_switch), + + /* MAD related widgets */ + SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"), + SND_SOC_DAPM_INPUT("MADINPUT"), + + WCD_DAPM_MUX("MAD_SEL MUX", 0, mad_sel), + WCD_DAPM_MUX("MAD_INP MUX", 0, mad_inp_mux), + + SND_SOC_DAPM_SWITCH_E("MAD_BROADCAST", SND_SOC_NOPM, 0, 0, + &mad_brdcst_switch, tavil_codec_ape_enable_mad, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SWITCH_E("MAD_CPE1", SND_SOC_NOPM, 0, 0, + &mad_cpe1_switch, tavil_codec_cpe_mad_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH_E("MAD_CPE2", SND_SOC_NOPM, 0, 0, + &mad_cpe2_switch, tavil_codec_cpe_mad_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT1"), + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT2"), + + SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM, + 0, 0, tavil_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, tavil_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, tavil_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, tavil_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, tavil_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, + tavil_codec_enable_ear_pa, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PA", WCD934X_ANA_HPH, 7, 0, NULL, 0, + tavil_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, + tavil_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, + tavil_codec_enable_lineout_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD934X_ANA_LO_1_2, 6, 0, NULL, 0, + tavil_codec_enable_lineout_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0, + tavil_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tavil_codec_enable_spkr_anc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + 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_SWITCH("ANC OUT EAR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_switch), + SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_spkr_switch), + SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0, + &anc_spkr_pa_switch), + + SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_rx_bias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT1 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHL, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT2 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHR, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT3 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO1, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT4 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO2, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT7 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_SPKR1, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT8 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_SPKR2, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + WCD_DAPM_MUX("RX INT1_1 NATIVE MUX", 0, int1_1_native), + WCD_DAPM_MUX("RX INT2_1 NATIVE MUX", 0, int2_1_native), + WCD_DAPM_MUX("RX INT3_1 NATIVE MUX", 0, int3_1_native), + WCD_DAPM_MUX("RX INT4_1 NATIVE MUX", 0, int4_1_native), + + WCD_DAPM_MUX("RX INT1_2 NATIVE MUX", 0, int1_2_native), + WCD_DAPM_MUX("RX INT2_2 NATIVE MUX", 0, int2_2_native), + WCD_DAPM_MUX("RX INT3_2 NATIVE MUX", 0, int3_2_native), + WCD_DAPM_MUX("RX INT4_2 NATIVE MUX", 0, int4_2_native), + WCD_DAPM_MUX("RX INT7_2 NATIVE MUX", 0, int7_2_native), + WCD_DAPM_MUX("RX INT8_2 NATIVE MUX", 0, int8_2_native), + + SND_SOC_DAPM_MUX_E("ASRC0 MUX", SND_SOC_NOPM, ASRC0, 0, + &asrc0_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC1 MUX", SND_SOC_NOPM, ASRC1, 0, + &asrc1_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC2 MUX", SND_SOC_NOPM, ASRC2, 0, + &asrc2_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC3 MUX", SND_SOC_NOPM, ASRC3, 0, + &asrc3_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static int tavil_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 tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); + u32 i = 0; + struct wcd9xxx_ch *ch; + int ret = 0; + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + case AIF4_PB: + if (!rx_slot || !rx_num) { + dev_err(tavil->dev, "%s: Invalid rx_slot 0x%pK or rx_num 0x%pK\n", + __func__, rx_slot, rx_num); + ret = -EINVAL; + break; + } + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, + list) { + dev_dbg(tavil->dev, "%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + rx_slot[i++] = ch->ch_num; + } + *rx_num = i; + dev_dbg(tavil->dev, "%s: dai_name = %s dai_id = %x rx_num = %d\n", + __func__, dai->name, dai->id, i); + if (*rx_num == 0) { + dev_err(tavil->dev, "%s: Channel list empty for dai_name = %s dai_id = %x\n", + __func__, dai->name, dai->id); + ret = -EINVAL; + } + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + case AIF4_MAD_TX: + case AIF4_VIFEED: + if (!tx_slot || !tx_num) { + dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK or tx_num 0x%pK\n", + __func__, tx_slot, tx_num); + ret = -EINVAL; + break; + } + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, + list) { + dev_dbg(tavil->dev, "%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + tx_slot[i++] = ch->ch_num; + } + *tx_num = i; + dev_dbg(tavil->dev, "%s: dai_name = %s dai_id = %x tx_num = %d\n", + __func__, dai->name, dai->id, i); + if (*tx_num == 0) { + dev_err(tavil->dev, "%s: Channel list empty for dai_name = %s dai_id = %x\n", + __func__, dai->name, dai->id); + ret = -EINVAL; + } + break; + default: + dev_err(tavil->dev, "%s: Invalid DAI ID %x\n", + __func__, dai->id); + ret = -EINVAL; + break; + } + + return ret; +} + +static int tavil_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 tavil_priv *tavil; + struct wcd9xxx *core; + struct wcd9xxx_codec_dai_data *dai_data = NULL; + + tavil = snd_soc_codec_get_drvdata(dai->codec); + core = dev_get_drvdata(dai->codec->dev->parent); + + if (!tx_slot || !rx_slot) { + dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK, rx_slot 0x%pK\n", + __func__, tx_slot, rx_slot); + return -EINVAL; + } + dev_dbg(tavil->dev, "%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n", + __func__, dai->name, dai->id, tx_num, rx_num); + + wcd9xxx_init_slimslave(core, core->slim->laddr, + tx_num, tx_slot, rx_num, rx_slot); + /* Reserve TX13 for MAD data channel */ + dai_data = &tavil->dai[AIF4_MAD_TX]; + if (dai_data) + list_add_tail(&core->tx_chs[WCD934X_TX13].list, + &dai_data->wcd9xxx_ch_list); + + return 0; +} + +static int tavil_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + return 0; +} + +static void tavil_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); +} + +static int tavil_set_decimator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u32 tx_port = 0, tx_fs_rate = 0; + u8 shift = 0, shift_val = 0, tx_mux_sel = 0; + int decimator = -1; + u16 tx_port_reg = 0, tx_fs_reg = 0; + + switch (sample_rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + default: + dev_err(tavil->dev, "%s: Invalid TX sample rate: %d\n", + __func__, sample_rate); + return -EINVAL; + + }; + + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) { + tx_port = ch->port; + dev_dbg(codec->dev, "%s: dai->id = %d, tx_port = %d", + __func__, dai->id, tx_port); + + if ((tx_port < 0) || (tx_port == 12) || (tx_port >= 14)) { + dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n", + __func__, tx_port, dai->id); + return -EINVAL; + } + /* Find the SB TX MUX input - which decimator is connected */ + if (tx_port < 4) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0; + shift = (tx_port << 1); + shift_val = 0x03; + } else if ((tx_port >= 4) && (tx_port < 8)) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1; + shift = ((tx_port - 4) << 1); + shift_val = 0x03; + } else if ((tx_port >= 8) && (tx_port < 11)) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2; + shift = ((tx_port - 8) << 1); + shift_val = 0x03; + } else if (tx_port == 11) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 0; + shift_val = 0x0F; + } else if (tx_port == 13) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 4; + shift_val = 0x03; + } + tx_mux_sel = snd_soc_read(codec, tx_port_reg) & + (shift_val << shift); + tx_mux_sel = tx_mux_sel >> shift; + + if (tx_port <= 8) { + if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3)) + decimator = tx_port; + } else if (tx_port <= 10) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = ((tx_port == 9) ? 7 : 6); + } else if (tx_port == 11) { + if ((tx_mux_sel >= 1) && (tx_mux_sel < 7)) + decimator = tx_mux_sel - 1; + } else if (tx_port == 13) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = 5; + } + + if (decimator >= 0) { + tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + + 16 * decimator; + dev_dbg(codec->dev, "%s: set DEC%u (-> SLIM_TX%u) rate to %u\n", + __func__, decimator, tx_port, sample_rate); + snd_soc_update_bits(codec, tx_fs_reg, 0x0F, tx_fs_rate); + } else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) { + /* Check if the TX Mux input is RX MIX TXn */ + dev_dbg(codec->dev, "%s: RX_MIX_TX%u going to CDC_IF TX%u\n", + __func__, tx_port, tx_port); + } else { + dev_err(codec->dev, "%s: ERROR: Invalid decimator: %d\n", + __func__, decimator); + return -EINVAL; + } + } + return 0; +} + +static int tavil_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp; + u32 j; + u16 int_mux_cfg1, int_fs_reg; + u8 int_mux_cfg1_val; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) { + int_2_inp = INTn_2_INP_SEL_RX0 + ch->port - + WCD934X_RX_PORT_START_NUMBER; + if ((int_2_inp < INTn_2_INP_SEL_RX0) || + (int_2_inp > INTn_2_INP_SEL_RX7)) { + dev_err(codec->dev, "%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - WCD934X_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + int_mux_cfg1 = WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1; + 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) { + int_mux_cfg1 += 2; + continue; + } + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) & + 0x0F; + if (int_mux_cfg1_val == int_2_inp) { + /* + * Ear mix path supports only 48, 96, 192, + * 384KHz only + */ + if ((j == INTERP_EAR) && + (rate_reg_val < 0x4 || + rate_reg_val > 0x7)) { + dev_err_ratelimited(codec->dev, + "%s: Invalid rate for AIF_PB DAI(%d)\n", + __func__, dai->id); + return -EINVAL; + } + + int_fs_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL + + 20 * j; + dev_dbg(codec->dev, "%s: AIF_PB DAI(%d) connected to INT%u_2\n", + __func__, dai->id, j); + dev_dbg(codec->dev, "%s: set INT%u_2 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, 0x0F, + rate_reg_val); + } + int_mux_cfg1 += 2; + } + } + return 0; +} + +static int tavil_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp; + u32 j; + u16 int_mux_cfg0, int_mux_cfg1; + u16 int_fs_reg; + u8 int_mux_cfg0_val, int_mux_cfg1_val; + u8 inp0_sel, inp1_sel, inp2_sel; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) { + int_1_mix1_inp = INTn_1_INP_SEL_RX0 + ch->port - + WCD934X_RX_PORT_START_NUMBER; + if ((int_1_mix1_inp < INTn_1_INP_SEL_RX0) || + (int_1_mix1_inp > INTn_1_INP_SEL_RX7)) { + dev_err(codec->dev, "%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - WCD934X_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + int_mux_cfg0 = WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0; + + /* + * 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) { + int_mux_cfg0 += 2; + continue; + } + int_mux_cfg1 = int_mux_cfg0 + 1; + + int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1); + inp0_sel = int_mux_cfg0_val & 0x0F; + inp1_sel = (int_mux_cfg0_val >> 4) & 0x0F; + inp2_sel = (int_mux_cfg1_val >> 4) & 0x0F; + if ((inp0_sel == int_1_mix1_inp) || + (inp1_sel == int_1_mix1_inp) || + (inp2_sel == int_1_mix1_inp)) { + /* + * Ear and speaker primary path does not support + * native sample rates + */ + if ((j == INTERP_EAR || j == INTERP_SPKR1 || + j == INTERP_SPKR2) && + (rate_reg_val > 0x7)) { + dev_err_ratelimited(codec->dev, + "%s: Invalid rate for AIF_PB DAI(%d)\n", + __func__, dai->id); + return -EINVAL; + } + + int_fs_reg = WCD934X_CDC_RX0_RX_PATH_CTL + + 20 * j; + dev_dbg(codec->dev, + "%s: AIF_PB DAI(%d) connected to INT%u_1\n", + __func__, dai->id, j); + dev_dbg(codec->dev, + "%s: set INT%u_1 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, 0x0F, + rate_reg_val); + } + int_mux_cfg0 += 2; + } + if (dsd_conf) + tavil_dsd_set_interp_rate(dsd_conf, ch->port, + sample_rate, rate_reg_val); + } + + return 0; +} + + +static int tavil_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + 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(codec->dev, "%s: Unsupported sample rate: %d\n", + __func__, sample_rate); + return -EINVAL; + } + + ret = tavil_set_prim_interpolator_rate(dai, (u8)rate_val, sample_rate); + if (ret) + return ret; + ret = tavil_set_mix_interpolator_rate(dai, (u8)rate_val, sample_rate); + if (ret) + return ret; + + return ret; +} + +static int tavil_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + return 0; +} + +static int tavil_vi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); + + dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params)); + + tavil->dai[dai->id].rate = params_rate(params); + tavil->dai[dai->id].bit_width = 32; + + return 0; +} + +static int tavil_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); + int ret = 0; + + dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = tavil_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(tavil->dev, "%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16: + tavil->dai[dai->id].bit_width = 16; + break; + case 24: + tavil->dai[dai->id].bit_width = 24; + break; + case 32: + tavil->dai[dai->id].bit_width = 32; + break; + default: + return -EINVAL; + } + tavil->dai[dai->id].rate = params_rate(params); + break; + case SNDRV_PCM_STREAM_CAPTURE: + if (dai->id != AIF4_MAD_TX) + ret = tavil_set_decimator_rate(dai, + params_rate(params)); + if (ret) { + dev_err(tavil->dev, "%s: cannot set TX Decimator rate: %d\n", + __func__, ret); + return ret; + } + switch (params_width(params)) { + case 16: + tavil->dai[dai->id].bit_width = 16; + break; + case 24: + tavil->dai[dai->id].bit_width = 24; + break; + default: + dev_err(tavil->dev, "%s: Invalid format 0x%x\n", + __func__, params_width(params)); + return -EINVAL; + }; + tavil->dai[dai->id].rate = params_rate(params); + break; + default: + dev_err(tavil->dev, "%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + }; + + return 0; +} + +static struct snd_soc_dai_ops tavil_dai_ops = { + .startup = tavil_startup, + .shutdown = tavil_shutdown, + .hw_params = tavil_hw_params, + .prepare = tavil_prepare, + .set_channel_map = tavil_set_channel_map, + .get_channel_map = tavil_get_channel_map, +}; + +static struct snd_soc_dai_ops tavil_vi_dai_ops = { + .hw_params = tavil_vi_hw_params, + .set_channel_map = tavil_set_channel_map, + .get_channel_map = tavil_get_channel_map, +}; + +static struct snd_soc_dai_driver tavil_dai[] = { + { + .name = "tavil_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_rx4", + .id = AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_vifeedback", + .id = AIF4_VIFEED, + .capture = { + .stream_name = "VIfeed", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tavil_vi_dai_ops, + }, + { + .name = "tavil_mad1", + .id = AIF4_MAD_TX, + .capture = { + .stream_name = "AIF4 MAD TX", + .rates = SNDRV_PCM_RATE_16000, + .formats = WCD934X_FORMATS_S16_LE, + .rate_min = 16000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &tavil_dai_ops, + }, +}; + +static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil) +{ + struct snd_soc_codec *codec = tavil->codec; + + if (!codec) + return; + + mutex_lock(&tavil->power_lock); + dev_dbg(codec->dev, "%s: Entering power gating function, %d\n", + __func__, tavil->power_active_ref); + + if (tavil->power_active_ref > 0) + goto exit; + + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_BEGIN, + WCD9XXX_DIG_CORE_REGION_1); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x02, 0x00); + wcd9xxx_set_power_state(tavil->wcd9xxx, WCD_REGION_POWER_DOWN, + WCD9XXX_DIG_CORE_REGION_1); +exit: + dev_dbg(codec->dev, "%s: Exiting power gating function, %d\n", + __func__, tavil->power_active_ref); + mutex_unlock(&tavil->power_lock); +} + +static void tavil_codec_power_gate_work(struct work_struct *work) +{ + struct tavil_priv *tavil; + struct delayed_work *dwork; + struct snd_soc_codec *codec; + + dwork = to_delayed_work(work); + tavil = container_of(dwork, struct tavil_priv, power_gate_work); + codec = tavil->codec; + + if (!codec) + return; + + tavil_codec_power_gate_digital_core(tavil); +} + +/* called under power_lock acquisition */ +static int tavil_dig_core_remove_power_collapse(struct snd_soc_codec *codec) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + snd_soc_write(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5); + snd_soc_write(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7); + snd_soc_write(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_RST_CTL, 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_RST_CTL, 0x02, 0x02); + + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + regcache_mark_dirty(codec->component.regmap); + regcache_sync_region(codec->component.regmap, + WCD934X_DIG_CORE_REG_MIN, + WCD934X_DIG_CORE_REG_MAX); + + return 0; +} + +static int tavil_dig_core_power_collapse(struct tavil_priv *tavil, + int req_state) +{ + struct snd_soc_codec *codec; + int cur_state; + + /* Exit if feature is disabled */ + if (!dig_core_collapse_enable) + return 0; + + mutex_lock(&tavil->power_lock); + if (req_state == POWER_COLLAPSE) + tavil->power_active_ref--; + else if (req_state == POWER_RESUME) + tavil->power_active_ref++; + else + goto unlock_mutex; + + if (tavil->power_active_ref < 0) { + dev_dbg(tavil->dev, "%s: power_active_ref is negative\n", + __func__); + goto unlock_mutex; + } + + codec = tavil->codec; + if (!codec) + goto unlock_mutex; + + if (req_state == POWER_COLLAPSE) { + if (tavil->power_active_ref == 0) { + schedule_delayed_work(&tavil->power_gate_work, + msecs_to_jiffies(dig_core_collapse_timer * 1000)); + } + } else if (req_state == POWER_RESUME) { + if (tavil->power_active_ref == 1) { + /* + * At this point, there can be two cases: + * 1. Core already in power collapse state + * 2. Timer kicked in and still did not expire or + * waiting for the power_lock + */ + cur_state = wcd9xxx_get_current_power_state( + tavil->wcd9xxx, + WCD9XXX_DIG_CORE_REGION_1); + if (cur_state == WCD_REGION_POWER_DOWN) { + tavil_dig_core_remove_power_collapse(codec); + } else { + mutex_unlock(&tavil->power_lock); + cancel_delayed_work_sync( + &tavil->power_gate_work); + mutex_lock(&tavil->power_lock); + } + } + } + +unlock_mutex: + mutex_unlock(&tavil->power_lock); + + return 0; +} + +static int tavil_cdc_req_mclk_enable(struct tavil_priv *tavil, + bool enable) +{ + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(tavil->wcd_ext_clk); + if (ret) { + dev_err(tavil->dev, "%s: ext clk enable failed\n", + __func__); + goto done; + } + /* get BG */ + wcd_resmgr_enable_master_bias(tavil->resmgr); + /* get MCLK */ + wcd_resmgr_enable_clk_block(tavil->resmgr, WCD_CLK_MCLK); + } else { + /* put MCLK */ + wcd_resmgr_disable_clk_block(tavil->resmgr, WCD_CLK_MCLK); + /* put BG */ + wcd_resmgr_disable_master_bias(tavil->resmgr); + clk_disable_unprepare(tavil->wcd_ext_clk); + } + +done: + return ret; +} + +static int __tavil_cdc_mclk_enable_locked(struct tavil_priv *tavil, + bool enable) +{ + int ret = 0; + + if (!tavil->wcd_ext_clk) { + dev_err(tavil->dev, "%s: wcd ext clock is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(tavil->dev, "%s: mclk_enable = %u\n", __func__, enable); + + if (enable) { + tavil_dig_core_power_collapse(tavil, POWER_RESUME); + tavil_vote_svs(tavil, true); + ret = tavil_cdc_req_mclk_enable(tavil, true); + if (ret) + goto done; + } else { + tavil_cdc_req_mclk_enable(tavil, false); + tavil_vote_svs(tavil, false); + tavil_dig_core_power_collapse(tavil, POWER_COLLAPSE); + } + +done: + return ret; +} + +static int __tavil_cdc_mclk_enable(struct tavil_priv *tavil, + bool enable) +{ + int ret; + + WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr); + ret = __tavil_cdc_mclk_enable_locked(tavil, enable); + WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr); + + return ret; +} + +static ssize_t tavil_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + char buffer[TAVIL_VERSION_ENTRY_SIZE]; + int len = 0; + + tavil = (struct tavil_priv *) entry->private_data; + if (!tavil) { + pr_err("%s: tavil priv is null\n", __func__); + return -EINVAL; + } + + wcd9xxx = tavil->wcd9xxx; + + switch (wcd9xxx->version) { + case TAVIL_VERSION_WCD9340_1_0: + len = snprintf(buffer, sizeof(buffer), "WCD9340_1_0\n"); + break; + case TAVIL_VERSION_WCD9341_1_0: + len = snprintf(buffer, sizeof(buffer), "WCD9341_1_0\n"); + break; + case TAVIL_VERSION_WCD9340_1_1: + len = snprintf(buffer, sizeof(buffer), "WCD9340_1_1\n"); + break; + case TAVIL_VERSION_WCD9341_1_1: + len = snprintf(buffer, sizeof(buffer), "WCD9341_1_1\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops tavil_codec_info_ops = { + .read = tavil_codec_version_read, +}; + +/* + * tavil_codec_info_create_codec_entry - creates wcd934x module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates wcd934x module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int tavil_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct tavil_priv *tavil; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + tavil = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + tavil->entry = snd_register_module_info(codec_root->module, + "tavil", + codec_root); + if (!tavil->entry) { + dev_dbg(codec->dev, "%s: failed to create wcd934x entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + tavil->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create wcd934x version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = tavil; + version_entry->size = TAVIL_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &tavil_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + tavil->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(tavil_codec_info_create_codec_entry); + +/** + * tavil_cdc_mclk_enable - Enable/disable codec mclk + * + * @codec: codec instance + * @enable: Indicates clk enable or disable + * + * Returns 0 on Success and error on failure + */ +int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + return __tavil_cdc_mclk_enable(tavil, enable); +} +EXPORT_SYMBOL(tavil_cdc_mclk_enable); + +static int __tavil_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (enable) { + if (wcd_resmgr_get_clk_type(tavil->resmgr) == + WCD_CLK_RCO) { + ret = wcd_resmgr_enable_clk_block(tavil->resmgr, + WCD_CLK_RCO); + } else { + ret = tavil_cdc_req_mclk_enable(tavil, true); + if (ret) { + dev_err(codec->dev, + "%s: mclk_enable failed, err = %d\n", + __func__, ret); + goto done; + } + ret = wcd_resmgr_enable_clk_block(tavil->resmgr, + WCD_CLK_RCO); + ret |= tavil_cdc_req_mclk_enable(tavil, false); + } + + } else { + ret = wcd_resmgr_disable_clk_block(tavil->resmgr, + WCD_CLK_RCO); + } + + if (ret) { + dev_err(codec->dev, "%s: Error in %s RCO\n", + __func__, (enable ? "enabling" : "disabling")); + ret = -EINVAL; + } + +done: + return ret; +} + +/* + * tavil_codec_internal_rco_ctrl: Enable/Disable codec's RCO clock + * @codec: Handle to the codec + * @enable: Indicates whether clock should be enabled or disabled + */ +static int tavil_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr); + ret = __tavil_codec_internal_rco_ctrl(codec, enable); + WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr); + return ret; +} + +static const struct wcd_resmgr_cb tavil_resmgr_cb = { + .cdc_rco_ctrl = __tavil_codec_internal_rco_ctrl, +}; + +static const struct tavil_reg_mask_val tavil_codec_mclk2_1_1_defaults[] = { + {WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20}, +}; + +static const struct tavil_reg_mask_val tavil_codec_mclk2_1_0_defaults[] = { + /* + * PLL Settings: + * Clock Root: MCLK2, + * Clock Source: EXT_CLK, + * Clock Destination: MCLK2 + * Clock Freq In: 19.2MHz, + * Clock Freq Out: 11.2896MHz + */ + {WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20}, + {WCD934X_CLK_SYS_INT_POST_DIV_REG0, 0xFF, 0x5E}, + {WCD934X_CLK_SYS_INT_POST_DIV_REG1, 0x1F, 0x1F}, + {WCD934X_CLK_SYS_INT_REF_DIV_REG0, 0xFF, 0x54}, + {WCD934X_CLK_SYS_INT_REF_DIV_REG1, 0xFF, 0x01}, + {WCD934X_CLK_SYS_INT_FILTER_REG1, 0x07, 0x04}, + {WCD934X_CLK_SYS_INT_PLL_L_VAL, 0xFF, 0x93}, + {WCD934X_CLK_SYS_INT_PLL_N_VAL, 0xFF, 0xFA}, + {WCD934X_CLK_SYS_INT_TEST_REG0, 0xFF, 0x90}, + {WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG, 0xFF, 0x7E}, + {WCD934X_CLK_SYS_INT_VCO_PROG, 0xFF, 0xF8}, + {WCD934X_CLK_SYS_INT_TEST_REG1, 0xFF, 0x68}, + {WCD934X_CLK_SYS_INT_LDO_LOCK_CFG, 0xFF, 0x40}, + {WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG, 0xFF, 0x32}, +}; + +static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = { + {WCD934X_BIAS_VBG_FINE_ADJ, 0xFF, 0x75}, + {WCD934X_CODEC_CPR_SVS_CX_VDD, 0xFF, 0x7C}, /* value in svs mode */ + {WCD934X_CODEC_CPR_SVS2_CX_VDD, 0xFF, 0x58}, /* value in svs2 mode */ + {WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_COMPANDER8_CTL7, 0x1E, 0x18}, + {WCD934X_CDC_COMPANDER7_CTL7, 0x1E, 0x18}, + {WCD934X_CDC_RX0_RX_PATH_SEC0, 0x08, 0x0}, + {WCD934X_CDC_CLSH_DECAY_CTRL, 0x03, 0x0}, + {WCD934X_MICB1_TEST_CTL_2, 0x07, 0x01}, + {WCD934X_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12}, + {WCD934X_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08}, + {WCD934X_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12}, + {WCD934X_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x1F, 0x09}, + {WCD934X_CDC_TX0_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX1_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX2_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX3_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX4_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX5_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX6_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX7_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX8_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_RX_OCP_CTL, 0x0F, 0x02}, /* OCP number of attempts is 2 */ + {WCD934X_HPH_OCP_CTL, 0xFF, 0x3A}, /* OCP current limit */ + {WCD934X_HPH_L_TEST, 0x01, 0x01}, + {WCD934X_HPH_R_TEST, 0x01, 0x01}, + {WCD934X_CPE_FLL_CONFIG_CTL_2, 0xFF, 0x20}, +}; + +static const struct tavil_reg_mask_val tavil_codec_reg_init_1_1_val[] = { + {WCD934X_CDC_COMPANDER1_CTL7, 0x1E, 0x06}, + {WCD934X_CDC_COMPANDER2_CTL7, 0x1E, 0x06}, + {WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0xFF, 0x84}, + {WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0xFF, 0x84}, + {WCD934X_CDC_RX3_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD934X_CDC_RX4_RX_PATH_SEC0, 0xFC, 0xF4}, +}; + +static const struct tavil_cpr_reg_defaults cpr_defaults[] = { + { 0x00000820, 0x00000094 }, + { 0x00000fC0, 0x00000048 }, + { 0x0000f000, 0x00000044 }, + { 0x0000bb80, 0xC0000178 }, + { 0x00000000, 0x00000160 }, + { 0x10854522, 0x00000060 }, + { 0x10854509, 0x00000064 }, + { 0x108544dd, 0x00000068 }, + { 0x108544ad, 0x0000006C }, + { 0x0000077E, 0x00000070 }, + { 0x000007da, 0x00000074 }, + { 0x00000000, 0x00000078 }, + { 0x00000000, 0x0000007C }, + { 0x00042029, 0x00000080 }, + { 0x4002002A, 0x00000090 }, + { 0x4002002B, 0x00000090 }, +}; + +static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = { + {WCD934X_CDC_CLSH_K2_MSB, 0x0F, 0x00}, + {WCD934X_CDC_CLSH_K2_LSB, 0xFF, 0x60}, + {WCD934X_CPE_SS_DMIC_CFG, 0x80, 0x00}, + {WCD934X_CDC_BOOST0_BOOST_CTL, 0x70, 0x50}, + {WCD934X_CDC_BOOST1_BOOST_CTL, 0x70, 0x50}, + {WCD934X_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08}, + {WCD934X_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08}, + {WCD934X_CDC_TOP_TOP_CFG1, 0x02, 0x02}, + {WCD934X_CDC_TOP_TOP_CFG1, 0x01, 0x01}, + {WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0x01, 0x01}, + {WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x01, 0x01}, + {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD934X_CODEC_RPM_CLK_GATE, 0x08, 0x00}, + {WCD934X_TLMM_DMIC3_CLK_PINCFG, 0xFF, 0x0a}, + {WCD934X_TLMM_DMIC3_DATA_PINCFG, 0xFF, 0x0a}, + {WCD934X_CPE_SS_SVA_CFG, 0x60, 0x00}, +}; + +static void tavil_codec_init_reg(struct tavil_priv *priv) +{ + struct snd_soc_codec *codec = priv->codec; + u32 i; + + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_common_val); i++) + snd_soc_update_bits(codec, + tavil_codec_reg_init_common_val[i].reg, + tavil_codec_reg_init_common_val[i].mask, + tavil_codec_reg_init_common_val[i].val); + + if (TAVIL_IS_1_1(priv->wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_1_1_val); i++) + snd_soc_update_bits(codec, + tavil_codec_reg_init_1_1_val[i].reg, + tavil_codec_reg_init_1_1_val[i].mask, + tavil_codec_reg_init_1_1_val[i].val); + } +} + +static void tavil_update_reg_defaults(struct tavil_priv *tavil) +{ + u32 i; + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tavil->wcd9xxx; + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_defaults); i++) + regmap_update_bits(wcd9xxx->regmap, + tavil_codec_reg_defaults[i].reg, + tavil_codec_reg_defaults[i].mask, + tavil_codec_reg_defaults[i].val); +} + +static void tavil_update_cpr_defaults(struct tavil_priv *tavil) +{ + int i; + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tavil->wcd9xxx; + if (!TAVIL_IS_1_1(wcd9xxx)) + return; + + __tavil_cdc_mclk_enable(tavil, true); + + regmap_write(wcd9xxx->regmap, WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD, 0x2C); + regmap_update_bits(wcd9xxx->regmap, WCD934X_CODEC_RPM_CLK_GATE, + 0x10, 0x00); + + for (i = 0; i < ARRAY_SIZE(cpr_defaults); i++) { + regmap_bulk_write(wcd9xxx->regmap, + WCD934X_CODEC_CPR_WR_DATA_0, + (u8 *)&cpr_defaults[i].wr_data, 4); + regmap_bulk_write(wcd9xxx->regmap, + WCD934X_CODEC_CPR_WR_ADDR_0, + (u8 *)&cpr_defaults[i].wr_addr, 4); + } + + __tavil_cdc_mclk_enable(tavil, false); +} + +static void tavil_slim_interface_init_reg(struct snd_soc_codec *codec) +{ + int i; + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + + for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) + wcd9xxx_interface_reg_write(priv->wcd9xxx, + WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + i, + 0xFF); +} + +static irqreturn_t tavil_misc_irq(int irq, void *data) +{ + struct tavil_priv *tavil = data; + int misc_val; + + /* Find source of interrupt */ + regmap_read(tavil->wcd9xxx->regmap, WCD934X_INTR_CODEC_MISC_STATUS, + &misc_val); + + if (misc_val & 0x08) { + dev_info(tavil->dev, "%s: irq: %d, DSD DC detected!\n", + __func__, irq); + /* DSD DC interrupt, reset DSD path */ + tavil_dsd_reset(tavil->dsd_config); + } else { + dev_err(tavil->dev, "%s: Codec misc irq: %d, val: 0x%x\n", + __func__, irq, misc_val); + } + + /* Clear interrupt status */ + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_INTR_CODEC_MISC_CLEAR, misc_val, 0x00); + + return IRQ_HANDLED; +} + +static irqreturn_t tavil_slimbus_irq(int irq, void *data) +{ + struct tavil_priv *tavil = data; + unsigned long status = 0; + int i, j, port_id, k; + u32 bit; + u8 val, int_val = 0; + bool tx, cleared; + 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++) { + val = wcd9xxx_interface_reg_read(tavil->wcd9xxx, i); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = (j >= 16 ? true : false); + port_id = (tx ? j - 16 : j); + val = wcd9xxx_interface_reg_read(tavil->wcd9xxx, + WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0 + j); + if (val) { + if (!tx) + reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + + (port_id / 8); + else + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + tavil->wcd9xxx, reg); + /* + * Ignore interrupts for ports for which the + * interrupts are not specifically enabled. + */ + if (!(int_val & (1 << (port_id % 8)))) + continue; + } + if (val & WCD934X_SLIM_IRQ_OVERFLOW) + dev_err_ratelimited(tavil->dev, "%s: overflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if (val & WCD934X_SLIM_IRQ_UNDERFLOW) + dev_err_ratelimited(tavil->dev, "%s: underflow error on %s port %d, value %x\n", + __func__, (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_RX_EN0 + + (port_id / 8); + else + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + tavil->wcd9xxx, reg); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + wcd9xxx_interface_reg_write(tavil->wcd9xxx, + reg, int_val); + } + } + if (val & WCD934X_SLIM_IRQ_PORT_CLOSED) { + /* + * INT SOURCE register starts from RX to TX + * but port number in the ch_mask is in opposite way + */ + bit = (tx ? j - 16 : j + 16); + dev_dbg(tavil->dev, "%s: %s port %d closed value %x, bit %u\n", + __func__, (tx ? "TX" : "RX"), port_id, val, + bit); + for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) { + dev_dbg(tavil->dev, "%s: tavil->dai[%d].ch_mask = 0x%lx\n", + __func__, k, tavil->dai[k].ch_mask); + if (test_and_clear_bit(bit, + &tavil->dai[k].ch_mask)) { + cleared = true; + if (!tavil->dai[k].ch_mask) + wake_up( + &tavil->dai[k].dai_wait); + /* + * There are cases when multiple DAIs + * might be using the same slimbus + * channel. Hence don't break here. + */ + } + } + WARN(!cleared, + "Couldn't find slimbus %s port %d for closing\n", + (tx ? "TX" : "RX"), port_id); + } + wcd9xxx_interface_reg_write(tavil->wcd9xxx, + WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 + + (j / 8), + 1 << (j % 8)); + } + + return IRQ_HANDLED; +} + +static int tavil_setup_irqs(struct tavil_priv *tavil) +{ + int ret = 0; + struct snd_soc_codec *codec = tavil->codec; + struct wcd9xxx *wcd9xxx = tavil->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS, + tavil_slimbus_irq, "SLIMBUS Slave", tavil); + if (ret) + dev_err(codec->dev, "%s: Failed to request irq %d\n", __func__, + WCD9XXX_IRQ_SLIMBUS); + else + tavil_slim_interface_init_reg(codec); + + /* Register for misc interrupts as well */ + ret = wcd9xxx_request_irq(core_res, WCD934X_IRQ_MISC, + tavil_misc_irq, "CDC MISC Irq", tavil); + if (ret) + dev_err(codec->dev, "%s: Failed to request cdc misc irq\n", + __func__); + + return ret; +} + +static void tavil_init_slim_slave_cfg(struct snd_soc_codec *codec) +{ + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + struct afe_param_cdc_slimbus_slave_cfg *cfg; + struct wcd9xxx *wcd9xxx = priv->wcd9xxx; + uint64_t eaddr = 0; + + cfg = &priv->slimbus_slave_cfg; + cfg->minor_version = 1; + cfg->tx_slave_port_offset = 0; + cfg->rx_slave_port_offset = 16; + + memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr)); + WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6); + cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF; + cfg->device_enum_addr_msw = eaddr >> 32; + + dev_dbg(codec->dev, "%s: slimbus logical address 0x%llx\n", + __func__, eaddr); +} + +static void tavil_cleanup_irqs(struct tavil_priv *tavil) +{ + struct wcd9xxx *wcd9xxx = tavil->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tavil); + wcd9xxx_free_irq(core_res, WCD934X_IRQ_MISC, tavil); +} + +/* + * wcd934x_get_micb_vout_ctl_val: converts micbias from volts to register value + * @micb_mv: micbias in mv + * + * return register value converted + */ +int wcd934x_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} +EXPORT_SYMBOL(wcd934x_get_micb_vout_ctl_val); + +static int tavil_handle_pdata(struct tavil_priv *tavil, + struct wcd9xxx_pdata *pdata) +{ + struct snd_soc_codec *codec = tavil->codec; + u8 mad_dmic_ctl_val; + u8 anc_ctl_value; + u32 def_dmic_rate, dmic_clk_drv; + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + int rc = 0; + + if (!pdata) { + dev_err(codec->dev, "%s: NULL pdata\n", __func__); + return -ENODEV; + } + + /* set micbias voltage */ + vout_ctl_1 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb1_mv); + vout_ctl_2 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb2_mv); + vout_ctl_3 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb3_mv); + vout_ctl_4 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb4_mv); + + if (IS_ERR_VALUE(vout_ctl_1) || IS_ERR_VALUE(vout_ctl_2) || + IS_ERR_VALUE(vout_ctl_3) || IS_ERR_VALUE(vout_ctl_4)) { + rc = -EINVAL; + goto done; + } + snd_soc_update_bits(codec, WCD934X_ANA_MICB1, 0x3F, vout_ctl_1); + snd_soc_update_bits(codec, WCD934X_ANA_MICB2, 0x3F, vout_ctl_2); + snd_soc_update_bits(codec, WCD934X_ANA_MICB3, 0x3F, vout_ctl_3); + snd_soc_update_bits(codec, WCD934X_ANA_MICB4, 0x3F, vout_ctl_4); + + /* Set the DMIC sample rate */ + switch (pdata->mclk_rate) { + case WCD934X_MCLK_CLK_9P6MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + break; + case WCD934X_MCLK_CLK_12P288MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ; + break; + default: + /* should never happen */ + dev_err(codec->dev, "%s: Invalid mclk_rate %d\n", + __func__, pdata->mclk_rate); + rc = -EINVAL; + goto done; + }; + + if (pdata->dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + pdata->dmic_sample_rate = def_dmic_rate; + } + if (pdata->mad_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: mad_dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + /* + * use dmic_sample_rate as the default for MAD + * if mad dmic sample rate is undefined + */ + pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate; + } + + if (pdata->dmic_clk_drv == + WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) { + pdata->dmic_clk_drv = WCD934X_DMIC_CLK_DRIVE_DEFAULT; + dev_info(codec->dev, + "%s: dmic_clk_strength invalid, default = %d\n", + __func__, pdata->dmic_clk_drv); + } + + switch (pdata->dmic_clk_drv) { + case 2: + dmic_clk_drv = 0; + break; + case 4: + dmic_clk_drv = 1; + break; + case 8: + dmic_clk_drv = 2; + break; + case 16: + dmic_clk_drv = 3; + break; + default: + dev_err(codec->dev, + "%s: invalid dmic_clk_drv %d, using default\n", + __func__, pdata->dmic_clk_drv); + dmic_clk_drv = 0; + break; + } + + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_PAD_DRVCTL_0, + 0x0C, dmic_clk_drv << 2); + + /* + * Default the DMIC clk rates to mad_dmic_sample_rate, + * whereas, the anc/txfe dmic rates to dmic_sample_rate + * since the anc/txfe are independent of mad block. + */ + mad_dmic_ctl_val = tavil_get_dmic_clk_val(tavil->codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC0_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC1_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC2_CTL, + 0x0E, mad_dmic_ctl_val << 1); + + if (dmic_clk_drv == WCD934X_DMIC_CLK_DIV_2) + anc_ctl_value = WCD934X_ANC_DMIC_X2_FULL_RATE; + else + anc_ctl_value = WCD934X_ANC_DMIC_X2_HALF_RATE; + + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL, + 0x20, anc_ctl_value << 5); + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL, + 0x20, anc_ctl_value << 5); + +done: + return rc; +} + +static void tavil_cdc_vote_svs(struct snd_soc_codec *codec, bool vote) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + return tavil_vote_svs(tavil, vote); +} + +struct wcd_dsp_cdc_cb cdc_cb = { + .cdc_clk_en = tavil_codec_internal_rco_ctrl, + .cdc_vote_svs = tavil_cdc_vote_svs, +}; + +static int tavil_wdsp_initialize(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tavil_priv *tavil; + struct wcd_dsp_params params; + int ret = 0; + + control = dev_get_drvdata(codec->dev->parent); + tavil = snd_soc_codec_get_drvdata(codec); + + params.cb = &cdc_cb; + params.irqs.cpe_ipc1_irq = WCD934X_IRQ_CPE1_INTR; + params.irqs.cpe_err_irq = WCD934X_IRQ_CPE_ERROR; + params.irqs.fatal_irqs = CPE_FATAL_IRQS; + params.clk_rate = control->mclk_rate; + params.dsp_instance = 0; + + wcd_dsp_cntl_init(codec, ¶ms, &tavil->wdsp_cntl); + if (!tavil->wdsp_cntl) { + dev_err(tavil->dev, "%s: wcd-dsp-control init failed\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +/* + * tavil_soc_get_mbhc: get wcd934x_mbhc handle of corresponding codec + * @codec: handle to snd_soc_codec * + * + * return wcd934x_mbhc handle or error code in case of failure + */ +struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec) +{ + struct tavil_priv *tavil; + + if (!codec) { + pr_err("%s: Invalid params, NULL codec\n", __func__); + return NULL; + } + tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil) { + pr_err("%s: Invalid params, NULL tavil\n", __func__); + return NULL; + } + + return tavil->mbhc; +} +EXPORT_SYMBOL(tavil_soc_get_mbhc); + +static void tavil_mclk2_reg_defaults(struct tavil_priv *tavil) +{ + int i; + struct snd_soc_codec *codec = tavil->codec; + + if (TAVIL_IS_1_0(tavil->wcd9xxx)) { + /* MCLK2 configuration */ + for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_0_defaults); i++) + snd_soc_update_bits(codec, + tavil_codec_mclk2_1_0_defaults[i].reg, + tavil_codec_mclk2_1_0_defaults[i].mask, + tavil_codec_mclk2_1_0_defaults[i].val); + } + if (TAVIL_IS_1_1(tavil->wcd9xxx)) { + /* MCLK2 configuration */ + for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_1_defaults); i++) + snd_soc_update_bits(codec, + tavil_codec_mclk2_1_1_defaults[i].reg, + tavil_codec_mclk2_1_1_defaults[i].mask, + tavil_codec_mclk2_1_1_defaults[i].val); + } +} + +static int tavil_device_down(struct wcd9xxx *wcd9xxx) +{ + struct snd_soc_codec *codec; + struct tavil_priv *priv; + int count; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + priv = snd_soc_codec_get_drvdata(codec); + swrm_wcd_notify(priv->swr.ctrl_data[0].swr_pdev, + SWR_DEVICE_DOWN, NULL); + tavil_dsd_reset(priv->dsd_config); + snd_soc_card_change_online_state(codec->component.card, 0); + for (count = 0; count < NUM_CODEC_DAIS; count++) + priv->dai[count].bus_down_in_recovery = true; + wcd_dsp_ssr_event(priv->wdsp_cntl, WCD_CDC_DOWN_EVENT); + wcd_resmgr_set_sido_input_src_locked(priv->resmgr, + SIDO_SOURCE_INTERNAL); + + return 0; +} + +static int tavil_post_reset_cb(struct wcd9xxx *wcd9xxx) +{ + int i, ret = 0; + struct wcd9xxx *control; + struct snd_soc_codec *codec; + struct tavil_priv *tavil; + struct wcd9xxx_pdata *pdata; + struct wcd_mbhc *mbhc; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + tavil = snd_soc_codec_get_drvdata(codec); + control = dev_get_drvdata(codec->dev->parent); + + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + + mutex_lock(&tavil->codec_mutex); + /* + * Codec hardware by default comes up in SVS mode. + * Initialize the svs_ref_cnt to 1 to reflect the hardware + * state in the driver. + */ + tavil->svs_ref_cnt = 1; + + tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tavil_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tavil_init_slim_slave_cfg(codec); + snd_soc_card_change_online_state(codec->component.card, 1); + + /* Class-H Init */ + wcd_clsh_init(&tavil->clsh_d); + /* Default HPH Mode to Class-H LOHiFi */ + tavil->hph_mode = CLS_H_LOHIFI; + + for (i = 0; i < TAVIL_MAX_MICBIAS; i++) + tavil->micb_ref[i] = 0; + + for (i = 0; i < COMPANDER_MAX; i++) + tavil->comp_enabled[i] = 0; + + dev_dbg(codec->dev, "%s: MCLK Rate = %x\n", + __func__, control->mclk_rate); + + if (control->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (control->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + wcd_resmgr_post_ssr_v2(tavil->resmgr); + tavil_update_reg_defaults(tavil); + tavil_codec_init_reg(tavil); + __tavil_enable_efuse_sensing(tavil); + tavil_mclk2_reg_defaults(tavil); + + __tavil_cdc_mclk_enable(tavil, true); + regcache_mark_dirty(codec->component.regmap); + regcache_sync(codec->component.regmap); + __tavil_cdc_mclk_enable(tavil, false); + + tavil_update_cpr_defaults(tavil); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tavil_handle_pdata(tavil, pdata); + if (IS_ERR_VALUE(ret)) + dev_err(codec->dev, "%s: invalid pdata\n", __func__); + + /* Initialize MBHC module */ + mbhc = &tavil->mbhc->wcd_mbhc; + ret = tavil_mbhc_post_ssr_init(tavil->mbhc, codec); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto done; + } else { + tavil_mbhc_hs_detect(codec, mbhc->mbhc_cfg); + } + + /* DSD initialization */ + ret = tavil_dsd_post_ssr_init(tavil->dsd_config); + if (ret) + dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__); + + tavil_cleanup_irqs(tavil); + ret = tavil_setup_irqs(tavil); + if (ret) { + dev_err(codec->dev, "%s: tavil irq setup failed %d\n", + __func__, ret); + goto done; + } + + tavil_set_spkr_mode(codec, tavil->swr.spkr_mode); + /* + * Once the codec initialization is completed, the svs vote + * can be released allowing the codec to go to SVS2. + */ + tavil_vote_svs(tavil, false); + wcd_dsp_ssr_event(tavil->wdsp_cntl, WCD_CDC_UP_EVENT); + +done: + mutex_unlock(&tavil->codec_mutex); + return ret; +} + +static int tavil_soc_codec_probe(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tavil_priv *tavil; + struct wcd9xxx_pdata *pdata; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int i, ret; + void *ptr = NULL; + + control = dev_get_drvdata(codec->dev->parent); + + dev_info(codec->dev, "%s()\n", __func__); + tavil = snd_soc_codec_get_drvdata(codec); + tavil->intf_type = wcd9xxx_get_intf_type(); + + control->dev_down = tavil_device_down; + control->post_reset = tavil_post_reset_cb; + control->ssr_priv = (void *)codec; + + /* Resource Manager post Init */ + ret = wcd_resmgr_post_init(tavil->resmgr, &tavil_resmgr_cb, codec); + if (ret) { + dev_err(codec->dev, "%s: wcd resmgr post init failed\n", + __func__); + goto err; + } + /* Class-H Init */ + wcd_clsh_init(&tavil->clsh_d); + /* Default HPH Mode to Class-H Low HiFi */ + tavil->hph_mode = CLS_H_LOHIFI; + + tavil->fw_data = devm_kzalloc(codec->dev, sizeof(*(tavil->fw_data)), + GFP_KERNEL); + if (!tavil->fw_data) + goto err; + + set_bit(WCD9XXX_ANC_CAL, tavil->fw_data->cal_bit); + set_bit(WCD9XXX_MBHC_CAL, tavil->fw_data->cal_bit); + set_bit(WCD9XXX_MAD_CAL, tavil->fw_data->cal_bit); + set_bit(WCD9XXX_VBAT_CAL, tavil->fw_data->cal_bit); + + ret = wcd_cal_create_hwdep(tavil->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (IS_ERR_VALUE(ret)) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + goto err_hwdep; + } + + /* Initialize MBHC module */ + ret = tavil_mbhc_init(&tavil->mbhc, codec, tavil->fw_data); + if (ret) { + pr_err("%s: mbhc initialization failed\n", __func__); + goto err_hwdep; + } + + tavil->codec = codec; + for (i = 0; i < COMPANDER_MAX; i++) + tavil->comp_enabled[i] = 0; + + tavil_codec_init_reg(tavil); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tavil_handle_pdata(tavil, pdata); + if (IS_ERR_VALUE(ret)) { + dev_err(codec->dev, "%s: bad pdata\n", __func__); + goto err_hwdep; + } + + ptr = devm_kzalloc(codec->dev, (sizeof(tavil_rx_chs) + + sizeof(tavil_tx_chs)), GFP_KERNEL); + if (!ptr) { + ret = -ENOMEM; + goto err_hwdep; + } + + snd_soc_dapm_add_routes(dapm, tavil_slim_audio_map, + ARRAY_SIZE(tavil_slim_audio_map)); + for (i = 0; i < NUM_CODEC_DAIS; i++) { + INIT_LIST_HEAD(&tavil->dai[i].wcd9xxx_ch_list); + init_waitqueue_head(&tavil->dai[i].dai_wait); + } + tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tavil_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tavil_slimbus_slave_port_cfg.slave_port_mapping[0] = + WCD934X_TX13; + tavil_init_slim_slave_cfg(codec); + + control->num_rx_port = WCD934X_RX_MAX; + control->rx_chs = ptr; + memcpy(control->rx_chs, tavil_rx_chs, sizeof(tavil_rx_chs)); + control->num_tx_port = WCD934X_TX_MAX; + control->tx_chs = ptr + sizeof(tavil_rx_chs); + memcpy(control->tx_chs, tavil_tx_chs, sizeof(tavil_tx_chs)); + + ret = tavil_setup_irqs(tavil); + if (ret) { + dev_err(tavil->dev, "%s: tavil irq setup failed %d\n", + __func__, ret); + goto err_pdata; + } + + for (i = 0; i < WCD934X_NUM_DECIMATORS; i++) { + tavil->tx_hpf_work[i].tavil = tavil; + tavil->tx_hpf_work[i].decimator = i; + INIT_DELAYED_WORK(&tavil->tx_hpf_work[i].dwork, + tavil_tx_hpf_corner_freq_callback); + + tavil->tx_mute_dwork[i].tavil = tavil; + tavil->tx_mute_dwork[i].decimator = i; + INIT_DELAYED_WORK(&tavil->tx_mute_dwork[i].dwork, + tavil_tx_mute_update_callback); + } + + tavil->spk_anc_dwork.tavil = tavil; + INIT_DELAYED_WORK(&tavil->spk_anc_dwork.dwork, + tavil_spk_anc_update_callback); + + tavil_mclk2_reg_defaults(tavil); + + /* DSD initialization */ + tavil->dsd_config = tavil_dsd_init(codec); + if (IS_ERR_OR_NULL(tavil->dsd_config)) + dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__); + + mutex_lock(&tavil->codec_mutex); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + mutex_unlock(&tavil->codec_mutex); + + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + + snd_soc_dapm_sync(dapm); + + tavil_wdsp_initialize(codec); + + /* + * Once the codec initialization is completed, the svs vote + * can be released allowing the codec to go to SVS2. + */ + tavil_vote_svs(tavil, false); + + return ret; + +err_pdata: + devm_kfree(codec->dev, ptr); + control->rx_chs = NULL; + control->tx_chs = NULL; +err_hwdep: + devm_kfree(codec->dev, tavil->fw_data); + tavil->fw_data = NULL; +err: + return ret; +} + +static int tavil_soc_codec_remove(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + control = dev_get_drvdata(codec->dev->parent); + devm_kfree(codec->dev, control->rx_chs); + control->rx_chs = NULL; + control->tx_chs = NULL; + tavil_cleanup_irqs(tavil); + + if (tavil->wdsp_cntl) + wcd_dsp_cntl_deinit(&tavil->wdsp_cntl); + + /* Deinitialize MBHC module */ + tavil_mbhc_deinit(codec); + tavil->mbhc = NULL; + + return 0; +} + +static struct regmap *tavil_get_regmap(struct device *dev) +{ + struct wcd9xxx *control = dev_get_drvdata(dev->parent); + + return control->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_tavil = { + .probe = tavil_soc_codec_probe, + .remove = tavil_soc_codec_remove, + .controls = tavil_snd_controls, + .num_controls = ARRAY_SIZE(tavil_snd_controls), + .dapm_widgets = tavil_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tavil_dapm_widgets), + .dapm_routes = tavil_audio_map, + .num_dapm_routes = ARRAY_SIZE(tavil_audio_map), + .get_regmap = tavil_get_regmap, +}; + +#ifdef CONFIG_PM +static int tavil_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tavil_priv *tavil = platform_get_drvdata(pdev); + + if (!tavil) { + dev_err(dev, "%s: tavil private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system suspend\n", __func__); + if (delayed_work_pending(&tavil->power_gate_work) && + cancel_delayed_work_sync(&tavil->power_gate_work)) + tavil_codec_power_gate_digital_core(tavil); + return 0; +} + +static int tavil_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tavil_priv *tavil = platform_get_drvdata(pdev); + + if (!tavil) { + dev_err(dev, "%s: tavil private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system resume\n", __func__); + return 0; +} + +static const struct dev_pm_ops tavil_pm_ops = { + .suspend = tavil_suspend, + .resume = tavil_resume, +}; +#endif + +static int tavil_swrm_read(void *handle, int reg) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + unsigned short swr_rd_addr_base; + unsigned short swr_rd_data_base; + int val, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tavil = (struct tavil_priv *)handle; + wcd9xxx = tavil->wcd9xxx; + + dev_dbg(tavil->dev, "%s: Reading soundwire register, 0x%x\n", + __func__, reg); + swr_rd_addr_base = WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0; + swr_rd_data_base = WCD934X_SWR_AHB_BRIDGE_RD_DATA_0; + + mutex_lock(&tavil->swr.read_mutex); + ret = regmap_bulk_write(wcd9xxx->regmap, swr_rd_addr_base, + (u8 *)®, 4); + if (ret < 0) { + dev_err(tavil->dev, "%s: RD Addr Failure\n", __func__); + goto done; + } + ret = regmap_bulk_read(wcd9xxx->regmap, swr_rd_data_base, + (u8 *)&val, 4); + if (ret < 0) { + dev_err(tavil->dev, "%s: RD Data Failure\n", __func__); + goto done; + } + ret = val; +done: + mutex_unlock(&tavil->swr.read_mutex); + + return ret; +} + +static int tavil_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_reg_val *bulk_reg; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + int i, j, ret; + + if (!handle || !reg || !val) { + pr_err("%s: NULL parameter\n", __func__); + return -EINVAL; + } + if (len <= 0) { + pr_err("%s: Invalid size: %zu\n", __func__, len); + return -EINVAL; + } + tavil = (struct tavil_priv *)handle; + wcd9xxx = tavil->wcd9xxx; + + swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0; + + bulk_reg = kzalloc((2 * len * sizeof(struct wcd9xxx_reg_val)), + GFP_KERNEL); + if (!bulk_reg) + return -ENOMEM; + + for (i = 0, j = 0; i < (len * 2); i += 2, j++) { + bulk_reg[i].reg = swr_wr_data_base; + bulk_reg[i].buf = (u8 *)(&val[j]); + bulk_reg[i].bytes = 4; + bulk_reg[i+1].reg = swr_wr_addr_base; + bulk_reg[i+1].buf = (u8 *)(®[j]); + bulk_reg[i+1].bytes = 4; + } + + mutex_lock(&tavil->swr.write_mutex); + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + (len * 2), false); + if (ret) { + dev_err(tavil->dev, "%s: swrm bulk write failed, ret: %d\n", + __func__, ret); + } + mutex_unlock(&tavil->swr.write_mutex); + + kfree(bulk_reg); + return ret; +} + +static int tavil_swrm_write(void *handle, int reg, int val) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + struct wcd9xxx_reg_val bulk_reg[2]; + int ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tavil = (struct tavil_priv *)handle; + wcd9xxx = tavil->wcd9xxx; + + swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0; + + /* First Write the Data to register */ + bulk_reg[0].reg = swr_wr_data_base; + bulk_reg[0].buf = (u8 *)(&val); + bulk_reg[0].bytes = 4; + bulk_reg[1].reg = swr_wr_addr_base; + bulk_reg[1].buf = (u8 *)(®); + bulk_reg[1].bytes = 4; + + mutex_lock(&tavil->swr.write_mutex); + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false); + if (ret < 0) + dev_err(tavil->dev, "%s: WR Data Failure\n", __func__); + mutex_unlock(&tavil->swr.write_mutex); + + return ret; +} + +static int tavil_swrm_clock(void *handle, bool enable) +{ + struct tavil_priv *tavil; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tavil = (struct tavil_priv *)handle; + + mutex_lock(&tavil->swr.clk_mutex); + dev_dbg(tavil->dev, "%s: swrm clock %s\n", + __func__, (enable?"enable" : "disable")); + if (enable) { + tavil->swr.clk_users++; + if (tavil->swr.clk_users == 1) { + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x00); + __tavil_cdc_mclk_enable(tavil, true); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + } + } else { + tavil->swr.clk_users--; + if (tavil->swr.clk_users == 0) { + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + __tavil_cdc_mclk_enable(tavil, false); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x10); + } + } + dev_dbg(tavil->dev, "%s: swrm clock users %d\n", + __func__, tavil->swr.clk_users); + mutex_unlock(&tavil->swr.clk_mutex); + + return 0; +} + +static int tavil_swrm_handle_irq(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action) +{ + struct tavil_priv *tavil; + int ret = 0; + struct wcd9xxx *wcd9xxx; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tavil = (struct tavil_priv *) handle; + wcd9xxx = tavil->wcd9xxx; + + if (action) { + ret = wcd9xxx_request_irq(&wcd9xxx->core_res, + WCD934X_IRQ_SOUNDWIRE, + swrm_irq_handler, + "Tavil SWR Master", swrm_handle); + if (ret) + dev_err(tavil->dev, "%s: Failed to request irq %d\n", + __func__, WCD934X_IRQ_SOUNDWIRE); + } else + wcd9xxx_free_irq(&wcd9xxx->core_res, WCD934X_IRQ_SOUNDWIRE, + swrm_handle); + + return ret; +} + +static void tavil_codec_add_spi_device(struct tavil_priv *tavil, + struct device_node *node) +{ + struct spi_master *master; + struct spi_device *spi; + u32 prop_value; + int rc; + + /* Read the master bus num from DT node */ + rc = of_property_read_u32(node, "qcom,master-bus-num", + &prop_value); + if (IS_ERR_VALUE(rc)) { + dev_err(tavil->dev, "%s: prop %s not found in node %s", + __func__, "qcom,master-bus-num", node->full_name); + goto done; + } + + /* Get the reference to SPI master */ + master = spi_busnum_to_master(prop_value); + if (!master) { + dev_err(tavil->dev, "%s: Invalid spi_master for bus_num %u\n", + __func__, prop_value); + goto done; + } + + /* Allocate the spi device */ + spi = spi_alloc_device(master); + if (!spi) { + dev_err(tavil->dev, "%s: spi_alloc_device failed\n", + __func__); + goto err_spi_alloc_dev; + } + + /* Initialize device properties */ + if (of_modalias_node(node, spi->modalias, + sizeof(spi->modalias)) < 0) { + dev_err(tavil->dev, "%s: cannot find modalias for %s\n", + __func__, node->full_name); + goto err_dt_parse; + } + + rc = of_property_read_u32(node, "qcom,chip-select", + &prop_value); + if (IS_ERR_VALUE(rc)) { + dev_err(tavil->dev, "%s: prop %s not found in node %s", + __func__, "qcom,chip-select", node->full_name); + goto err_dt_parse; + } + spi->chip_select = prop_value; + + rc = of_property_read_u32(node, "qcom,max-frequency", + &prop_value); + if (IS_ERR_VALUE(rc)) { + dev_err(tavil->dev, "%s: prop %s not found in node %s", + __func__, "qcom,max-frequency", node->full_name); + goto err_dt_parse; + } + spi->max_speed_hz = prop_value; + + spi->dev.of_node = node; + + rc = spi_add_device(spi); + if (IS_ERR_VALUE(rc)) { + dev_err(tavil->dev, "%s: spi_add_device failed\n", __func__); + goto err_dt_parse; + } + + /* Put the reference to SPI master */ + put_device(&master->dev); + + return; + +err_dt_parse: + spi_dev_put(spi); + +err_spi_alloc_dev: + /* Put the reference to SPI master */ + put_device(&master->dev); +done: + return; +} + +static void tavil_add_child_devices(struct work_struct *work) +{ + struct tavil_priv *tavil; + struct platform_device *pdev; + struct device_node *node; + struct wcd9xxx *wcd9xxx; + struct tavil_swr_ctrl_data *swr_ctrl_data = NULL, *temp; + int ret, ctrl_num = 0; + struct wcd_swr_ctrl_platform_data *platdata; + char plat_dev_name[WCD934X_STRING_LEN]; + + tavil = container_of(work, struct tavil_priv, + tavil_add_child_devices_work); + if (!tavil) { + pr_err("%s: Memory for WCD934X does not exist\n", + __func__); + return; + } + wcd9xxx = tavil->wcd9xxx; + if (!wcd9xxx) { + pr_err("%s: Memory for WCD9XXX does not exist\n", + __func__); + return; + } + if (!wcd9xxx->dev->of_node) { + dev_err(wcd9xxx->dev, "%s: DT node for wcd9xxx does not exist\n", + __func__); + return; + } + + platdata = &tavil->swr.plat_data; + + for_each_child_of_node(wcd9xxx->dev->of_node, node) { + + /* Parse and add the SPI device node */ + if (!strcmp(node->name, "wcd_spi")) { + tavil_codec_add_spi_device(tavil, node); + continue; + } + + /* Parse other child device nodes and add platform device */ + if (!strcmp(node->name, "swr_master")) + strlcpy(plat_dev_name, "tavil_swr_ctrl", + (WCD934X_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (WCD934X_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err_mem; + } + pdev->dev.parent = tavil->dev; + pdev->dev.of_node = node; + + if (strcmp(node->name, "swr_master") == 0) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto err_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto err_pdev_add; + } + + if (strcmp(node->name, "swr_master") == 0) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct tavil_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(wcd9xxx->dev, "out of memory\n"); + ret = -ENOMEM; + goto err_pdev_add; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + tavil->swr.ctrl_data = swr_ctrl_data; + } + } + + return; + +err_pdev_add: + platform_device_put(pdev); +err_mem: + return; +} + +static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil) +{ + int val, rc; + + __tavil_cdc_mclk_enable(tavil, true); + + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x1E, 0x10); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x01, 0x01); + + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + rc = regmap_read(tavil->wcd9xxx->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); + + __tavil_cdc_mclk_enable(tavil, false); + + return rc; +} + +static void ___tavil_get_codec_fine_version(struct tavil_priv *tavil) +{ + int val1, val2, version; + struct regmap *regmap; + u16 id_minor; + u32 version_mask = 0; + + regmap = tavil->wcd9xxx->regmap; + version = tavil->wcd9xxx->version; + id_minor = tavil->wcd9xxx->codec_type->id_minor; + + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val1); + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val2); + + dev_dbg(tavil->dev, "%s: chip version :0x%x 0x:%x\n", + __func__, val1, 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 == cpu_to_le16(0)) + version = TAVIL_VERSION_WCD9340_1_0; + else if (id_minor == cpu_to_le16(0x01)) + version = TAVIL_VERSION_WCD9340_1_1; + break; + case SLNQ_DISABLED: + if (id_minor == cpu_to_le16(0)) + version = TAVIL_VERSION_WCD9341_1_0; + else if (id_minor == cpu_to_le16(0x01)) + version = TAVIL_VERSION_WCD9341_1_1; + break; + } + + tavil->wcd9xxx->version = version; + tavil->wcd9xxx->codec_type->version = version; +} + +/* + * tavil_get_wcd_dsp_cntl: Get the reference to wcd_dsp_cntl + * @dev: Device pointer for codec device + * + * This API gets the reference to codec's struct wcd_dsp_cntl + */ +struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev) +{ + struct platform_device *pdev; + struct tavil_priv *tavil; + + if (!dev) { + pr_err("%s: Invalid device\n", __func__); + return NULL; + } + + pdev = to_platform_device(dev); + tavil = platform_get_drvdata(pdev); + + return tavil->wdsp_cntl; +} +EXPORT_SYMBOL(tavil_get_wcd_dsp_cntl); + +static int tavil_probe(struct platform_device *pdev) +{ + int ret = 0; + struct tavil_priv *tavil; + struct clk *wcd_ext_clk; + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd9xxx_power_region *cdc_pwr; + + tavil = devm_kzalloc(&pdev->dev, sizeof(struct tavil_priv), + GFP_KERNEL); + if (!tavil) + return -ENOMEM; + + platform_set_drvdata(pdev, tavil); + + tavil->wcd9xxx = dev_get_drvdata(pdev->dev.parent); + tavil->dev = &pdev->dev; + INIT_DELAYED_WORK(&tavil->power_gate_work, tavil_codec_power_gate_work); + mutex_init(&tavil->power_lock); + INIT_WORK(&tavil->tavil_add_child_devices_work, + tavil_add_child_devices); + mutex_init(&tavil->micb_lock); + mutex_init(&tavil->swr.read_mutex); + mutex_init(&tavil->swr.write_mutex); + mutex_init(&tavil->swr.clk_mutex); + mutex_init(&tavil->codec_mutex); + mutex_init(&tavil->svs_mutex); + + /* + * Codec hardware by default comes up in SVS mode. + * Initialize the svs_ref_cnt to 1 to reflect the hardware + * state in the driver. + */ + tavil->svs_ref_cnt = 1; + + cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region), + GFP_KERNEL); + if (!cdc_pwr) { + ret = -ENOMEM; + goto err_resmgr; + } + tavil->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr; + cdc_pwr->pwr_collapse_reg_min = WCD934X_DIG_CORE_REG_MIN; + cdc_pwr->pwr_collapse_reg_max = WCD934X_DIG_CORE_REG_MAX; + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + /* + * Init resource manager so that if child nodes such as SoundWire + * requests for clock, resource manager can honor the request + */ + resmgr = wcd_resmgr_init(&tavil->wcd9xxx->core_res, NULL); + if (IS_ERR(resmgr)) { + ret = PTR_ERR(resmgr); + dev_err(&pdev->dev, "%s: Failed to initialize wcd resmgr\n", + __func__); + goto err_resmgr; + } + tavil->resmgr = resmgr; + tavil->swr.plat_data.handle = (void *) tavil; + tavil->swr.plat_data.read = tavil_swrm_read; + tavil->swr.plat_data.write = tavil_swrm_write; + tavil->swr.plat_data.bulk_write = tavil_swrm_bulk_write; + tavil->swr.plat_data.clk = tavil_swrm_clock; + tavil->swr.plat_data.handle_irq = tavil_swrm_handle_irq; + tavil->swr.spkr_gain_offset = WCD934X_RX_GAIN_OFFSET_0_DB; + + /* Register for Clock */ + wcd_ext_clk = clk_get(tavil->wcd9xxx->dev, "wcd_clk"); + if (IS_ERR(wcd_ext_clk)) { + dev_err(tavil->wcd9xxx->dev, "%s: clk get %s failed\n", + __func__, "wcd_ext_clk"); + goto err_clk; + } + tavil->wcd_ext_clk = wcd_ext_clk; + set_bit(AUDIO_NOMINAL, &tavil->status_mask); + /* Update codec register default values */ + dev_dbg(&pdev->dev, "%s: MCLK Rate = %x\n", __func__, + tavil->wcd9xxx->mclk_rate); + if (tavil->wcd9xxx->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ) + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (tavil->wcd9xxx->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + tavil_update_reg_defaults(tavil); + __tavil_enable_efuse_sensing(tavil); + ___tavil_get_codec_fine_version(tavil); + tavil_update_cpr_defaults(tavil); + + /* Register with soc framework */ + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil, + tavil_dai, ARRAY_SIZE(tavil_dai)); + if (ret) { + dev_err(&pdev->dev, "%s: Codec registration failed\n", + __func__); + goto err_cdc_reg; + } + schedule_work(&tavil->tavil_add_child_devices_work); + + return ret; + +err_cdc_reg: + clk_put(tavil->wcd_ext_clk); +err_clk: + wcd_resmgr_remove(tavil->resmgr); +err_resmgr: + mutex_destroy(&tavil->micb_lock); + mutex_destroy(&tavil->svs_mutex); + mutex_destroy(&tavil->codec_mutex); + mutex_destroy(&tavil->swr.read_mutex); + mutex_destroy(&tavil->swr.write_mutex); + mutex_destroy(&tavil->swr.clk_mutex); + devm_kfree(&pdev->dev, tavil); + + return ret; +} + +static int tavil_remove(struct platform_device *pdev) +{ + struct tavil_priv *tavil; + + tavil = platform_get_drvdata(pdev); + if (!tavil) + return -EINVAL; + + mutex_destroy(&tavil->micb_lock); + mutex_destroy(&tavil->svs_mutex); + mutex_destroy(&tavil->codec_mutex); + mutex_destroy(&tavil->swr.read_mutex); + mutex_destroy(&tavil->swr.write_mutex); + mutex_destroy(&tavil->swr.clk_mutex); + + snd_soc_unregister_codec(&pdev->dev); + clk_put(tavil->wcd_ext_clk); + wcd_resmgr_remove(tavil->resmgr); + if (tavil->dsd_config) { + tavil_dsd_deinit(tavil->dsd_config); + tavil->dsd_config = NULL; + } + devm_kfree(&pdev->dev, tavil); + return 0; +} + +static struct platform_driver tavil_codec_driver = { + .probe = tavil_probe, + .remove = tavil_remove, + .driver = { + .name = "tavil_codec", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &tavil_pm_ops, +#endif + }, +}; + +module_platform_driver(tavil_codec_driver); + +MODULE_DESCRIPTION("Tavil Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd934x/wcd934x.h b/sound/soc/codecs/wcd934x/wcd934x.h new file mode 100644 index 0000000000000000000000000000000000000000..ae70175de239c094609dcdc41547a7624812873a --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x.h @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef WCD934X_H +#define WCD934X_H + +#include +#include +#include "wcd934x-dsp-cntl.h" +#include "../wcd9xxx-common-v2.h" +#include "../wcd-mbhc-v2.h" + +#define WCD934X_REGISTER_START_OFFSET 0x800 +#define WCD934X_SB_PGD_PORT_RX_BASE 0x40 +#define WCD934X_SB_PGD_PORT_TX_BASE 0x50 +#define WCD934X_RX_PORT_START_NUMBER 16 + +#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 WCD934X_ANC_DMIC_X2_FULL_RATE 1 +#define WCD934X_ANC_DMIC_X2_HALF_RATE 0 + +#define TAVIL_MAX_MICBIAS 4 +#define TAVIL_NUM_INTERPOLATORS 9 +#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64 + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) + +/* Feature masks to distinguish codec version */ +#define DSD_DISABLED_MASK 0 +#define SLNQ_DISABLED_MASK 1 + +#define DSD_DISABLED (1 << DSD_DISABLED_MASK) +#define SLNQ_DISABLED (1 << SLNQ_DISABLED_MASK) + +/* Number of input and output Slimbus port */ +enum { + WCD934X_RX0 = 0, + WCD934X_RX1, + WCD934X_RX2, + WCD934X_RX3, + WCD934X_RX4, + WCD934X_RX5, + WCD934X_RX6, + WCD934X_RX7, + 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, +}; + +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, + INTERP_SPKR2, + INTERP_MAX, +}; + +enum { + /* INTR_REG 0 */ + WCD934X_IRQ_MISC = 1, + WCD934X_IRQ_HPH_PA_OCPL_FAULT, + WCD934X_IRQ_HPH_PA_OCPR_FAULT, + WCD934X_IRQ_EAR_PA_OCP_FAULT, + WCD934X_IRQ_HPH_PA_CNPL_COMPLETE, + WCD934X_IRQ_HPH_PA_CNPR_COMPLETE, + WCD934X_IRQ_EAR_PA_CNP_COMPLETE, + /* INTR_REG 1 */ + WCD934X_IRQ_MBHC_SW_DET, + WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, + WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, + WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD934X_IRQ_RESERVED_0, + WCD934X_IRQ_RESERVED_1, + WCD934X_IRQ_RESERVED_2, + /* INTR_REG 2 */ + WCD934X_IRQ_LINE_PA1_CNP_COMPLETE, + WCD934X_IRQ_LINE_PA2_CNP_COMPLETE, + WCD934X_IRQ_SLNQ_ANALOG_ERROR, + WCD934X_IRQ_RESERVED_3, + WCD934X_IRQ_SOUNDWIRE, + WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE, + WCD934X_IRQ_RCO_ERROR, + WCD934X_IRQ_CPE_ERROR, + /* INTR_REG 3 */ + WCD934X_IRQ_MAD_AUDIO, + WCD934X_IRQ_MAD_BEACON, + WCD934X_IRQ_MAD_ULTRASOUND, + WCD934X_IRQ_VBAT_ATTACK, + WCD934X_IRQ_VBAT_RESTORE, + WCD934X_IRQ_CPE1_INTR, + WCD934X_IRQ_RESERVED_4, + WCD934X_IRQ_SLNQ_DIGITAL, + WCD934X_NUM_IRQS, +}; + +/* + * Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + WCD934X_SPKR_MODE_DEFAULT, + WCD934X_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +/* + * Rx path gain offsets + */ +enum { + WCD934X_RX_GAIN_OFFSET_M1P5_DB, + WCD934X_RX_GAIN_OFFSET_0_DB, +}; + +/* + * Dai data structure holds the + * dai specific info like rate, + * channel number etc. + */ +struct tavil_codec_dai_data { + u32 rate; + u32 *ch_num; + u32 ch_act; + u32 ch_tot; +}; + +/* + * Structure used to update codec + * register defaults after reset + */ +struct tavil_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +extern void *tavil_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type); +extern int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable); +extern int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode); +extern int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset); +extern struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev); +extern int wcd934x_get_micb_vout_ctl_val(u32 micb_mv); +extern int tavil_micbias_control(struct snd_soc_codec *codec, + int micb_num, + int req, bool is_dapm); +extern int tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, + int micb_num); +extern struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec); +extern int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, + int event, int intp_idx); +extern struct tavil_dsd_config *tavil_get_dsd_config( + struct snd_soc_codec *codec); +extern int tavil_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +#endif diff --git a/sound/soc/codecs/wcd9xxx-common-v2.c b/sound/soc/codecs/wcd9xxx-common-v2.c new file mode 100644 index 0000000000000000000000000000000000000000..ad62d182a79af57886217e1eee2b016f2bd86ee0 --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-common-v2.c @@ -0,0 +1,1270 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "wcd9xxx-common-v2.h" + +#define WCD_USLEEP_RANGE 50 +#define MAX_IMPED_PARAMS 6 + +enum { + DAC_GAIN_0DB = 0, + DAC_GAIN_0P2DB, + DAC_GAIN_0P4DB, + DAC_GAIN_0P6DB, + DAC_GAIN_0P8DB, + DAC_GAIN_M0P2DB, + DAC_GAIN_M0P4DB, + DAC_GAIN_M0P6DB, +}; + +enum { + VREF_FILT_R_0OHM = 0, + VREF_FILT_R_25KOHM, + VREF_FILT_R_50KOHM, + VREF_FILT_R_100KOHM, +}; + +enum { + DELTA_I_0MA, + DELTA_I_10MA, + DELTA_I_20MA, + DELTA_I_30MA, + DELTA_I_40MA, + DELTA_I_50MA, +}; + +struct wcd_imped_val { + u32 imped_val; + u8 index; +}; + +static const struct wcd_reg_mask_val imped_table[][MAX_IMPED_PARAMS] = { + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf5}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf5}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf5}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf5}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x0}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x0}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfe}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfe}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfe}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfe}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xff}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xff}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xff}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xff}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, +}; + +static const struct wcd_imped_val imped_index[] = { + {4, 0}, + {5, 1}, + {6, 2}, + {7, 3}, + {8, 4}, + {9, 5}, + {10, 6}, + {11, 7}, + {12, 8}, + {13, 9}, +}; + +static void (*clsh_state_fp[NUM_CLSH_STATES_V2])(struct snd_soc_codec *, + struct wcd_clsh_cdc_data *, + u8 req_state, bool en, int mode); + +static int get_impedance_index(int imped) +{ + int i = 0; + + if (imped < imped_index[i].imped_val) { + pr_debug("%s, detected impedance is less than 4 Ohm\n", + __func__); + i = 0; + goto ret; + } + if (imped >= imped_index[ARRAY_SIZE(imped_index) - 1].imped_val) { + pr_debug("%s, detected impedance is greater than 12 Ohm\n", + __func__); + i = ARRAY_SIZE(imped_index) - 1; + goto ret; + } + for (i = 0; i < ARRAY_SIZE(imped_index) - 1; i++) { + if (imped >= imped_index[i].imped_val && + imped < imped_index[i + 1].imped_val) + break; + } +ret: + pr_debug("%s: selected impedance index = %d\n", + __func__, imped_index[i].index); + return imped_index[i].index; +} + +/* + * Function: wcd_clsh_imped_config + * Params: codec, imped, reset + * Description: + * This function updates HPHL and HPHR gain settings + * according to the impedance value. + */ +void wcd_clsh_imped_config(struct snd_soc_codec *codec, int imped, bool reset) +{ + int i; + int index = 0; + + /* reset = 1, which means request is to reset the register values */ + if (reset) { + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_update_bits(codec, imped_table[index][i].reg, + imped_table[index][i].mask, 0); + return; + } + index = get_impedance_index(imped); + if (index >= (ARRAY_SIZE(imped_index) - 1)) { + pr_debug("%s, impedance not in range = %d\n", __func__, imped); + return; + } + if (index >= ARRAY_SIZE(imped_table)) { + pr_debug("%s, impedance index not in range = %d\n", __func__, + index); + return; + } + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_update_bits(codec, imped_table[index][i].reg, + imped_table[index][i].mask, + imped_table[index][i].val); +} +EXPORT_SYMBOL(wcd_clsh_imped_config); + +static bool is_native_44_1_active(struct snd_soc_codec *codec) +{ + bool native_active = false; + u8 native_clk, rx1_rate, rx2_rate; + + native_clk = snd_soc_read(codec, + WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL); + rx1_rate = snd_soc_read(codec, WCD9XXX_CDC_RX1_RX_PATH_CTL); + rx2_rate = snd_soc_read(codec, WCD9XXX_CDC_RX2_RX_PATH_CTL); + + dev_dbg(codec->dev, "%s: native_clk %x rx1_rate= %x rx2_rate= %x", + __func__, native_clk, rx1_rate, rx2_rate); + + if ((native_clk & 0x2) && + ((rx1_rate & 0x0F) == 0x9 || (rx2_rate & 0x0F) == 0x9)) + native_active = true; + + return native_active; +} + +static const char *mode_to_str(int mode) +{ + switch (mode) { + case CLS_H_NORMAL: + return "CLS_H_NORMAL"; + case CLS_H_HIFI: + return "CLS_H_HIFI"; + case CLS_H_LOHIFI: + return "CLS_H_LOHIFI"; + case CLS_H_LP: + return "CLS_H_LP"; + case CLS_H_ULP: + return "CLS_H_ULP"; + case CLS_AB: + return "CLS_AB"; + case CLS_AB_HIFI: + return "CLS_AB_HIFI"; + default: + return "CLS_H_INVALID"; + }; +} + +static const char *state_to_str(u8 state, char *buf, size_t buflen) +{ + int i; + int cnt = 0; + /* + * This array of strings should match with enum wcd_clsh_state_bit. + */ + static const char *const states[] = { + "STATE_EAR", + "STATE_HPH_L", + "STATE_HPH_R", + "STATE_LO", + }; + + if (state == WCD_CLSH_STATE_IDLE) { + snprintf(buf, buflen, "[STATE_IDLE]"); + goto done; + } + + buf[0] = '\0'; + for (i = 0; i < ARRAY_SIZE(states); i++) { + if (!(state & (1 << i))) + continue; + cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf, + buf[0] == '\0' ? "[" : "|", + states[i]); + } + if (cnt > 0) + strlcat(buf + cnt, "]", buflen); + +done: + if (buf[0] == '\0') + snprintf(buf, buflen, "[STATE_UNKNOWN]"); + return buf; +} + +static inline void +wcd_enable_clsh_block(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, bool enable) +{ + if ((enable && ++clsh_d->clsh_users == 1) || + (!enable && --clsh_d->clsh_users == 0)) + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_CRC, 0x01, + (u8) enable); + if (clsh_d->clsh_users < 0) + clsh_d->clsh_users = 0; + dev_dbg(codec->dev, "%s: clsh_users %d, enable %d", __func__, + clsh_d->clsh_users, enable); +} + +static inline bool wcd_clsh_enable_status(struct snd_soc_codec *codec) +{ + return snd_soc_read(codec, WCD9XXX_A_CDC_CLSH_CRC) & 0x01; +} + +static inline int wcd_clsh_get_int_mode(struct wcd_clsh_cdc_data *clsh_d, + int clsh_state) +{ + int mode; + + if ((clsh_state != WCD_CLSH_STATE_EAR) && + (clsh_state != WCD_CLSH_STATE_HPHL) && + (clsh_state != WCD_CLSH_STATE_HPHR) && + (clsh_state != WCD_CLSH_STATE_LO)) + mode = CLS_NONE; + else + mode = clsh_d->interpolator_modes[ffs(clsh_state)]; + + return mode; +} + +static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_data *clsh_d, + int clsh_state, int mode) +{ + if ((clsh_state != WCD_CLSH_STATE_EAR) && + (clsh_state != WCD_CLSH_STATE_HPHL) && + (clsh_state != WCD_CLSH_STATE_HPHR) && + (clsh_state != WCD_CLSH_STATE_LO)) + return; + + clsh_d->interpolator_modes[ffs(clsh_state)] = mode; +} + +static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x08, 0x08); /* set to HIFI */ + else + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x08, 0x00); /* set to default */ +} + +static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x04, 0x04); /* set to HIFI */ + else + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x04, 0x00); /* set to Default */ +} + +static inline void wcd_clsh_gm3_boost_disable(struct snd_soc_codec *codec, + int mode) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!IS_CODEC_TYPE(wcd9xxx, WCD934X)) + return; + + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) { + if (TAVIL_IS_1_0(wcd9xxx)) + snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL, + 0x80, 0x0); /* disable GM3 Boost */ + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x80); + } else { + snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL, + 0x80, 0x80); /* set to Default */ + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x70); + } +} + + +static inline void wcd_clsh_force_iq_ctl(struct snd_soc_codec *codec, + int mode) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!IS_CODEC_TYPE(wcd9xxx, WCD934X)) + return; + + if (mode == CLS_H_LOHIFI || mode == CLS_AB) { + snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x20); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0xC0); + snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x02); + } else { + + snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x0); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0x80); + snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x06); + } +} + +static void wcd_clsh_buck_ctrl(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + int mode, + bool enable) +{ + /* enable/disable buck */ + if ((enable && (++clsh_d->buck_users == 1)) || + (!enable && (--clsh_d->buck_users == 0))) + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + (1 << 7), (enable << 7)); + dev_dbg(codec->dev, "%s: buck_users %d, enable %d, mode: %s", + __func__, clsh_d->buck_users, enable, mode_to_str(mode)); + /* + * 500us sleep is required after buck enable/disable + * as per HW requirement + */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); +} + +static void wcd_clsh_flyback_ctrl(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + int mode, + bool enable) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_reg_val bulk_reg[2]; + u8 vneg[] = {0x00, 0x40}; + + /* enable/disable flyback */ + if ((enable && (++clsh_d->flyback_users == 1)) || + (!enable && (--clsh_d->flyback_users == 0))) { + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + (1 << 6), (enable << 6)); + /* 100usec delay is needed as per HW requirement */ + usleep_range(100, 110); + if (enable && (TASHA_IS_1_1(wcd9xxx))) { + wcd_clsh_set_flyback_mode(codec, CLS_H_HIFI); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN, + 0x60, 0x40); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN, + 0x10, 0x10); + vneg[0] = snd_soc_read(codec, + WCD9XXX_A_ANA_RX_SUPPLIES); + vneg[0] &= ~(0x40); + vneg[1] = vneg[0] | 0x40; + bulk_reg[0].reg = WCD9XXX_A_ANA_RX_SUPPLIES; + bulk_reg[0].buf = &vneg[0]; + bulk_reg[0].bytes = 1; + bulk_reg[1].reg = WCD9XXX_A_ANA_RX_SUPPLIES; + bulk_reg[1].buf = &vneg[1]; + bulk_reg[1].bytes = 1; + /* 500usec delay is needed as per HW requirement */ + usleep_range(500, 510); + wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, + false); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN, + 0x10, 0x00); + wcd_clsh_set_flyback_mode(codec, mode); + } + + } + dev_dbg(codec->dev, "%s: flyback_users %d, enable %d, mode: %s", + __func__, clsh_d->flyback_users, enable, mode_to_str(mode)); + /* + * 500us sleep is required after flyback enable/disable + * as per HW requirement + */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); +} + +static void wcd_clsh_set_gain_path(struct snd_soc_codec *codec, + int mode) +{ + u8 val = 0; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!TASHA_IS_2_0(wcd9xxx)) + return; + + switch (mode) { + case CLS_H_NORMAL: + case CLS_AB: + val = 0x00; + break; + case CLS_H_HIFI: + val = 0x02; + break; + case CLS_H_LP: + val = 0x01; + break; + default: + return; + }; + snd_soc_update_bits(codec, WCD9XXX_HPH_L_EN, 0xC0, (val << 6)); + snd_soc_update_bits(codec, WCD9XXX_HPH_R_EN, 0xC0, (val << 6)); +} + +static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec, + int mode) +{ + u8 val = 0; + u8 gain = 0; + u8 res_val = VREF_FILT_R_0OHM; + u8 ipeak = DELTA_I_50MA; + + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + switch (mode) { + case CLS_H_NORMAL: + res_val = VREF_FILT_R_50KOHM; + val = 0x00; + gain = DAC_GAIN_0DB; + ipeak = DELTA_I_50MA; + break; + case CLS_AB: + val = 0x00; + gain = DAC_GAIN_0DB; + ipeak = DELTA_I_50MA; + break; + case CLS_AB_HIFI: + val = 0x08; + break; + case CLS_H_HIFI: + val = 0x08; + gain = DAC_GAIN_M0P2DB; + ipeak = DELTA_I_50MA; + break; + case CLS_H_LOHIFI: + val = 0x00; + if ((IS_CODEC_TYPE(wcd9xxx, WCD9335)) || + (IS_CODEC_TYPE(wcd9xxx, WCD9326))) { + val = 0x08; + gain = DAC_GAIN_M0P2DB; + ipeak = DELTA_I_50MA; + } + break; + case CLS_H_ULP: + val = 0x0C; + break; + case CLS_H_LP: + val = 0x04; + ipeak = DELTA_I_30MA; + break; + default: + return; + }; + + /* + * For tavil set mode to Lower_power for + * CLS_H_LOHIFI and CLS_AB + */ + if ((IS_CODEC_TYPE(wcd9xxx, WCD934X)) && + (mode == CLS_H_LOHIFI || mode == CLS_AB)) + val = 0x04; + + snd_soc_update_bits(codec, WCD9XXX_A_ANA_HPH, 0x0C, val); + if (TASHA_IS_2_0(wcd9xxx)) { + snd_soc_update_bits(codec, WCD9XXX_CLASSH_CTRL_VCL_2, + 0x30, (res_val << 4)); + if (mode != CLS_H_LP) + snd_soc_update_bits(codec, WCD9XXX_HPH_REFBUFF_UHQA_CTL, + 0x07, gain); + snd_soc_update_bits(codec, WCD9XXX_CLASSH_CTRL_CCL_1, + 0xF0, (ipeak << 4)); + } +} + +static void wcd_clsh_set_flyback_vneg_ctl(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_1, 0xE0, + 0x00); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, (0x07 << 5)); + } else { + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_1, 0xE0, + (0x07 << 5)); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, (0x02 << 5)); + } +} + +static void wcd_clsh_set_flyback_current(struct snd_soc_codec *codec, int mode) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!TASHA_IS_2_0(wcd9xxx)) + return; + + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0x0F, 0x0A); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0xF0, 0xA0); + /* Sleep needed to avoid click and pop as per HW requirement */ + usleep_range(100, 110); +} + +static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_codec *codec, + int mode) +{ + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x02, 0x00); +} + +static void wcd_clsh_state_lo(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + dev_err(codec->dev, "%s: LO cannot be in this mode: %d\n", + __func__, mode); + return; + } + + if (is_enable) { + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_vneg_ctl(codec, true); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + } else { + wcd_clsh_buck_ctrl(codec, clsh_d, mode, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_flyback_vneg_ctl(codec, false); + wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_hph_ear(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + int hph_mode = 0; + + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable) { + if (req_state == WCD_CLSH_STATE_EAR) { + /* If HPH is running in CLS-AB when + * EAR comes, let it continue to run + * in Class-AB, no need to enable Class-H + * for EAR. + */ + if (clsh_d->state & WCD_CLSH_STATE_HPHL) + hph_mode = wcd_clsh_get_int_mode(clsh_d, + WCD_CLSH_STATE_HPHL); + else if (clsh_d->state & WCD_CLSH_STATE_HPHR) + hph_mode = wcd_clsh_get_int_mode(clsh_d, + WCD_CLSH_STATE_HPHR); + else + return; + if (hph_mode != CLS_AB && hph_mode != CLS_AB_HIFI + && !is_native_44_1_active(codec)) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + } + + if (is_native_44_1_active(codec)) { + snd_soc_write(codec, WCD9XXX_CDC_CLSH_HPH_V_PA, 0x39); + snd_soc_update_bits(codec, + WCD9XXX_CDC_RX0_RX_PATH_SEC0, + 0x03, 0x00); + if ((req_state == WCD_CLSH_STATE_HPHL) || + (req_state == WCD_CLSH_STATE_HPHR)) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x00); + } + + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + if ((req_state == WCD_CLSH_STATE_HPHL) || + (req_state == WCD_CLSH_STATE_HPHR)) { + wcd_clsh_set_gain_path(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + } + } else { + if (req_state == WCD_CLSH_STATE_EAR) { + /* + * If EAR goes away, disable EAR Channel Enable + * if HPH running in Class-H otherwise + * and if HPH requested mode is CLS_AB then + * no need to disable EAR channel enable bit. + */ + if (wcd_clsh_enable_status(codec)) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x00); + } + + if (is_native_44_1_active(codec)) { + snd_soc_write(codec, WCD9XXX_CDC_CLSH_HPH_V_PA, 0x1C); + snd_soc_update_bits(codec, + WCD9XXX_CDC_RX0_RX_PATH_SEC0, + 0x03, 0x01); + if (((clsh_d->state & WCD_CLSH_STATE_HPH_ST) + != WCD_CLSH_STATE_HPH_ST) && + ((req_state == WCD_CLSH_STATE_HPHL) || + (req_state == WCD_CLSH_STATE_HPHR))) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + } + + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + if ((req_state & WCD_CLSH_STATE_HPH_ST) && + !wcd_clsh_enable_status(codec)) { + /* If Class-H is not enabled when HPH is turned + * off, enable it as EAR is in progress + */ + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } + } +} + +static void wcd_clsh_state_ear_lo(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable && (req_state == WCD_CLSH_STATE_LO)) { + wcd_clsh_set_buck_regulator_mode(codec, CLS_AB); + } else { + if (req_state == WCD_CLSH_STATE_EAR) + goto end; + + /* LO powerdown. + * If EAR Class-H is already enabled, just + * turn on regulator other enable Class-H + * configuration + */ + if (wcd_clsh_enable_status(codec)) { + wcd_clsh_set_buck_regulator_mode(codec, + CLS_H_NORMAL); + goto end; + } + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + wcd_clsh_set_buck_regulator_mode(codec, + CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + } +end: + return; +} + +static void wcd_clsh_state_hph_lo(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + int hph_mode = 0; + + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable) { + /* + * If requested state is LO, put regulator + * in class-AB or if requested state is HPH, + * which means LO is already enabled, keep + * the regulator config the same at class-AB + * and just set the power modes for flyback + * and buck. + */ + if (req_state == WCD_CLSH_STATE_LO) + wcd_clsh_set_buck_regulator_mode(codec, CLS_AB); + else { + if (!wcd_clsh_enable_status(codec)) { + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_set_flyback_vneg_ctl(codec, false); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_hph_mode(codec, mode); + wcd_clsh_set_gain_path(codec, mode); + } else { + dev_dbg(codec->dev, "%s:clsh is already enabled\n", + __func__); + } + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + } + } else { + if ((req_state == WCD_CLSH_STATE_HPHL) || + (req_state == WCD_CLSH_STATE_HPHR)) { + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + /* + * If HPH is powering down first, then disable clsh, + * set the buck/flyback mode to default and keep the + * regulator at Class-AB + */ + if ((clsh_d->state & WCD_CLSH_STATE_HPH_ST) + != WCD_CLSH_STATE_HPH_ST) { + wcd_enable_clsh_block(codec, clsh_d, false); + wcd_clsh_set_flyback_vneg_ctl(codec, true); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } + } else { + /* LO powerdown. + * If HPH mode also is CLS-AB, no need + * to turn-on class-H, otherwise enable + * Class-H configuration. + */ + if (clsh_d->state & WCD_CLSH_STATE_HPHL) + hph_mode = wcd_clsh_get_int_mode(clsh_d, + WCD_CLSH_STATE_HPHL); + else if (clsh_d->state & WCD_CLSH_STATE_HPHR) + hph_mode = wcd_clsh_get_int_mode(clsh_d, + WCD_CLSH_STATE_HPHR); + else + return; + dev_dbg(codec->dev, "%s: hph_mode = %d\n", __func__, + hph_mode); + + if ((hph_mode == CLS_AB) || + (hph_mode == CLS_AB_HIFI) || + (hph_mode == CLS_NONE)) + goto end; + + /* + * If Class-H is already enabled (HPH ON and then + * LO ON), no need to turn on again, just set the + * regulator mode. + */ + if (wcd_clsh_enable_status(codec)) { + wcd_clsh_set_buck_regulator_mode(codec, + hph_mode); + goto end; + } else { + dev_dbg(codec->dev, "%s: clsh is not enabled\n", + __func__); + } + + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + wcd_clsh_set_buck_regulator_mode(codec, + hph_mode); + if (clsh_d->state & WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (clsh_d->state & WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + wcd_clsh_set_hph_mode(codec, hph_mode); + } + } +end: + return; +} + +static void wcd_clsh_state_hph_st(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_AB || mode == CLS_AB_HIFI) + return; + + if (is_enable) { + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + } else { + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + } +} + +static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_H_NORMAL) { + dev_err(codec->dev, "%s: Normal mode not applicable for hph_r\n", + __func__); + return; + } + + if (is_enable) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + wcd_enable_clsh_block(codec, clsh_d, true); + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + } + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_gm3_boost_disable(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_hph_mode(codec, mode); + wcd_clsh_set_gain_path(codec, mode); + } else { + wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); + + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + wcd_enable_clsh_block(codec, clsh_d, false); + } + /* buck and flyback set to default mode and disable */ + wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL); + wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_H_NORMAL) { + dev_err(codec->dev, "%s: Normal mode not applicable for hph_l\n", + __func__); + return; + } + + if (is_enable) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + wcd_enable_clsh_block(codec, clsh_d, true); + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + } + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_gm3_boost_disable(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_hph_mode(codec, mode); + wcd_clsh_set_gain_path(codec, mode); + } else { + wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); + + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + wcd_enable_clsh_block(codec, clsh_d, false); + } + /* set buck and flyback to Default Mode */ + wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL); + wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_ear(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode != CLS_H_NORMAL) { + dev_err(codec->dev, "%s: mode: %s cannot be used for EAR\n", + __func__, mode_to_str(mode)); + return; + } + + if (is_enable) { + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + } else { + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x00); + wcd_enable_clsh_block(codec, clsh_d, false); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_err(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + char msg[128]; + + dev_err(codec->dev, + "%s Wrong request for class H state machine requested to %s %s", + __func__, is_enable ? "enable" : "disable", + state_to_str(req_state, msg, sizeof(msg))); + WARN_ON(1); +} + +/* + * Function: wcd_clsh_is_state_valid + * Params: state + * Description: + * Provides information on valid states of Class H configuration + */ +static bool wcd_clsh_is_state_valid(u8 state) +{ + switch (state) { + case WCD_CLSH_STATE_IDLE: + case WCD_CLSH_STATE_EAR: + case WCD_CLSH_STATE_HPHL: + case WCD_CLSH_STATE_HPHR: + case WCD_CLSH_STATE_HPH_ST: + case WCD_CLSH_STATE_LO: + case WCD_CLSH_STATE_HPHL_EAR: + case WCD_CLSH_STATE_HPHR_EAR: + case WCD_CLSH_STATE_HPH_ST_EAR: + case WCD_CLSH_STATE_HPHL_LO: + case WCD_CLSH_STATE_HPHR_LO: + case WCD_CLSH_STATE_HPH_ST_LO: + return true; + default: + return false; + }; +} + +/* + * Function: wcd_clsh_fsm + * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event + * Description: + * This function handles PRE DAC and POST DAC conditions of different devices + * and updates class H configuration of different combination of devices + * based on validity of their states. cdc_clsh_d will contain current + * class h state information + */ +void wcd_clsh_fsm(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode) +{ + u8 old_state, new_state; + char msg0[128], msg1[128]; + + switch (clsh_event) { + case WCD_CLSH_EVENT_PRE_DAC: + old_state = cdc_clsh_d->state; + new_state = old_state | req_state; + + if (!wcd_clsh_is_state_valid(new_state)) { + dev_err(codec->dev, + "%s: Class-H not a valid new state: %s\n", + __func__, + state_to_str(new_state, msg0, sizeof(msg0))); + return; + } + if (new_state == old_state) { + dev_err(codec->dev, + "%s: Class-H already in requested state: %s\n", + __func__, + state_to_str(new_state, msg0, sizeof(msg0))); + return; + } + cdc_clsh_d->state = new_state; + wcd_clsh_set_int_mode(cdc_clsh_d, req_state, int_mode); + (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state, + CLSH_REQ_ENABLE, int_mode); + dev_dbg(codec->dev, + "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str(old_state, msg0, sizeof(msg0)), + state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1))); + break; + case WCD_CLSH_EVENT_POST_PA: + old_state = cdc_clsh_d->state; + new_state = old_state & (~req_state); + if (new_state < NUM_CLSH_STATES_V2) { + if (!wcd_clsh_is_state_valid(old_state)) { + dev_err(codec->dev, + "%s:Invalid old state:%s\n", + __func__, + state_to_str(old_state, msg0, + sizeof(msg0))); + return; + } + if (new_state == old_state) { + dev_err(codec->dev, + "%s: Class-H already in requested state: %s\n", + __func__, + state_to_str(new_state, msg0, + sizeof(msg0))); + return; + } + (*clsh_state_fp[old_state]) (codec, cdc_clsh_d, + req_state, CLSH_REQ_DISABLE, + int_mode); + cdc_clsh_d->state = new_state; + wcd_clsh_set_int_mode(cdc_clsh_d, req_state, CLS_NONE); + dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str(old_state, msg0, + sizeof(msg0)), + state_to_str(cdc_clsh_d->state, msg1, + sizeof(msg1))); + } + break; + }; +} + +int wcd_clsh_get_clsh_state(struct wcd_clsh_cdc_data *clsh) +{ + return clsh->state; +} +EXPORT_SYMBOL(wcd_clsh_get_clsh_state); + +void wcd_clsh_init(struct wcd_clsh_cdc_data *clsh) +{ + int i; + + clsh->state = WCD_CLSH_STATE_IDLE; + + for (i = 0; i < NUM_CLSH_STATES_V2; i++) + clsh_state_fp[i] = wcd_clsh_state_err; + + clsh_state_fp[WCD_CLSH_STATE_EAR] = wcd_clsh_state_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHL] = + wcd_clsh_state_hph_l; + clsh_state_fp[WCD_CLSH_STATE_HPHR] = + wcd_clsh_state_hph_r; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST] = + wcd_clsh_state_hph_st; + clsh_state_fp[WCD_CLSH_STATE_LO] = wcd_clsh_state_lo; + clsh_state_fp[WCD_CLSH_STATE_HPHL_EAR] = + wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHR_EAR] = + wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST_EAR] = + wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHL_LO] = wcd_clsh_state_hph_lo; + clsh_state_fp[WCD_CLSH_STATE_HPHR_LO] = wcd_clsh_state_hph_lo; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST_LO] = + wcd_clsh_state_hph_lo; + clsh_state_fp[WCD_CLSH_STATE_EAR_LO] = wcd_clsh_state_ear_lo; + /* Set interpolaotr modes to NONE */ + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_EAR, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHL, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHR, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_LO, CLS_NONE); + clsh->flyback_users = 0; + clsh->buck_users = 0; + clsh->clsh_users = 0; +} +EXPORT_SYMBOL(wcd_clsh_init); + +MODULE_DESCRIPTION("WCD9XXX Common Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9xxx-common-v2.h b/sound/soc/codecs/wcd9xxx-common-v2.h new file mode 100644 index 0000000000000000000000000000000000000000..ee7e587b3f247ea3679baea5c6c0df660bda6888 --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-common-v2.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _WCD9XXX_COMMON_V2 + +#define _WCD9XXX_COMMON_V2 + +#define CLSH_REQ_ENABLE true +#define CLSH_REQ_DISABLE false + +#define WCD_CLSH_EVENT_PRE_DAC 0x01 +#define WCD_CLSH_EVENT_POST_PA 0x02 +#define MAX_VBAT_MONITOR_WRITES 17 +/* + * Basic states for Class H state machine. + * represented as a bit mask within a u8 data type + * bit 0: EAR mode + * bit 1: HPH Left mode + * bit 2: HPH Right mode + * bit 3: Lineout mode + */ +#define WCD_CLSH_STATE_IDLE 0x00 +#define WCD_CLSH_STATE_EAR (0x01 << 0) +#define WCD_CLSH_STATE_HPHL (0x01 << 1) +#define WCD_CLSH_STATE_HPHR (0x01 << 2) +#define WCD_CLSH_STATE_LO (0x01 << 3) +#define WCD_CLSH_STATE_MAX 4 +#define NUM_CLSH_STATES_V2 (0x01 << WCD_CLSH_STATE_MAX) + + +/* Derived State: Bits 1 and 2 should be set for Headphone stereo */ +#define WCD_CLSH_STATE_HPH_ST (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_HPHR) + +#define WCD_CLSH_STATE_HPHL_LO (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_LO) +#define WCD_CLSH_STATE_HPHR_LO (WCD_CLSH_STATE_HPHR | \ + WCD_CLSH_STATE_LO) +#define WCD_CLSH_STATE_HPH_ST_LO (WCD_CLSH_STATE_HPH_ST | \ + WCD_CLSH_STATE_LO) +#define WCD_CLSH_STATE_EAR_LO (WCD_CLSH_STATE_EAR | \ + WCD_CLSH_STATE_LO) +#define WCD_CLSH_STATE_HPHL_EAR (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_EAR) +#define WCD_CLSH_STATE_HPHR_EAR (WCD_CLSH_STATE_HPHR | \ + WCD_CLSH_STATE_EAR) +#define WCD_CLSH_STATE_HPH_ST_EAR (WCD_CLSH_STATE_HPH_ST | \ + WCD_CLSH_STATE_EAR) + +enum { + CLS_H_NORMAL = 0, /* Class-H Default */ + CLS_H_HIFI, /* Class-H HiFi */ + CLS_H_LP, /* Class-H Low Power */ + CLS_AB, /* Class-AB Low HIFI*/ + CLS_H_LOHIFI, /* LoHIFI */ + CLS_H_ULP, /* Ultra Low power */ + CLS_AB_HIFI, /* Class-AB */ + CLS_NONE, /* None of the above modes */ +}; + +/* Class H data that the codec driver will maintain */ +struct wcd_clsh_cdc_data { + u8 state; + int flyback_users; + int buck_users; + int clsh_users; + int interpolator_modes[WCD_CLSH_STATE_MAX]; +}; + +struct wcd_mad_audio_header { + u32 reserved[3]; + u32 num_reg_cfg; +}; + +struct wcd_mad_microphone_info { + uint8_t input_microphone; + uint8_t cycle_time; + uint8_t settle_time; + uint8_t padding; +} __packed; + +struct wcd_mad_micbias_info { + uint8_t micbias; + uint8_t k_factor; + uint8_t external_bypass_capacitor; + uint8_t internal_biasing; + uint8_t cfilter; + uint8_t padding[3]; +} __packed; + +struct wcd_mad_rms_audio_beacon_info { + uint8_t rms_omit_samples; + uint8_t rms_comp_time; + uint8_t detection_mechanism; + uint8_t rms_diff_threshold; + uint8_t rms_threshold_lsb; + uint8_t rms_threshold_msb; + uint8_t padding[2]; + uint8_t iir_coefficients[36]; +} __packed; + +struct wcd_mad_rms_ultrasound_info { + uint8_t rms_comp_time; + uint8_t detection_mechanism; + uint8_t rms_diff_threshold; + uint8_t rms_threshold_lsb; + uint8_t rms_threshold_msb; + uint8_t padding[3]; + uint8_t iir_coefficients[36]; +} __packed; + +struct wcd_mad_audio_cal { + uint32_t version; + struct wcd_mad_microphone_info microphone_info; + struct wcd_mad_micbias_info micbias_info; + struct wcd_mad_rms_audio_beacon_info audio_info; + struct wcd_mad_rms_audio_beacon_info beacon_info; + struct wcd_mad_rms_ultrasound_info ultrasound_info; +} __packed; + +struct wcd9xxx_anc_header { + u32 reserved[3]; + u32 num_anc_slots; +}; + +struct vbat_monitor_reg { + u32 size; + u32 writes[MAX_VBAT_MONITOR_WRITES]; +} __packed; + +struct wcd_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +extern void wcd_clsh_fsm(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode); + +extern void wcd_clsh_init(struct wcd_clsh_cdc_data *clsh); +extern int wcd_clsh_get_clsh_state(struct wcd_clsh_cdc_data *clsh); +extern void wcd_clsh_imped_config(struct snd_soc_codec *codec, int imped, + bool reset); + +enum { + RESERVED = 0, + AANC_LPF_FF_FB = 1, + AANC_LPF_COEFF_MSB, + AANC_LPF_COEFF_LSB, + HW_MAD_AUDIO_ENABLE, + HW_MAD_ULTR_ENABLE, + HW_MAD_BEACON_ENABLE, + HW_MAD_AUDIO_SLEEP_TIME, + HW_MAD_ULTR_SLEEP_TIME, + HW_MAD_BEACON_SLEEP_TIME, + HW_MAD_TX_AUDIO_SWITCH_OFF, + HW_MAD_TX_ULTR_SWITCH_OFF, + HW_MAD_TX_BEACON_SWITCH_OFF, + MAD_AUDIO_INT_DEST_SELECT_REG, + MAD_ULT_INT_DEST_SELECT_REG, + MAD_BEACON_INT_DEST_SELECT_REG, + MAD_CLIP_INT_DEST_SELECT_REG, + VBAT_INT_DEST_SELECT_REG, + MAD_AUDIO_INT_MASK_REG, + MAD_ULT_INT_MASK_REG, + MAD_BEACON_INT_MASK_REG, + MAD_CLIP_INT_MASK_REG, + VBAT_INT_MASK_REG, + MAD_AUDIO_INT_STATUS_REG, + MAD_ULT_INT_STATUS_REG, + MAD_BEACON_INT_STATUS_REG, + MAD_CLIP_INT_STATUS_REG, + VBAT_INT_STATUS_REG, + MAD_AUDIO_INT_CLEAR_REG, + MAD_ULT_INT_CLEAR_REG, + MAD_BEACON_INT_CLEAR_REG, + MAD_CLIP_INT_CLEAR_REG, + VBAT_INT_CLEAR_REG, + SB_PGD_PORT_TX_WATERMARK_N, + SB_PGD_PORT_TX_ENABLE_N, + SB_PGD_PORT_RX_WATERMARK_N, + SB_PGD_PORT_RX_ENABLE_N, + SB_PGD_TX_PORTn_MULTI_CHNL_0, + SB_PGD_TX_PORTn_MULTI_CHNL_1, + SB_PGD_RX_PORTn_MULTI_CHNL_0, + SB_PGD_RX_PORTn_MULTI_CHNL_1, + AANC_FF_GAIN_ADAPTIVE, + AANC_FFGAIN_ADAPTIVE_EN, + AANC_GAIN_CONTROL, + SPKR_CLIP_PIPE_BANK_SEL, + SPKR_CLIPDET_VAL0, + SPKR_CLIPDET_VAL1, + SPKR_CLIPDET_VAL2, + SPKR_CLIPDET_VAL3, + SPKR_CLIPDET_VAL4, + SPKR_CLIPDET_VAL5, + SPKR_CLIPDET_VAL6, + SPKR_CLIPDET_VAL7, + VBAT_RELEASE_INT_DEST_SELECT_REG, + VBAT_RELEASE_INT_MASK_REG, + VBAT_RELEASE_INT_STATUS_REG, + VBAT_RELEASE_INT_CLEAR_REG, + MAD2_CLIP_INT_DEST_SELECT_REG, + MAD2_CLIP_INT_MASK_REG, + MAD2_CLIP_INT_STATUS_REG, + MAD2_CLIP_INT_CLEAR_REG, + SPKR2_CLIP_PIPE_BANK_SEL, + SPKR2_CLIPDET_VAL0, + SPKR2_CLIPDET_VAL1, + SPKR2_CLIPDET_VAL2, + SPKR2_CLIPDET_VAL3, + SPKR2_CLIPDET_VAL4, + SPKR2_CLIPDET_VAL5, + SPKR2_CLIPDET_VAL6, + SPKR2_CLIPDET_VAL7, + MAX_CFG_REGISTERS, +}; + +#endif diff --git a/sound/soc/codecs/wcd9xxx-common.c b/sound/soc/codecs/wcd9xxx-common.c new file mode 100644 index 0000000000000000000000000000000000000000..7b2e68a211b009dbe5ab43d48a4694f6d1e6aac2 --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-common.c @@ -0,0 +1,1480 @@ +/* Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include "wcd9xxx-common.h" + +#define CLSH_COMPUTE_EAR 0x01 +#define CLSH_COMPUTE_HPH_L 0x02 +#define CLSH_COMPUTE_HPH_R 0x03 + +#define BUCK_VREF_0P494V 0x3F +#define BUCK_VREF_2V 0xFF +#define BUCK_VREF_0P494V 0x3F +#define BUCK_VREF_1P8V 0xE6 + +#define BUCK_SETTLE_TIME_US 50 +#define NCP_SETTLE_TIME_US 50 + +#define MAX_IMPED_PARAMS 13 + +#define USLEEP_RANGE_MARGIN_US 100 + +struct wcd9xxx_imped_val { + u32 imped_val; + u8 index; +}; + +static const struct wcd9xxx_reg_mask_val imped_table[][MAX_IMPED_PARAMS] = { + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x46}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9B}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x15}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x04}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0C}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x47}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9B}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x15}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x05}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0C}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x49}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x07}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x12}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x35}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x4E}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x06}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0E}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x49}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x16}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x17}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x5F}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xCF}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x06}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0F}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x59}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x15}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xCE}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xBD}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x07}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x10}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x66}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9A}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2E}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xBD}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA6}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x07}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x11}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x79}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x37}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA6}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAD}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x08}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x12}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x76}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x4E}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAD}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x09}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x12}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x78}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x12}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xD0}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0A}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x13}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x7A}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xB7}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0B}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x14}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x60}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x09}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA4}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1F}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0C}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x14}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x79}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x17}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1F}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0D}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x15}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x78}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x16}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0E}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x16}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x89}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x40}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x10}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x16}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x97}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xD0}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x12}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x17}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x8A}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xB7}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x10}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x13}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x17}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x8A}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x07}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA4}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x15}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x18}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x9A}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x08}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x27}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x18}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x19}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x8B}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x18}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x20}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2E}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x1A}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x19}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x9A}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x17}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2E}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2D}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x1D}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x1A}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0xA9}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2D}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x1F}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x19}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0xB9}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x10}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x23}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x18}, + }, + { + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0xA9}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x07}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x27}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x35}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x26}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x16}, + }, +}; + +static const struct wcd9xxx_imped_val imped_index[] = { + {4000, 0}, + {4500, 1}, + {5000, 2}, + {5500, 3}, + {6000, 4}, + {6500, 5}, + {7000, 6}, + {7700, 7}, + {8470, 8}, + {9317, 9}, + {10248, 10}, + {11273, 11}, + {12400, 12}, + {13641, 13}, + {15005, 14}, + {16505, 15}, + {18156, 16}, + {19971, 17}, + {21969, 18}, + {24165, 19}, + {26582, 20}, + {29240, 21}, + {32164, 22}, +}; + +static inline void +wcd9xxx_enable_clsh_block(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, bool enable) +{ + if ((enable && ++clsh_d->clsh_users == 1) || + (!enable && --clsh_d->clsh_users == 0)) + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL, + 0x01, enable ? 0x01 : 0x00); + dev_dbg(codec->dev, "%s: clsh_users %d, enable %d", __func__, + clsh_d->clsh_users, enable); +} + +static inline void wcd9xxx_enable_anc_delay( + struct snd_soc_codec *codec, + bool on) +{ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL, + 0x02, on ? 0x02 : 0x00); +} + +static inline void +wcd9xxx_enable_buck(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, bool enable) +{ + if ((enable && ++clsh_d->buck_users == 1) || + (!enable && --clsh_d->buck_users == 0)) + snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1, + 0x80, enable ? 0x80 : 0x00); + dev_dbg(codec->dev, "%s: buck_users %d, enable %d", __func__, + clsh_d->buck_users, enable); +} + +static void (*clsh_state_fp[NUM_CLSH_STATES])(struct snd_soc_codec *, + struct wcd9xxx_clsh_cdc_data *, + u8 req_state, bool req_type); + +static const char *state_to_str(u8 state, char *buf, size_t buflen) +{ + int i; + int cnt = 0; + /* + * This array of strings should match with enum wcd9xxx_clsh_state_bit. + */ + static const char *const states[] = { + "STATE_EAR", + "STATE_HPH_L", + "STATE_HPH_R", + "STATE_LO", + }; + + if (state == WCD9XXX_CLSH_STATE_IDLE) { + snprintf(buf, buflen, "[STATE_IDLE]"); + goto done; + } + + buf[0] = '\0'; + for (i = 0; i < ARRAY_SIZE(states); i++) { + if (!(state & (1 << i))) + continue; + cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf, + buf[0] == '\0' ? "[" : "|", + states[i]); + } + if (cnt > 0) + strlcat(buf + cnt, "]", buflen); + +done: + if (buf[0] == '\0') + snprintf(buf, buflen, "[STATE_UNKNOWN]"); + return buf; +} + +static void wcd9xxx_cfg_clsh_param_common( + struct snd_soc_codec *codec) +{ + int i; + const struct wcd9xxx_reg_mask_val reg_set[] = { + {WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, 0x3 << 0, 0}, + {WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, 0x3 << 2, 1 << 2}, + {WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, (0x1 << 4), 0}, + {WCD9XXX_A_CDC_CLSH_B2_CTL, (0x3 << 0), 0x01}, + {WCD9XXX_A_CDC_CLSH_B2_CTL, (0x3 << 2), (0x01 << 2)}, + {WCD9XXX_A_CDC_CLSH_B2_CTL, (0xf << 4), (0x03 << 4)}, + {WCD9XXX_A_CDC_CLSH_B3_CTL, (0xf << 4), (0x03 << 4)}, + {WCD9XXX_A_CDC_CLSH_B3_CTL, (0xf << 0), (0x0B)}, + {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 5), (0x01 << 5)}, + {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 1), (0x01 << 1)}, + }; + + for (i = 0; i < ARRAY_SIZE(reg_set); i++) + snd_soc_update_bits(codec, reg_set[i].reg, reg_set[i].mask, + reg_set[i].val); + + dev_dbg(codec->dev, "%s: Programmed class H controller common parameters", + __func__); +} + +static void wcd9xxx_chargepump_request(struct snd_soc_codec *codec, bool on) +{ + static int cp_count; + + if (on && (++cp_count == 1)) { + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL, + 0x01, 0x01); + dev_dbg(codec->dev, "%s: Charge Pump enabled, count = %d\n", + __func__, cp_count); + } else if (!on) { + if (--cp_count < 0) { + dev_dbg(codec->dev, + "%s: Unbalanced disable for charge pump\n", + __func__); + if (snd_soc_read(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL) & + 0x01) { + dev_dbg(codec->dev, + "%s: Actual chargepump is ON\n", + __func__); + } + cp_count = 0; + WARN_ON(1); + } + + if (cp_count == 0) { + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL, + 0x01, 0x00); + dev_dbg(codec->dev, + "%s: Charge pump disabled, count = %d\n", + __func__, cp_count); + } + } +} + +void wcd9xxx_enable_high_perf_mode(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + u8 uhqa_mode, u8 req_state, bool req_type) +{ + dev_dbg(codec->dev, "%s: users fclk8 %d, fclk5 %d", __func__, + clsh_d->ncp_users[NCP_FCLK_LEVEL_8], + clsh_d->ncp_users[NCP_FCLK_LEVEL_5]); + + if (req_type == WCD9XXX_CLSAB_REQ_ENABLE) { + clsh_d->ncp_users[NCP_FCLK_LEVEL_8]++; + snd_soc_write(codec, WCD9XXX_A_RX_HPH_BIAS_PA, + WCD9XXX_A_RX_HPH_BIAS_PA__POR); + snd_soc_write(codec, WCD9XXX_A_RX_HPH_L_PA_CTL, 0x48); + snd_soc_write(codec, WCD9XXX_A_RX_HPH_R_PA_CTL, 0x48); + if (uhqa_mode) + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CHOP_CTL, + 0x20, 0x00); + wcd9xxx_chargepump_request(codec, true); + wcd9xxx_enable_anc_delay(codec, true); + wcd9xxx_enable_buck(codec, clsh_d, false); + if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] > 0) + snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, + 0x0F, 0x08); + snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x30, 0x30); + + /* Enable NCP and wait until settles down */ + if (snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN, 0x01, 0x01)) + usleep_range(NCP_SETTLE_TIME_US, NCP_SETTLE_TIME_US+10); + } else { + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CHOP_CTL, + 0x20, 0x20); + snd_soc_write(codec, WCD9XXX_A_RX_HPH_L_PA_CTL, + WCD9XXX_A_RX_HPH_L_PA_CTL__POR); + snd_soc_write(codec, WCD9XXX_A_RX_HPH_R_PA_CTL, + WCD9XXX_A_RX_HPH_R_PA_CTL__POR); + snd_soc_write(codec, WCD9XXX_A_RX_HPH_BIAS_PA, 0x57); + wcd9xxx_enable_buck(codec, clsh_d, true); + wcd9xxx_chargepump_request(codec, false); + wcd9xxx_enable_anc_delay(codec, false); + clsh_d->ncp_users[NCP_FCLK_LEVEL_8]--; + if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] == 0 && + clsh_d->ncp_users[NCP_FCLK_LEVEL_5] == 0) + snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN, + 0x01, 0x00); + else if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] == 0) + snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, + 0x0F, 0x05); + } + dev_dbg(codec->dev, "%s: leave\n", __func__); +} +EXPORT_SYMBOL(wcd9xxx_enable_high_perf_mode); + +static int get_impedance_index(u32 imped) +{ + int i = 0; + + if (imped < imped_index[i].imped_val) { + pr_debug("%s, detected impedance is less than 4 Ohm\n", + __func__); + goto ret; + } + if (imped >= imped_index[ARRAY_SIZE(imped_index) - 1].imped_val) { + pr_debug("%s, detected impedance is greater than 32164 Ohm\n", + __func__); + i = ARRAY_SIZE(imped_index) - 1; + goto ret; + } + for (i = 0; i < ARRAY_SIZE(imped_index) - 1; i++) { + if (imped >= imped_index[i].imped_val && + imped < imped_index[i + 1].imped_val) + break; + } +ret: + pr_debug("%s: selected impedance index = %d\n", + __func__, imped_index[i].index); + return imped_index[i].index; +} + +void wcd9xxx_clsh_imped_config(struct snd_soc_codec *codec, + int imped) +{ + int i = 0; + int index = 0; + + index = get_impedance_index(imped); + if (index >= ARRAY_SIZE(imped_index)) { + pr_err("%s, invalid imped = %d\n", __func__, imped); + return; + } + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_write(codec, imped_table[index][i].reg, + imped_table[index][i].val); +} + +static void wcd9xxx_clsh_comp_req(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + int compute_pa, bool on) +{ + u8 shift; + + if (compute_pa == CLSH_COMPUTE_EAR) { + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL, 0x10, + (on ? 0x10 : 0)); + } else { + if (compute_pa == CLSH_COMPUTE_HPH_L) { + shift = 3; + } else if (compute_pa == CLSH_COMPUTE_HPH_R) { + shift = 2; + } else { + dev_dbg(codec->dev, + "%s: classh computation request is incorrect\n", + __func__); + return; + } + + if (on) + wcd9xxx_resmgr_add_cond_update_bits(clsh_d->resmgr, + WCD9XXX_COND_HPH, + WCD9XXX_A_CDC_CLSH_B1_CTL, + shift, false); + else + wcd9xxx_resmgr_rm_cond_update_bits(clsh_d->resmgr, + WCD9XXX_COND_HPH, + WCD9XXX_A_CDC_CLSH_B1_CTL, + shift, false); + } +} + +int wcd9xxx_soc_update_bits_push(struct snd_soc_codec *codec, + struct list_head *list, + uint16_t reg, uint8_t mask, + uint8_t value, int delay) +{ + int rc; + struct wcd9xxx_register_save_node *node; + + node = kmalloc(sizeof(*node), GFP_KERNEL); + if (unlikely(!node)) { + pr_err("%s: Not enough memory\n", __func__); + return -ENOMEM; + } + node->reg = reg; + node->value = snd_soc_read(codec, reg); + list_add(&node->lh, list); + if (mask == 0xFF) + rc = snd_soc_write(codec, reg, value); + else + rc = snd_soc_update_bits(codec, reg, mask, value); + if (delay) + usleep_range(delay, delay + USLEEP_RANGE_MARGIN_US); + return rc; +} +EXPORT_SYMBOL(wcd9xxx_soc_update_bits_push); + +void wcd9xxx_restore_registers(struct snd_soc_codec *codec, + struct list_head *lh) +{ + struct wcd9xxx_register_save_node *node, *nodetmp; + + list_for_each_entry_safe(node, nodetmp, lh, lh) { + snd_soc_write(codec, node->reg, node->value); + list_del(&node->lh); + kfree(node); + } +} +EXPORT_SYMBOL(wcd9xxx_restore_registers); + +static void wcd9xxx_dynamic_bypass_buck_ctrl_lo(struct snd_soc_codec *cdc, + bool enable) +{ + int i; + const struct wcd9xxx_reg_mask_val reg_set[] = { + {WCD9XXX_A_BUCK_MODE_3, (0x1 << 3), (enable << 3)}, + {WCD9XXX_A_BUCK_MODE_5, enable ? 0xFF : 0x02, 0x02}, + {WCD9XXX_A_BUCK_MODE_5, 0x1, 0x01} + }; + + if (!enable) { + snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_1, + (0x1 << 3), 0x00); + snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_4, + 0xFF, BUCK_VREF_2V); + } + for (i = 0; i < ARRAY_SIZE(reg_set); i++) + snd_soc_update_bits(cdc, reg_set[i].reg, reg_set[i].mask, + reg_set[i].val); + + /* 50us sleep is reqd. as per the class H HW design sequence */ + usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10); +} + +static void wcd9xxx_dynamic_bypass_buck_ctrl(struct snd_soc_codec *cdc, + bool enable) +{ + int i; + const struct wcd9xxx_reg_mask_val reg_set[] = { + {WCD9XXX_A_BUCK_MODE_3, (0x1 << 3), (enable << 3)}, + {WCD9XXX_A_BUCK_MODE_5, (0x1 << 1), ((!enable) << 1)}, + {WCD9XXX_A_BUCK_MODE_5, 0x1, !enable} + }; + if (!enable) { + snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_1, + (0x1 << 3), 0x00); + snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_4, + 0xFF, BUCK_VREF_2V); + } + for (i = 0; i < ARRAY_SIZE(reg_set); i++) + snd_soc_update_bits(cdc, reg_set[i].reg, reg_set[i].mask, + reg_set[i].val); + + /* 50us sleep is reqd. as per the class H HW design sequence */ + usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10); +} + +static void wcd9xxx_set_buck_mode(struct snd_soc_codec *codec, u8 buck_vref) +{ + int i; + const struct wcd9xxx_reg_mask_val reg_set[] = { + {WCD9XXX_A_BUCK_MODE_5, 0x02, 0x02}, + {WCD9XXX_A_BUCK_MODE_4, 0xFF, buck_vref}, + {WCD9XXX_A_BUCK_MODE_1, 0x04, 0x04}, + {WCD9XXX_A_BUCK_MODE_3, 0x04, 0x00}, + {WCD9XXX_A_BUCK_MODE_3, 0x08, 0x00}, + }; + + for (i = 0; i < ARRAY_SIZE(reg_set); i++) + snd_soc_update_bits(codec, reg_set[i].reg, + reg_set[i].mask, reg_set[i].val); + + dev_dbg(codec->dev, "%s: Done\n", __func__); + usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US + 10); +} + + +/* This will be called for all states except Lineout */ +static void wcd9xxx_clsh_enable_post_pa(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *cdc_clsh_d) +{ + int i; + const struct wcd9xxx_reg_mask_val reg_set[] = { + {WCD9XXX_A_BUCK_MODE_5, 0x02, 0x00}, + {WCD9XXX_A_NCP_STATIC, 0x20, 0x00}, + {WCD9XXX_A_BUCK_MODE_3, 0x04, 0x04}, + }; + + for (i = 0; i < ARRAY_SIZE(reg_set); i++) + snd_soc_update_bits(codec, reg_set[i].reg, + reg_set[i].mask, reg_set[i].val); + + if (!cdc_clsh_d->is_dynamic_vdd_cp) + snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_3, + 0x08, 0x08); + + dev_dbg(codec->dev, "%s: completed clsh mode settings after PA enable\n", + __func__); + +} + +static void wcd9xxx_set_fclk_get_ncp(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + enum ncp_fclk_level fclk_level) +{ + clsh_d->ncp_users[fclk_level]++; + + pr_debug("%s: enter ncp type %d users fclk8 %d, fclk5 %d\n", __func__, + fclk_level, clsh_d->ncp_users[NCP_FCLK_LEVEL_8], + clsh_d->ncp_users[NCP_FCLK_LEVEL_5]); + + snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x10, 0x00); + /* fclk level 8 dominates level 5 */ + if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] > 0) + snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x0F, 0x08); + else if (clsh_d->ncp_users[NCP_FCLK_LEVEL_5] > 0) + snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x0F, 0x05); + else + WARN_ONCE(1, "Unexpected users %d,%d\n", + clsh_d->ncp_users[NCP_FCLK_LEVEL_8], + clsh_d->ncp_users[NCP_FCLK_LEVEL_5]); + snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x20, 0x20); + + /* enable NCP and wait until settles down */ + if (snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN, 0x01, 0x01)) + usleep_range(NCP_SETTLE_TIME_US, NCP_SETTLE_TIME_US + 50); + pr_debug("%s: leave\n", __func__); +} + +static void wcd9xxx_set_fclk_put_ncp(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + enum ncp_fclk_level fclk_level) +{ + clsh_d->ncp_users[fclk_level]--; + + pr_debug("%s: enter ncp type %d users fclk8 %d, fclk5 %d\n", __func__, + fclk_level, clsh_d->ncp_users[NCP_FCLK_LEVEL_8], + clsh_d->ncp_users[NCP_FCLK_LEVEL_5]); + + if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] == 0 && + clsh_d->ncp_users[NCP_FCLK_LEVEL_5] == 0) + snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN, 0x01, 0x00); + else if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] == 0) + /* if dominating level 8 has gone, switch to 5 */ + snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x0F, 0x05); + pr_debug("%s: leave\n", __func__); +} + +static void wcd9xxx_cfg_clsh_param_ear(struct snd_soc_codec *codec) +{ + int i; + const struct wcd9xxx_reg_mask_val reg_set[] = { + {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 7), 0}, + {WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR, (0x3f << 0), 0x0D}, + {WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR, (0x3f << 0), 0x3A}, + + /* Under assumption that EAR load is 10.7ohm */ + {WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD, (0x3f << 0), 0x26}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD, (0x3f << 0), 0x2C}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L, 0xff, 0xA9}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U, 0xff, 0x07}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, (0x1 << 7), 0}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, (0xf << 0), 0x08}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1b}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2d}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x36}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x37}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + }; + + for (i = 0; i < ARRAY_SIZE(reg_set); i++) + snd_soc_update_bits(codec, reg_set[i].reg, + reg_set[i].mask, reg_set[i].val); + + dev_dbg(codec->dev, "%s: Programmed Class H controller EAR specific params\n", + __func__); +} + +static void wcd9xxx_cfg_clsh_param_hph(struct snd_soc_codec *codec) +{ + int i; + const struct wcd9xxx_reg_mask_val reg_set[] = { + {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 6), 0}, + {WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH, 0x3f, 0x0D}, + {WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH, 0x3f, 0x1D}, + + /* Under assumption that HPH load is 16ohm per channel */ + {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0x3f, 0x13}, + {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0x1f, 0x19}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x97}, + {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, (0x1 << 7), 0}, + {WCD9XXX_A_CDC_CLSH_K_ADDR, 0x0f, 0}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25}, + {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00}, + }; + + for (i = 0; i < ARRAY_SIZE(reg_set); i++) + snd_soc_update_bits(codec, reg_set[i].reg, reg_set[i].mask, + reg_set[i].val); + dev_dbg(codec->dev, "%s: Programmed Class H controller HPH specific params\n", + __func__); +} + +static void wcd9xxx_ncp_bypass_enable(struct snd_soc_codec *cdc, bool enable) +{ + snd_soc_update_bits(cdc, WCD9XXX_A_NCP_STATIC, 0x10, (enable << 4)); + /* 50us sleep is reqd. as per the class H HW design sequence */ + usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10); +} + +static void wcd9xxx_clsh_set_Iest(struct snd_soc_codec *codec, + u8 value) +{ + snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5, + 0x01, (0x01 & 0x03)); + snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5, + 0xFC, (value << 2)); +} + +static void wcd9xxx_clsh_state_hph_ear(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable) +{ + int compute_pa = 0; + + dev_dbg(codec->dev, "%s: enter %s\n", __func__, + is_enable ? "enable" : "disable"); + + if (is_enable) { + /* + * The below check condition is required to make sure + * functions inside if condition will execute only once. + */ + if ((clsh_d->state == WCD9XXX_CLSH_STATE_EAR) || + (req_state == WCD9XXX_CLSH_STATE_EAR)) { + wcd9xxx_dynamic_bypass_buck_ctrl(codec, false); + wcd9xxx_ncp_bypass_enable(codec, true); + } + switch (req_state) { + case WCD9XXX_CLSH_STATE_HPHL: + compute_pa = CLSH_COMPUTE_HPH_L; + break; + case WCD9XXX_CLSH_STATE_HPHR: + compute_pa = CLSH_COMPUTE_HPH_R; + break; + case WCD9XXX_CLSH_STATE_EAR: + compute_pa = CLSH_COMPUTE_EAR; + break; + default: + dev_dbg(codec->dev, + "%s:Invalid state:0x%x,enable:0x%x\n", + __func__, req_state, is_enable); + break; + } + wcd9xxx_clsh_comp_req(codec, clsh_d, compute_pa, true); + + dev_dbg(codec->dev, "%s: Enabled hph+ear mode clsh\n", + __func__); + } else { + switch (req_state) { + case WCD9XXX_CLSH_STATE_HPHL: + compute_pa = CLSH_COMPUTE_HPH_L; + break; + case WCD9XXX_CLSH_STATE_HPHR: + compute_pa = CLSH_COMPUTE_HPH_R; + break; + case WCD9XXX_CLSH_STATE_EAR: + compute_pa = CLSH_COMPUTE_EAR; + break; + default: + dev_dbg(codec->dev, + "%s:Invalid state:0x%x,enable:0x%x\n", + __func__, req_state, is_enable); + break; + } + wcd9xxx_clsh_comp_req(codec, clsh_d, compute_pa, false); + + if (((clsh_d->state & (~req_state)) == + WCD9XXX_CLSH_STATE_EAR) || + (req_state == WCD9XXX_CLSH_STATE_EAR)) { + wcd9xxx_ncp_bypass_enable(codec, false); + wcd9xxx_dynamic_bypass_buck_ctrl(codec, true); + } + } +} + +static void wcd9xxx_clsh_state_hph_lo(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable) +{ + + dev_dbg(codec->dev, "%s: enter %s\n", __func__, + is_enable ? "enable" : "disable"); + if (is_enable) { + if ((clsh_d->state == WCD9XXX_CLSH_STATE_LO) || + (req_state == WCD9XXX_CLSH_STATE_LO)) { + wcd9xxx_dynamic_bypass_buck_ctrl_lo(codec, false); + wcd9xxx_enable_buck(codec, clsh_d, true); + wcd9xxx_ncp_bypass_enable(codec, true); + if (req_state & WCD9XXX_CLSH_STATE_HPH_ST) { + wcd9xxx_set_fclk_get_ncp(codec, clsh_d, + NCP_FCLK_LEVEL_8); + wcd9xxx_set_fclk_put_ncp(codec, clsh_d, + NCP_FCLK_LEVEL_5); + wcd9xxx_enable_clsh_block(codec, clsh_d, true); + wcd9xxx_chargepump_request(codec, true); + wcd9xxx_enable_anc_delay(codec, true); + } + } + if (req_state == WCD9XXX_CLSH_STATE_HPHL) + wcd9xxx_clsh_comp_req(codec, clsh_d, + CLSH_COMPUTE_HPH_L, true); + if (req_state == WCD9XXX_CLSH_STATE_HPHR) + wcd9xxx_clsh_comp_req(codec, clsh_d, + CLSH_COMPUTE_HPH_R, true); + } else { + switch (req_state) { + case WCD9XXX_CLSH_STATE_LO: + snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, + 0x20, 0x00); + wcd9xxx_dynamic_bypass_buck_ctrl_lo(codec, true); + break; + case WCD9XXX_CLSH_STATE_HPHL: + wcd9xxx_clsh_comp_req(codec, clsh_d, + CLSH_COMPUTE_HPH_L, false); + break; + case WCD9XXX_CLSH_STATE_HPHR: + wcd9xxx_clsh_comp_req(codec, clsh_d, + CLSH_COMPUTE_HPH_R, false); + break; + default: + dev_dbg(codec->dev, + "%s:Invalid state:0x%x,enable:0x%x\n", + __func__, req_state, is_enable); + break; + } + if ((req_state == WCD9XXX_CLSH_STATE_LO) || + ((clsh_d->state & (~req_state)) == WCD9XXX_CLSH_STATE_LO)) { + wcd9xxx_ncp_bypass_enable(codec, false); + + if ((clsh_d->state & (~req_state)) == + WCD9XXX_CLSH_STATE_LO) { + wcd9xxx_set_fclk_get_ncp(codec, clsh_d, + NCP_FCLK_LEVEL_5); + wcd9xxx_set_fclk_put_ncp(codec, clsh_d, + NCP_FCLK_LEVEL_8); + } + + if (req_state & WCD9XXX_CLSH_STATE_HPH_ST) { + usleep_range(BUCK_SETTLE_TIME_US, + BUCK_SETTLE_TIME_US + 10); + if (clsh_d->buck_mv == + WCD9XXX_CDC_BUCK_MV_1P8) { + wcd9xxx_enable_buck(codec, clsh_d, + false); + wcd9xxx_ncp_bypass_enable(codec, true); + } else { + /* + *NCP settle time recommended by codec + *specification + */ + usleep_range(NCP_SETTLE_TIME_US, + NCP_SETTLE_TIME_US + 10); + wcd9xxx_clsh_set_Iest(codec, 0x02); + } + snd_soc_update_bits(codec, + WCD9XXX_A_BUCK_MODE_1, + 0x04, 0x00); + snd_soc_update_bits(codec, + WCD9XXX_A_BUCK_MODE_4, + 0xFF, BUCK_VREF_1P8V); + } + } + } +} + +static void wcd9xxx_clsh_state_ear_lo(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable) +{ + + dev_dbg(codec->dev, "%s: enter %s\n", __func__, + is_enable ? "enable" : "disable"); + if (is_enable) { + wcd9xxx_dynamic_bypass_buck_ctrl(codec, false); + wcd9xxx_enable_buck(codec, clsh_d, true); + wcd9xxx_ncp_bypass_enable(codec, true); + if (req_state & WCD9XXX_CLSH_STATE_EAR) { + wcd9xxx_set_fclk_get_ncp(codec, clsh_d, + NCP_FCLK_LEVEL_8); + wcd9xxx_set_fclk_put_ncp(codec, clsh_d, + NCP_FCLK_LEVEL_5); + wcd9xxx_enable_clsh_block(codec, clsh_d, true); + wcd9xxx_chargepump_request(codec, true); + wcd9xxx_enable_anc_delay(codec, true); + wcd9xxx_clsh_comp_req(codec, clsh_d, + CLSH_COMPUTE_EAR, true); + } + } else { + wcd9xxx_ncp_bypass_enable(codec, false); + + if ((clsh_d->state & (~req_state)) == WCD9XXX_CLSH_STATE_LO) { + wcd9xxx_set_fclk_get_ncp(codec, clsh_d, + NCP_FCLK_LEVEL_5); + wcd9xxx_set_fclk_put_ncp(codec, clsh_d, + NCP_FCLK_LEVEL_8); + } + + if (req_state & WCD9XXX_CLSH_STATE_LO) { + snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, + 0x20, 0x00); + wcd9xxx_dynamic_bypass_buck_ctrl(codec, true); + } else if (req_state & WCD9XXX_CLSH_STATE_EAR) { + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR, + false); + /*sleep 5ms*/ + if (clsh_d->buck_mv == WCD9XXX_CDC_BUCK_MV_1P8) { + wcd9xxx_enable_buck(codec, clsh_d, false); + wcd9xxx_ncp_bypass_enable(codec, true); + } else { + /* NCP settle time recommended by codec spec */ + usleep_range(NCP_SETTLE_TIME_US, + NCP_SETTLE_TIME_US + 10); + wcd9xxx_clsh_set_Iest(codec, 0x02); + } + snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_4, + 0xFF, BUCK_VREF_1P8V); + } + } +} + +static void wcd9xxx_clsh_state_hph_ear_lo(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable) +{ + dev_dbg(codec->dev, "%s: enter %s\n", __func__, + is_enable ? "enable" : "disable"); + + if (req_state & WCD9XXX_CLSH_STATE_HPHL) + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, + is_enable); + + if (req_state & WCD9XXX_CLSH_STATE_HPHR) + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, + is_enable); + + if (req_state & WCD9XXX_CLSH_STATE_EAR) + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR, + is_enable); +} + +static void wcd9xxx_clsh_state_ear(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable) +{ + pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable"); + if (is_enable) { + wcd9xxx_cfg_clsh_param_common(codec); + wcd9xxx_cfg_clsh_param_ear(codec); + wcd9xxx_enable_clsh_block(codec, clsh_d, true); + wcd9xxx_chargepump_request(codec, true); + wcd9xxx_enable_anc_delay(codec, true); + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR, true); + wcd9xxx_set_buck_mode(codec, BUCK_VREF_2V); + wcd9xxx_enable_buck(codec, clsh_d, true); + wcd9xxx_set_fclk_get_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8); + + dev_dbg(codec->dev, "%s: Enabled ear mode class h\n", __func__); + } else { + dev_dbg(codec->dev, "%s: stub fallback to ear\n", __func__); + wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8); + wcd9xxx_enable_buck(codec, clsh_d, false); + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR, false); + wcd9xxx_chargepump_request(codec, false); + wcd9xxx_enable_clsh_block(codec, clsh_d, false); + } +} + +static void wcd9xxx_clsh_state_hph_l(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable) +{ + pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable"); + + if (is_enable) { + wcd9xxx_cfg_clsh_param_common(codec); + wcd9xxx_cfg_clsh_param_hph(codec); + wcd9xxx_enable_clsh_block(codec, clsh_d, true); + wcd9xxx_chargepump_request(codec, true); + wcd9xxx_enable_anc_delay(codec, true); + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, true); + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, true); + wcd9xxx_set_buck_mode(codec, BUCK_VREF_0P494V); + wcd9xxx_enable_buck(codec, clsh_d, true); + wcd9xxx_set_fclk_get_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8); + + dev_dbg(codec->dev, "%s: Done\n", __func__); + } else { + wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8); + wcd9xxx_enable_buck(codec, clsh_d, false); + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, false); + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, false); + wcd9xxx_enable_clsh_block(codec, clsh_d, false); + wcd9xxx_chargepump_request(codec, false); + } +} + +static void wcd9xxx_clsh_state_hph_r(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable) +{ + pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable"); + + if (is_enable) { + wcd9xxx_cfg_clsh_param_common(codec); + wcd9xxx_cfg_clsh_param_hph(codec); + wcd9xxx_enable_clsh_block(codec, clsh_d, true); + wcd9xxx_chargepump_request(codec, true); + wcd9xxx_enable_anc_delay(codec, true); + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, true); + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, true); + wcd9xxx_set_buck_mode(codec, BUCK_VREF_0P494V); + wcd9xxx_enable_buck(codec, clsh_d, true); + wcd9xxx_set_fclk_get_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8); + + dev_dbg(codec->dev, "%s: Done\n", __func__); + } else { + wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8); + wcd9xxx_enable_buck(codec, clsh_d, false); + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, false); + wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, false); + wcd9xxx_enable_clsh_block(codec, clsh_d, false); + wcd9xxx_chargepump_request(codec, false); + } +} + +static void wcd9xxx_clsh_state_hph_st(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable) +{ + pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable"); + + if (is_enable) + dev_dbg(codec->dev, "%s: stub fallback to hph_st\n", __func__); + else + dev_dbg(codec->dev, "%s: stub fallback to hph_st\n", __func__); +} + +static void wcd9xxx_clsh_state_lo(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable) +{ + pr_debug("%s: enter %s, buck_mv %d\n", __func__, + is_enable ? "enable" : "disable", clsh_d->buck_mv); + + if (is_enable) { + wcd9xxx_set_buck_mode(codec, BUCK_VREF_1P8V); + wcd9xxx_enable_buck(codec, clsh_d, true); + wcd9xxx_set_fclk_get_ncp(codec, clsh_d, NCP_FCLK_LEVEL_5); + + if (clsh_d->buck_mv == WCD9XXX_CDC_BUCK_MV_1P8) { + wcd9xxx_enable_buck(codec, clsh_d, false); + snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, + 1 << 4, 1 << 4); + /* NCP settle time recommended by codec specification */ + usleep_range(NCP_SETTLE_TIME_US, + NCP_SETTLE_TIME_US + 10); + } else { + /* NCP settle time recommended by codec specification */ + usleep_range(NCP_SETTLE_TIME_US, + NCP_SETTLE_TIME_US + 10); + snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5, + 0x01, (0x01 & 0x03)); + snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5, + 0xFC, (0xFC & 0xB)); + } + snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1, 0x04, 0x00); + } else { + dev_dbg(codec->dev, "%s: stub fallback to lineout\n", __func__); + wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_5); + if (clsh_d->buck_mv != WCD9XXX_CDC_BUCK_MV_1P8) + wcd9xxx_enable_buck(codec, clsh_d, false); + } +} + +static void wcd9xxx_clsh_state_err(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable) +{ + char msg[128]; + + dev_dbg(codec->dev, + "%s Wrong request for class H state machine requested to %s %s", + __func__, is_enable ? "enable" : "disable", + state_to_str(req_state, msg, sizeof(msg))); + WARN_ON(1); +} + +/* + * Function: wcd9xxx_clsh_is_state_valid + * Params: state + * Description: + * Provides information on valid states of Class H configuration + */ +static int wcd9xxx_clsh_is_state_valid(u8 state) +{ + switch (state) { + case WCD9XXX_CLSH_STATE_IDLE: + case WCD9XXX_CLSH_STATE_EAR: + case WCD9XXX_CLSH_STATE_HPHL: + case WCD9XXX_CLSH_STATE_HPHR: + case WCD9XXX_CLSH_STATE_HPH_ST: + case WCD9XXX_CLSH_STATE_LO: + case WCD9XXX_CLSH_STATE_HPHL_EAR: + case WCD9XXX_CLSH_STATE_HPHR_EAR: + case WCD9XXX_CLSH_STATE_HPH_ST_EAR: + case WCD9XXX_CLSH_STATE_HPHL_LO: + case WCD9XXX_CLSH_STATE_HPHR_LO: + case WCD9XXX_CLSH_STATE_HPH_ST_LO: + case WCD9XXX_CLSH_STATE_EAR_LO: + case WCD9XXX_CLSH_STATE_HPHL_EAR_LO: + case WCD9XXX_CLSH_STATE_HPHR_EAR_LO: + case WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO: + return 1; + default: + break; + } + return 0; +} + +/* + * Function: wcd9xxx_clsh_fsm + * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event + * Description: + * This function handles PRE DAC and POST DAC conditions of different devices + * and updates class H configuration of different combination of devices + * based on validity of their states. cdc_clsh_d will contain current + * class h state information + */ +void wcd9xxx_clsh_fsm(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *cdc_clsh_d, + u8 req_state, bool req_type, u8 clsh_event) +{ + u8 old_state, new_state; + char msg0[128], msg1[128]; + + switch (clsh_event) { + case WCD9XXX_CLSH_EVENT_PRE_DAC: + /* PRE_DAC event should be used only for Enable */ + BUG_ON(req_type != WCD9XXX_CLSH_REQ_ENABLE); + + old_state = cdc_clsh_d->state; + new_state = old_state | req_state; + + if (!wcd9xxx_clsh_is_state_valid(new_state)) { + dev_dbg(codec->dev, + "%s: classH not a valid new state: %s\n", + __func__, + state_to_str(new_state, msg0, sizeof(msg0))); + return; + } + if (new_state == old_state) { + dev_dbg(codec->dev, + "%s: classH already in requested state: %s\n", + __func__, + state_to_str(new_state, msg0, sizeof(msg0))); + return; + } + (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state, + req_type); + cdc_clsh_d->state = new_state; + dev_dbg(codec->dev, + "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str(old_state, msg0, sizeof(msg0)), + state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1))); + + break; + case WCD9XXX_CLSH_EVENT_POST_PA: + if (req_type == WCD9XXX_CLSH_REQ_DISABLE) { + old_state = cdc_clsh_d->state; + new_state = old_state & (~req_state); + + if (new_state < NUM_CLSH_STATES) { + if (!wcd9xxx_clsh_is_state_valid(old_state)) { + dev_dbg(codec->dev, + "%s:Invalid old state:%s\n", + __func__, + state_to_str(old_state, msg0, + sizeof(msg0))); + return; + } + if (new_state == old_state) { + dev_dbg(codec->dev, + "%s: clsH already in old state: %s\n", + __func__, + state_to_str(new_state, msg0, + sizeof(msg0))); + return; + } + (*clsh_state_fp[old_state]) (codec, cdc_clsh_d, + req_state, + req_type); + cdc_clsh_d->state = new_state; + dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str(old_state, msg0, + sizeof(msg0)), + state_to_str(cdc_clsh_d->state, msg1, + sizeof(msg1))); + + } else { + dev_dbg(codec->dev, "%s:wrong new state=0x%x\n", + __func__, new_state); + } + } else if (!(cdc_clsh_d->state & WCD9XXX_CLSH_STATE_LO)) { + wcd9xxx_clsh_enable_post_pa(codec, cdc_clsh_d); + } + + break; + } + +} +EXPORT_SYMBOL(wcd9xxx_clsh_fsm); + +void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh, + struct wcd9xxx_resmgr *resmgr) +{ + int i; + + clsh->state = WCD9XXX_CLSH_STATE_IDLE; + clsh->resmgr = resmgr; + + for (i = 0; i < NUM_CLSH_STATES; i++) + clsh_state_fp[i] = wcd9xxx_clsh_state_err; + + clsh_state_fp[WCD9XXX_CLSH_STATE_EAR] = wcd9xxx_clsh_state_ear; + clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL] = + wcd9xxx_clsh_state_hph_l; + clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR] = + wcd9xxx_clsh_state_hph_r; + clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST] = + wcd9xxx_clsh_state_hph_st; + clsh_state_fp[WCD9XXX_CLSH_STATE_LO] = wcd9xxx_clsh_state_lo; + clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_EAR] = + wcd9xxx_clsh_state_hph_ear; + clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_EAR] = + wcd9xxx_clsh_state_hph_ear; + clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_EAR] = + wcd9xxx_clsh_state_hph_ear; + clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_LO] = wcd9xxx_clsh_state_hph_lo; + clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_LO] = wcd9xxx_clsh_state_hph_lo; + clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_LO] = + wcd9xxx_clsh_state_hph_lo; + clsh_state_fp[WCD9XXX_CLSH_STATE_EAR_LO] = wcd9xxx_clsh_state_ear_lo; + clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_EAR_LO] = + wcd9xxx_clsh_state_hph_ear_lo; + clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_EAR_LO] = + wcd9xxx_clsh_state_hph_ear_lo; + clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO] = + wcd9xxx_clsh_state_hph_ear_lo; + +} +EXPORT_SYMBOL(wcd9xxx_clsh_init); + +MODULE_DESCRIPTION("WCD9XXX Common"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9xxx-common.h b/sound/soc/codecs/wcd9xxx-common.h new file mode 100644 index 0000000000000000000000000000000000000000..5c0c4a98f3fc7084b5c27d58b3dd66cdb6f571d2 --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-common.h @@ -0,0 +1,286 @@ +/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef WCD9XXX_CODEC_COMMON + +#define WCD9XXX_CODEC_COMMON + +#include "wcd9xxx-resmgr.h" + +#define WCD9XXX_CLSH_REQ_ENABLE true +#define WCD9XXX_CLSH_REQ_DISABLE false + +#define WCD9XXX_CLSH_EVENT_PRE_DAC 0x01 +#define WCD9XXX_CLSH_EVENT_POST_PA 0x02 + +/* Basic states for Class H state machine. + * represented as a bit mask within a u8 data type + * bit 0: EAR mode + * bit 1: HPH Left mode + * bit 2: HPH Right mode + * bit 3: Lineout mode + * bit 4: Ultrasound mode + */ +#define WCD9XXX_CLSH_STATE_IDLE 0x00 +#define WCD9XXX_CLSH_STATE_EAR (0x01 << 0) +#define WCD9XXX_CLSH_STATE_HPHL (0x01 << 1) +#define WCD9XXX_CLSH_STATE_HPHR (0x01 << 2) +#define WCD9XXX_CLSH_STATE_LO (0x01 << 3) +#define NUM_CLSH_STATES (0x01 << 4) + +#define WCD9XXX_CLSAB_STATE_IDLE 0x00 +#define WCD9XXX_CLSAB_STATE_HPHL (0x01 << 1) +#define WCD9XXX_CLSAB_STATE_HPHR (0x01 << 2) + +#define WCD9XXX_CLSAB_REQ_ENABLE true +#define WCD9XXX_CLSAB_REQ_DISABLE false + +#define WCD9XXX_NON_UHQA_MODE 0 + +#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_2 0x0 +#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_3 0x1 +#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_4 0x2 + +#define WCD9XXX_DMIC_B1_CTL_DIV_2 0x00 +#define WCD9XXX_DMIC_B1_CTL_DIV_3 0x22 +#define WCD9XXX_DMIC_B1_CTL_DIV_4 0x44 + +#define WCD9XXX_DMIC_B2_CTL_DIV_2 0x00 +#define WCD9XXX_DMIC_B2_CTL_DIV_3 0x02 +#define WCD9XXX_DMIC_B2_CTL_DIV_4 0x04 + +#define WCD9XXX_ANC_DMIC_X2_ON 0x1 +#define WCD9XXX_ANC_DMIC_X2_OFF 0x0 + +/* Derived State: Bits 1 and 2 should be set for Headphone stereo */ +#define WCD9XXX_CLSH_STATE_HPH_ST (WCD9XXX_CLSH_STATE_HPHL | \ + WCD9XXX_CLSH_STATE_HPHR) + +#define WCD9XXX_CLSH_STATE_HPHL_EAR (WCD9XXX_CLSH_STATE_HPHL | \ + WCD9XXX_CLSH_STATE_EAR) +#define WCD9XXX_CLSH_STATE_HPHR_EAR (WCD9XXX_CLSH_STATE_HPHR | \ + WCD9XXX_CLSH_STATE_EAR) + +#define WCD9XXX_CLSH_STATE_HPH_ST_EAR (WCD9XXX_CLSH_STATE_HPH_ST | \ + WCD9XXX_CLSH_STATE_EAR) + +#define WCD9XXX_CLSH_STATE_HPHL_LO (WCD9XXX_CLSH_STATE_HPHL | \ + WCD9XXX_CLSH_STATE_LO) +#define WCD9XXX_CLSH_STATE_HPHR_LO (WCD9XXX_CLSH_STATE_HPHR | \ + WCD9XXX_CLSH_STATE_LO) + +#define WCD9XXX_CLSH_STATE_HPH_ST_LO (WCD9XXX_CLSH_STATE_HPH_ST | \ + WCD9XXX_CLSH_STATE_LO) + +#define WCD9XXX_CLSH_STATE_EAR_LO (WCD9XXX_CLSH_STATE_EAR | \ + WCD9XXX_CLSH_STATE_LO) + +#define WCD9XXX_CLSH_STATE_HPHL_EAR_LO (WCD9XXX_CLSH_STATE_HPHL | \ + WCD9XXX_CLSH_STATE_EAR | \ + WCD9XXX_CLSH_STATE_LO) +#define WCD9XXX_CLSH_STATE_HPHR_EAR_LO (WCD9XXX_CLSH_STATE_HPHR | \ + WCD9XXX_CLSH_STATE_EAR | \ + WCD9XXX_CLSH_STATE_LO) +#define WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO (WCD9XXX_CLSH_STATE_HPH_ST | \ + WCD9XXX_CLSH_STATE_EAR | \ + WCD9XXX_CLSH_STATE_LO) + +struct wcd9xxx_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +enum ncp_fclk_level { + NCP_FCLK_LEVEL_8, + NCP_FCLK_LEVEL_5, + NCP_FCLK_LEVEL_MAX, +}; + +/* Class H data that the codec driver will maintain */ +struct wcd9xxx_clsh_cdc_data { + u8 state; + int buck_mv; + bool is_dynamic_vdd_cp; + int clsh_users; + int buck_users; + int ncp_users[NCP_FCLK_LEVEL_MAX]; + struct wcd9xxx_resmgr *resmgr; +}; + +struct wcd9xxx_anc_header { + u32 reserved[3]; + u32 num_anc_slots; +}; + +enum wcd9xxx_buck_volt { + WCD9XXX_CDC_BUCK_UNSUPPORTED = 0, + WCD9XXX_CDC_BUCK_MV_1P8 = 1800000, + WCD9XXX_CDC_BUCK_MV_2P15 = 2150000, +}; + +struct mad_audio_header { + u32 reserved[3]; + u32 num_reg_cfg; +}; + +struct mad_microphone_info { + uint8_t input_microphone; + uint8_t cycle_time; + uint8_t settle_time; + uint8_t padding; +} __packed; + +struct mad_micbias_info { + uint8_t micbias; + uint8_t k_factor; + uint8_t external_bypass_capacitor; + uint8_t internal_biasing; + uint8_t cfilter; + uint8_t padding[3]; +} __packed; + +struct mad_rms_audio_beacon_info { + uint8_t rms_omit_samples; + uint8_t rms_comp_time; + uint8_t detection_mechanism; + uint8_t rms_diff_threshold; + uint8_t rms_threshold_lsb; + uint8_t rms_threshold_msb; + uint8_t padding[2]; + uint8_t iir_coefficients[36]; +} __packed; + +struct mad_rms_ultrasound_info { + uint8_t rms_comp_time; + uint8_t detection_mechanism; + uint8_t rms_diff_threshold; + uint8_t rms_threshold_lsb; + uint8_t rms_threshold_msb; + uint8_t padding[3]; + uint8_t iir_coefficients[36]; +} __packed; + +struct mad_audio_cal { + uint32_t version; + struct mad_microphone_info microphone_info; + struct mad_micbias_info micbias_info; + struct mad_rms_audio_beacon_info audio_info; + struct mad_rms_audio_beacon_info beacon_info; + struct mad_rms_ultrasound_info ultrasound_info; +} __packed; + +extern void wcd9xxx_clsh_fsm(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *cdc_clsh_d, + u8 req_state, bool req_type, u8 clsh_event); + +extern void wcd9xxx_enable_high_perf_mode(struct snd_soc_codec *codec, + struct wcd9xxx_clsh_cdc_data *clsh_d, + u8 uhqa_mode, u8 req_state, bool req_type); + +extern void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh, + struct wcd9xxx_resmgr *resmgr); + +extern void wcd9xxx_clsh_imped_config(struct snd_soc_codec *codec, + int imped); + +enum wcd9xxx_codec_event { + WCD9XXX_CODEC_EVENT_CODEC_UP = 0, +}; + +struct wcd9xxx_register_save_node { + struct list_head lh; + u16 reg; + u16 value; +}; + +extern int wcd9xxx_soc_update_bits_push(struct snd_soc_codec *codec, + struct list_head *lh, + uint16_t reg, uint8_t mask, + uint8_t value, int delay); +extern void wcd9xxx_restore_registers(struct snd_soc_codec *codec, + struct list_head *lh); +enum { + RESERVED = 0, + AANC_LPF_FF_FB = 1, + AANC_LPF_COEFF_MSB, + AANC_LPF_COEFF_LSB, + HW_MAD_AUDIO_ENABLE, + HW_MAD_ULTR_ENABLE, + HW_MAD_BEACON_ENABLE, + HW_MAD_AUDIO_SLEEP_TIME, + HW_MAD_ULTR_SLEEP_TIME, + HW_MAD_BEACON_SLEEP_TIME, + HW_MAD_TX_AUDIO_SWITCH_OFF, + HW_MAD_TX_ULTR_SWITCH_OFF, + HW_MAD_TX_BEACON_SWITCH_OFF, + MAD_AUDIO_INT_DEST_SELECT_REG, + MAD_ULT_INT_DEST_SELECT_REG, + MAD_BEACON_INT_DEST_SELECT_REG, + MAD_CLIP_INT_DEST_SELECT_REG, + MAD_VBAT_INT_DEST_SELECT_REG, + MAD_AUDIO_INT_MASK_REG, + MAD_ULT_INT_MASK_REG, + MAD_BEACON_INT_MASK_REG, + MAD_CLIP_INT_MASK_REG, + MAD_VBAT_INT_MASK_REG, + MAD_AUDIO_INT_STATUS_REG, + MAD_ULT_INT_STATUS_REG, + MAD_BEACON_INT_STATUS_REG, + MAD_CLIP_INT_STATUS_REG, + MAD_VBAT_INT_STATUS_REG, + MAD_AUDIO_INT_CLEAR_REG, + MAD_ULT_INT_CLEAR_REG, + MAD_BEACON_INT_CLEAR_REG, + MAD_CLIP_INT_CLEAR_REG, + MAD_VBAT_INT_CLEAR_REG, + SB_PGD_PORT_TX_WATERMARK_N, + SB_PGD_PORT_TX_ENABLE_N, + SB_PGD_PORT_RX_WATERMARK_N, + SB_PGD_PORT_RX_ENABLE_N, + SB_PGD_TX_PORTn_MULTI_CHNL_0, + SB_PGD_TX_PORTn_MULTI_CHNL_1, + SB_PGD_RX_PORTn_MULTI_CHNL_0, + SB_PGD_RX_PORTn_MULTI_CHNL_1, + AANC_FF_GAIN_ADAPTIVE, + AANC_FFGAIN_ADAPTIVE_EN, + AANC_GAIN_CONTROL, + SPKR_CLIP_PIPE_BANK_SEL, + SPKR_CLIPDET_VAL0, + SPKR_CLIPDET_VAL1, + SPKR_CLIPDET_VAL2, + SPKR_CLIPDET_VAL3, + SPKR_CLIPDET_VAL4, + SPKR_CLIPDET_VAL5, + SPKR_CLIPDET_VAL6, + SPKR_CLIPDET_VAL7, + VBAT_RELEASE_INT_DEST_SELECT_REG, + VBAT_RELEASE_INT_MASK_REG, + VBAT_RELEASE_INT_STATUS_REG, + VBAT_RELEASE_INT_CLEAR_REG, + MAD2_CLIP_INT_DEST_SELECT_REG, + MAD2_CLIP_INT_MASK_REG, + MAD2_CLIP_INT_STATUS_REG, + MAD2_CLIP_INT_CLEAR_REG, + SPKR2_CLIP_PIPE_BANK_SEL, + SPKR2_CLIPDET_VAL0, + SPKR2_CLIPDET_VAL1, + SPKR2_CLIPDET_VAL2, + SPKR2_CLIPDET_VAL3, + SPKR2_CLIPDET_VAL4, + SPKR2_CLIPDET_VAL5, + SPKR2_CLIPDET_VAL6, + SPKR2_CLIPDET_VAL7, + MAX_CFG_REGISTERS, +}; + +#endif diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c new file mode 100644 index 0000000000000000000000000000000000000000..45bd84b97143d1ec0e8c2376a9e728102177417f --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-mbhc.c @@ -0,0 +1,5676 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd9xxx-mbhc.h" +#include "wcdcal-hwdep.h" +#include "wcd9xxx-resmgr.h" +#include "wcd9xxx-common.h" + +#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \ + SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \ + SND_JACK_UNSUPPORTED | SND_JACK_MICROPHONE2 | \ + SND_JACK_MECHANICAL) +#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3 | \ + SND_JACK_BTN_4 | SND_JACK_BTN_5) + +#define NUM_DCE_PLUG_DETECT 3 +#define NUM_DCE_PLUG_INS_DETECT 5 +#define NUM_ATTEMPTS_INSERT_DETECT 25 +#define NUM_ATTEMPTS_TO_REPORT 5 + +#define FAKE_INS_LOW 10 +#define FAKE_INS_HIGH 80 +#define FAKE_INS_HIGH_NO_SWCH 150 +#define FAKE_REMOVAL_MIN_PERIOD_MS 50 +#define FAKE_INS_DELTA_SCALED_MV 300 + +#define BUTTON_MIN 0x8000 +#define STATUS_REL_DETECTION 0x0C + +#define HS_DETECT_PLUG_TIME_MS (5 * 1000) +#define ANC_HPH_DETECT_PLUG_TIME_MS (5 * 1000) +#define HS_DETECT_PLUG_INERVAL_MS 100 +#define SWCH_REL_DEBOUNCE_TIME_MS 50 +#define SWCH_IRQ_DEBOUNCE_TIME_US 5000 +#define BTN_RELEASE_DEBOUNCE_TIME_MS 25 + +#define GND_MIC_SWAP_THRESHOLD 2 +#define OCP_ATTEMPT 1 + +#define FW_READ_ATTEMPTS 15 +#define FW_READ_TIMEOUT 4000000 + +#define BUTTON_POLLING_SUPPORTED true + +#define MCLK_RATE_12288KHZ 12288000 +#define MCLK_RATE_9600KHZ 9600000 + +#define DEFAULT_DCE_STA_WAIT 55 +#define DEFAULT_DCE_WAIT 60000 +#define DEFAULT_STA_WAIT 5000 + +#define VDDIO_MICBIAS_MV 1800 + +#define WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US 5000 + +#define WCD9XXX_HPHL_STATUS_READY_WAIT_US 1000 +#define WCD9XXX_MUX_SWITCH_READY_WAIT_MS 50 +#define WCD9XXX_MEAS_DELTA_MAX_MV 120 +#define WCD9XXX_MEAS_INVALD_RANGE_LOW_MV 20 +#define WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV 80 + +/* Threshold in milliohm used for mono/stereo + * plug classification + */ +#define WCD9XXX_MONO_HS_DIFF_THR 20000000 +#define WCD9XXX_MONO_HS_MIN_THR 2000 + +/* + * Invalid voltage range for the detection + * of plug type with current source + */ +#define WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV 160 +#define WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV 265 + +/* + * Threshold used to detect euro headset + * with current source + */ +#define WCD9XXX_CS_GM_SWAP_THRES_MIN_MV 10 +#define WCD9XXX_CS_GM_SWAP_THRES_MAX_MV 40 + +#define WCD9XXX_MBHC_NSC_CS 9 +#define WCD9XXX_GM_SWAP_THRES_MIN_MV 150 +#define WCD9XXX_GM_SWAP_THRES_MAX_MV 650 +#define WCD9XXX_THRESHOLD_MIC_THRESHOLD 200 + +#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100 + +/* RX_HPH_CNP_WG_TIME increases by 0.24ms */ +#define WCD9XXX_WG_TIME_FACTOR_US 240 + +#define WCD9XXX_V_CS_HS_MAX 500 +#define WCD9XXX_V_CS_NO_MIC 5 +#define WCD9XXX_MB_MEAS_DELTA_MAX_MV 80 +#define WCD9XXX_CS_MEAS_DELTA_MAX_MV 12 + +#define WCD9XXX_ZDET_ZONE_1 80000 +#define WCD9XXX_ZDET_ZONE_2 800000 + +#define WCD9XXX_IS_IN_ZDET_ZONE_1(x) (x < WCD9XXX_ZDET_ZONE_1 ? 1 : 0) +#define WCD9XXX_IS_IN_ZDET_ZONE_2(x) ((x > WCD9XXX_ZDET_ZONE_1 && \ + x < WCD9XXX_ZDET_ZONE_2) ? 1 : 0) +#define WCD9XXX_IS_IN_ZDET_ZONE_3(x) (x > WCD9XXX_ZDET_ZONE_2 ? 1 : 0) +#define WCD9XXX_BOX_CAR_AVRG_MIN 1 +#define WCD9XXX_BOX_CAR_AVRG_MAX 10 + +/* + * Need to report LINEIN if H/L impedance + * is larger than 5K ohm + */ +#define WCD9XXX_LINEIN_THRESHOLD 5000000 + +static int impedance_detect_en; +module_param(impedance_detect_en, int, 0664); +MODULE_PARM_DESC(impedance_detect_en, "enable/disable impedance detect"); +static unsigned int z_det_box_car_avg = 1; +module_param(z_det_box_car_avg, int, 0664); +MODULE_PARM_DESC(z_det_box_car_avg, + "Number of samples for impedance detection"); + +static bool detect_use_vddio_switch; + +struct wcd9xxx_mbhc_detect { + u16 dce; + u16 sta; + u16 hphl_status; + bool swap_gnd; + bool vddio; + bool hwvalue; + bool mic_bias; + /* internal purpose from here */ + bool _above_no_mic; + bool _below_v_hs_max; + s16 _vdces; + enum wcd9xxx_mbhc_plug_type _type; +}; + +enum meas_type { + STA = 0, + DCE, +}; + +enum { + MBHC_USE_HPHL_TRIGGER = 1, + MBHC_USE_MB_TRIGGER = 2 +}; + +/* + * Flags to track of PA and DAC state. + * PA and DAC should be tracked separately as AUXPGA loopback requires + * only PA to be turned on without DAC being on. + */ +enum pa_dac_ack_flags { + WCD9XXX_HPHL_PA_OFF_ACK = 0, + WCD9XXX_HPHR_PA_OFF_ACK, + WCD9XXX_HPHL_DAC_OFF_ACK, + WCD9XXX_HPHR_DAC_OFF_ACK +}; + +enum wcd9xxx_current_v_idx { + WCD9XXX_CURRENT_V_INS_H, + WCD9XXX_CURRENT_V_INS_HU, + WCD9XXX_CURRENT_V_B1_H, + WCD9XXX_CURRENT_V_B1_HU, + WCD9XXX_CURRENT_V_BR_H, +}; + +static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl, + uint32_t *zr); +static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc, + const enum wcd9xxx_current_v_idx idx); +static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z, + struct mbhc_micbias_regs *micb_regs, + bool norel); + +static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc); + +static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc, + enum meas_type dce, s16 vin_mv, + bool cs_enable); + +static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc) +{ + return snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_EN_CTL) & 0x1; +} + +static void wcd9xxx_turn_onoff_override(struct wcd9xxx_mbhc *mbhc, bool on) +{ + struct snd_soc_codec *codec = mbhc->codec; + + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, + 0x04, on ? 0x04 : 0x00); +} + +/* called under codec_resource_lock acquisition */ +static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + pr_debug("%s: enter\n", __func__); + if (!mbhc->polling_active) { + pr_debug("polling not active, nothing to pause\n"); + return; + } + + /* Soft reset MBHC block */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8); + pr_debug("%s: leave\n", __func__); +} + +/* called under codec_resource_lock acquisition */ +static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + int mbhc_state = mbhc->mbhc_state; + + pr_debug("%s: enter\n", __func__); + if (!mbhc->polling_active) { + pr_debug("Polling is not active, do not start polling\n"); + return; + } + + /* + * setup internal micbias if codec uses internal micbias for + * headset detection + */ + if (mbhc->mbhc_cfg->use_int_rbias) { + if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) + mbhc->mbhc_cb->setup_int_rbias(codec, true); + else + pr_err("%s: internal bias requested but codec did not provide callback\n", + __func__); + } + + snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04); + if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block) + mbhc->mbhc_cb->enable_mux_bias_block(codec); + else + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, + 0x80, 0x80); + + if (!mbhc->no_mic_headset_override && + mbhc_state == MBHC_STATE_POTENTIAL) { + pr_debug("%s recovering MBHC state machine\n", __func__); + mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY; + /* set to max button press threshold */ + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF); + /* set to max */ + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF); + } + + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1); + pr_debug("%s: leave\n", __func__); +} + +static int __wcd9xxx_resmgr_get_k_val(struct wcd9xxx_mbhc *mbhc, + unsigned int cfilt_mv) +{ + return wcd9xxx_resmgr_get_k_val(mbhc->resmgr, cfilt_mv); +} + +/* + * called under codec_resource_lock acquisition + * return old status + */ +static bool __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, + int vddio_switch, bool restartpolling, + bool checkpolling) +{ + bool ret; + int cfilt_k_val; + bool override; + struct snd_soc_codec *codec; + struct mbhc_internal_cal_data *d = &mbhc->mbhc_data; + + codec = mbhc->codec; + + if (mbhc->micbias_enable) { + pr_debug("%s: micbias is already on\n", __func__); + ret = mbhc->mbhc_micbias_switched; + return ret; + } + + ret = mbhc->mbhc_micbias_switched; + if (vddio_switch && !mbhc->mbhc_micbias_switched && + (!checkpolling || mbhc->polling_active)) { + if (restartpolling) + wcd9xxx_pause_hs_polling(mbhc); + override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & + 0x04; + if (!override) + wcd9xxx_turn_onoff_override(mbhc, true); + + snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, + 0x10, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, + 0x20, 0x00); + /* Adjust threshold if Mic Bias voltage changes */ + if (d->micb_mv != VDDIO_MICBIAS_MV) { + cfilt_k_val = __wcd9xxx_resmgr_get_k_val(mbhc, + VDDIO_MICBIAS_MV); + usleep_range(10000, 10100); + snd_soc_update_bits(codec, + mbhc->mbhc_bias_regs.cfilt_val, + 0xFC, (cfilt_k_val << 2)); + usleep_range(10000, 10100); + /* Threshods for insertion/removal */ + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, + d->v_ins_hu[MBHC_V_IDX_VDDIO] & 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, + (d->v_ins_hu[MBHC_V_IDX_VDDIO] >> 8) & + 0xFF); + + if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) { + /* Threshods for button press */ + snd_soc_write(codec, + WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, + d->v_b1_hu[MBHC_V_IDX_VDDIO] & 0xFF); + snd_soc_write(codec, + WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, + (d->v_b1_hu[MBHC_V_IDX_VDDIO] >> 8) & + 0xFF); + snd_soc_write(codec, + WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, + d->v_b1_h[MBHC_V_IDX_VDDIO] & 0xFF); + snd_soc_write(codec, + WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, + (d->v_b1_h[MBHC_V_IDX_VDDIO] >> 8) & + 0xFF); + /* Threshods for button release */ + snd_soc_write(codec, + WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, + d->v_brh[MBHC_V_IDX_VDDIO] & 0xFF); + snd_soc_write(codec, + WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL, + (d->v_brh[MBHC_V_IDX_VDDIO] >> 8) & + 0xFF); + } + pr_debug("%s: Programmed MBHC thresholds to VDDIO\n", + __func__); + } + + /* Enable MIC BIAS Switch to VDDIO */ + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, + 0x80, 0x80); + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, + 0x10, 0x00); + if (!override) + wcd9xxx_turn_onoff_override(mbhc, false); + if (restartpolling) + wcd9xxx_start_hs_polling(mbhc); + + mbhc->mbhc_micbias_switched = true; + pr_debug("%s: VDDIO switch enabled\n", __func__); + } else if (!vddio_switch && mbhc->mbhc_micbias_switched) { + if ((!checkpolling || mbhc->polling_active) && + restartpolling) + wcd9xxx_pause_hs_polling(mbhc); + + snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, + 0x10, 0x10); + snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, + 0x20, 0x20); + /* Reprogram thresholds */ + if (d->micb_mv != VDDIO_MICBIAS_MV) { + cfilt_k_val = + __wcd9xxx_resmgr_get_k_val(mbhc, + d->micb_mv); + snd_soc_update_bits(codec, + mbhc->mbhc_bias_regs.cfilt_val, + 0xFC, (cfilt_k_val << 2)); + usleep_range(10000, 10100); + /* Revert threshods for insertion/removal */ + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, + d->v_ins_hu[MBHC_V_IDX_CFILT] & 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, + (d->v_ins_hu[MBHC_V_IDX_CFILT] >> 8) & + 0xFF); + if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) { + /* Revert threshods for button press */ + snd_soc_write(codec, + WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, + d->v_b1_hu[MBHC_V_IDX_CFILT] & 0xFF); + snd_soc_write(codec, + WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, + (d->v_b1_hu[MBHC_V_IDX_CFILT] >> 8) & + 0xFF); + snd_soc_write(codec, + WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, + d->v_b1_h[MBHC_V_IDX_CFILT] & 0xFF); + snd_soc_write(codec, + WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, + (d->v_b1_h[MBHC_V_IDX_CFILT] >> 8) & + 0xFF); + /* Revert threshods for button release */ + snd_soc_write(codec, + WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, + d->v_brh[MBHC_V_IDX_CFILT] & 0xFF); + snd_soc_write(codec, + WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL, + (d->v_brh[MBHC_V_IDX_CFILT] >> 8) & + 0xFF); + } + pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n", + __func__); + } + + /* Disable MIC BIAS Switch to VDDIO */ + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80, + 0x00); + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10, + 0x00); + + if ((!checkpolling || mbhc->polling_active) && restartpolling) + wcd9xxx_start_hs_polling(mbhc); + + mbhc->mbhc_micbias_switched = false; + pr_debug("%s: VDDIO switch disabled\n", __func__); + } + + return ret; +} + +static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch) +{ + __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true); +} + +static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc, + const enum wcd9xxx_current_v_idx idx) +{ + enum mbhc_v_index vidx; + s16 ret = -EINVAL; + + if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) && + mbhc->mbhc_micbias_switched) + vidx = MBHC_V_IDX_VDDIO; + else + vidx = MBHC_V_IDX_CFILT; + + switch (idx) { + case WCD9XXX_CURRENT_V_INS_H: + ret = (s16)mbhc->mbhc_data.v_ins_h[vidx]; + break; + case WCD9XXX_CURRENT_V_INS_HU: + ret = (s16)mbhc->mbhc_data.v_ins_hu[vidx]; + break; + case WCD9XXX_CURRENT_V_B1_H: + ret = (s16)mbhc->mbhc_data.v_b1_h[vidx]; + break; + case WCD9XXX_CURRENT_V_B1_HU: + ret = (s16)mbhc->mbhc_data.v_b1_hu[vidx]; + break; + case WCD9XXX_CURRENT_V_BR_H: + ret = (s16)mbhc->mbhc_data.v_brh[vidx]; + break; + } + + return ret; +} + +void *wcd9xxx_mbhc_cal_btn_det_mp( + const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det, + const enum wcd9xxx_mbhc_btn_det_mem mem) +{ + void *ret = (void *)&btn_det->_v_btn_low; + + switch (mem) { + case MBHC_BTN_DET_GAIN: + ret += sizeof(btn_det->_n_cic); + /* fallthrough */ + case MBHC_BTN_DET_N_CIC: + ret += sizeof(btn_det->_n_ready); + /* fallthrough */ + case MBHC_BTN_DET_N_READY: + ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn; + /* fallthrough */ + case MBHC_BTN_DET_V_BTN_HIGH: + ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn; + /* fallthrough */ + case MBHC_BTN_DET_V_BTN_LOW: + /* do nothing */ + break; + default: + ret = NULL; + } + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_mbhc_cal_btn_det_mp); + +static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + const s16 v_ins_hu = wcd9xxx_get_current_v(mbhc, + WCD9XXX_CURRENT_V_INS_HU); + const s16 v_b1_hu = wcd9xxx_get_current_v(mbhc, + WCD9XXX_CURRENT_V_B1_HU); + const s16 v_b1_h = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H); + const s16 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H); + + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, + (v_ins_hu >> 8) & 0xFF); + + if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) { + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & + 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, + (v_b1_hu >> 8) & 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, v_b1_h & + 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, + (v_b1_h >> 8) & 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & + 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL, + (v_brh >> 8) & 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL, + mbhc->mbhc_data.v_brl & 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL, + (mbhc->mbhc_data.v_brl >> 8) & 0xFF); + } +} + +static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc, + bool fast) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd9xxx_cfilt_mode cfilt_mode; + + if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) { + cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast); + } else { + if (fast) + cfilt_mode.reg_mode_val = WCD9XXX_CFILT_FAST_MODE; + else + cfilt_mode.reg_mode_val = WCD9XXX_CFILT_SLOW_MODE; + + cfilt_mode.reg_mask = 0x40; + cfilt_mode.cur_mode_val = + snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40; + } + + if (cfilt_mode.cur_mode_val + != cfilt_mode.reg_mode_val) { + if (mbhc->polling_active && wcd9xxx_mbhc_polling(mbhc)) + wcd9xxx_pause_hs_polling(mbhc); + snd_soc_update_bits(codec, + mbhc->mbhc_bias_regs.cfilt_ctl, + cfilt_mode.reg_mask, + cfilt_mode.reg_mode_val); + if (mbhc->polling_active && wcd9xxx_mbhc_polling(mbhc)) + wcd9xxx_start_hs_polling(mbhc); + pr_debug("%s: CFILT mode change (%x to %x)\n", __func__, + cfilt_mode.cur_mode_val, + cfilt_mode.reg_mode_val); + } else { + pr_debug("%s: CFILT Value is already %x\n", + __func__, cfilt_mode.cur_mode_val); + } +} + +static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc, + struct snd_soc_jack *jack, int status, int mask) +{ + if (jack == &mbhc->headset_jack) { + wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr, + WCD9XXX_COND_HPH_MIC, + status & SND_JACK_MICROPHONE); + wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr, + WCD9XXX_COND_HPH, + status & SND_JACK_HEADPHONE); + } + + snd_soc_jack_report(jack, status, mask); +} + +static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status, + int irq) +{ + struct snd_soc_codec *codec; + + pr_debug("%s: clear ocp status %x\n", __func__, jack_status); + codec = mbhc->codec; + if (mbhc->hph_status & jack_status) { + mbhc->hph_status &= ~jack_status; + wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, WCD9XXX_JACK_MASK); + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10, + 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10, + 0x10); + /* + * reset retry counter as PA is turned off signifying + * start of new OCP detection session + */ + if (mbhc->intr_ids->hph_left_ocp) + mbhc->hphlocp_cnt = 0; + else + mbhc->hphrocp_cnt = 0; + wcd9xxx_enable_irq(mbhc->resmgr->core_res, irq); + } +} + +static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status) +{ + __hphocp_off_report(mbhc, SND_JACK_OC_HPHR, + mbhc->intr_ids->hph_right_ocp); +} + +static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status) +{ + __hphocp_off_report(mbhc, SND_JACK_OC_HPHL, + mbhc->intr_ids->hph_left_ocp); +} + +static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc, + enum wcd9xxx_mbhc_micbias_type mb_type) +{ + unsigned int cfilt; + struct wcd9xxx_micbias_setting *micbias_pdata = + mbhc->resmgr->micbias_pdata; + struct mbhc_micbias_regs *micbias_regs; + enum wcd9xxx_micbias_num mb_num; + + if (mb_type == MBHC_ANC_MIC_MB) { + micbias_regs = &mbhc->mbhc_anc_bias_regs; + mb_num = mbhc->mbhc_cfg->anc_micbias; + } else { + micbias_regs = &mbhc->mbhc_bias_regs; + mb_num = mbhc->mbhc_cfg->micbias; + } + + switch (mb_num) { + case MBHC_MICBIAS1: + cfilt = micbias_pdata->bias1_cfilt_sel; + micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC; + micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS; + micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL; + break; + case MBHC_MICBIAS2: + cfilt = micbias_pdata->bias2_cfilt_sel; + micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC; + micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS; + micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL; + break; + case MBHC_MICBIAS3: + cfilt = micbias_pdata->bias3_cfilt_sel; + micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC; + micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS; + micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL; + break; + case MBHC_MICBIAS4: + cfilt = micbias_pdata->bias4_cfilt_sel; + micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc; + micbias_regs->int_rbias = + mbhc->resmgr->reg_addr->micb_4_int_rbias; + micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl; + break; + default: + /* Should never reach here */ + pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__); + return; + } + + micbias_regs->cfilt_sel = cfilt; + + switch (cfilt) { + case WCD9XXX_CFILT1_SEL: + micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL; + micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL; + break; + case WCD9XXX_CFILT2_SEL: + micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL; + micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL; + break; + case WCD9XXX_CFILT3_SEL: + micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL; + micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL; + break; + } + + if (mb_type == MBHC_PRIMARY_MIC_MB) { + switch (cfilt) { + case WCD9XXX_CFILT1_SEL: + mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt1_mv; + break; + case WCD9XXX_CFILT2_SEL: + mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt2_mv; + break; + case WCD9XXX_CFILT3_SEL: + mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt3_mv; + break; + } + } + +} + +static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc) +{ + bool pa_turned_on = false; + struct snd_soc_codec *codec = mbhc->codec; + u8 wg_time; + + wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME); + wg_time += 1; + + if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, + &mbhc->hph_pa_dac_state)) { + pr_debug("%s: HPHR clear flag and enable DAC\n", __func__); + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, + 0xC0, 0xC0); + } + if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, + &mbhc->hph_pa_dac_state)) { + pr_debug("%s: HPHL clear flag and enable DAC\n", __func__); + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, + 0x80, 0x80); + } + + if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, + &mbhc->hph_pa_dac_state)) { + pr_debug("%s: HPHR clear flag and enable PA\n", __func__); + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10, + 1 << 4); + pa_turned_on = true; + } + if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, + &mbhc->hph_pa_dac_state)) { + pr_debug("%s: HPHL clear flag and enable PA\n", __func__); + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1 + << 5); + pa_turned_on = true; + } + + if (pa_turned_on) { + pr_debug("%s: PA was turned on by MBHC and not by DAPM\n", + __func__); + usleep_range(wg_time * 1000, wg_time * 1000 + 50); + } +} + +static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc) +{ + int r; + + r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork); + if (r) + /* if scheduled mbhc.mbhc_btn_dwork is canceled from here, + * we have to unlock from here instead btn_work + */ + wcd9xxx_unlock_sleep(mbhc->resmgr->core_res); + return r; +} + +static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left) +{ + u8 hph_reg_val = 0; + + if (left) + hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL); + else + hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL); + + return (hph_reg_val & 0xC0) ? true : false; +} + +static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec) +{ + u8 hph_reg_val = 0; + + hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN); + + return (hph_reg_val & 0x30) ? true : false; +} + +/* called under codec_resource_lock acquisition */ +static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc) +{ + u8 wg_time; + struct snd_soc_codec *codec = mbhc->codec; + + wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME); + wg_time += 1; + + /* If headphone PA is on, check if userspace receives + * removal event to sync-up PA's state + */ + if (wcd9xxx_is_hph_pa_on(codec)) { + pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__); + set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + } else { + pr_debug("%s PA is off\n", __func__); + } + + if (wcd9xxx_is_hph_dac_on(codec, 1)) + set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state); + if (wcd9xxx_is_hph_dac_on(codec, 0)) + set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state); + + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0x80, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00); + usleep_range(wg_time * 1000, wg_time * 1000 + 50); +} + +static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins) +{ + if (!mbhc->mbhc_cfg->insert_detect) + return; + pr_debug("%s: Setting up %s detection\n", __func__, + ins ? "insert" : "removal"); + /* Disable detection to avoid glitch */ + snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0); + if (mbhc->mbhc_cfg->gpio_level_insert) + snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, + (0x68 | (ins ? (1 << 1) : 0))); + else + snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, + (0x6C | (ins ? (1 << 1) : 0))); + /* Re-enable detection */ + snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 1); +} + +/* called under codec_resource_lock acquisition */ +static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion, + enum snd_jack_types jack_type) +{ + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + + pr_debug("%s: enter insertion %d hph_status %x\n", + __func__, insertion, mbhc->hph_status); + if (!insertion) { + /* Report removal */ + mbhc->hph_status &= ~jack_type; + /* + * cancel possibly scheduled btn work and + * report release if we reported button press + */ + if (wcd9xxx_cancel_btn_work(mbhc)) + pr_debug("%s: button press is canceled\n", __func__); + else if (mbhc->buttons_pressed) { + pr_debug("%s: release of button press%d\n", + __func__, jack_type); + wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0, + mbhc->buttons_pressed); + mbhc->buttons_pressed &= + ~WCD9XXX_JACK_BUTTON_MASK; + } + + if (mbhc->micbias_enable && mbhc->micbias_enable_cb) { + pr_debug("%s: Disabling micbias\n", __func__); + mbhc->micbias_enable = false; + mbhc->micbias_enable_cb(mbhc->codec, false, + mbhc->mbhc_cfg->micbias); + } + mbhc->zl = mbhc->zr = 0; + mbhc->hph_type = MBHC_HPH_NONE; + pr_debug("%s: Reporting removal %d(%x)\n", __func__, + jack_type, mbhc->hph_status); + wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status, + WCD9XXX_JACK_MASK); + wcd9xxx_set_and_turnoff_hph_padac(mbhc); + hphrocp_off_report(mbhc, SND_JACK_OC_HPHR); + hphlocp_off_report(mbhc, SND_JACK_OC_HPHL); + mbhc->current_plug = PLUG_TYPE_NONE; + mbhc->polling_active = false; + if (mbhc->mbhc_cb && mbhc->mbhc_cb->hph_auto_pulldown_ctrl) + mbhc->mbhc_cb->hph_auto_pulldown_ctrl(mbhc->codec, + false); + } else { + /* + * Report removal of current jack type. + * Headphone to headset shouldn't report headphone + * removal. + */ + if (mbhc->mbhc_cfg->detect_extn_cable && + !(mbhc->current_plug == PLUG_TYPE_HEADPHONE && + jack_type == SND_JACK_HEADSET) && + (mbhc->hph_status && mbhc->hph_status != jack_type)) { + if (mbhc->micbias_enable && mbhc->micbias_enable_cb && + mbhc->hph_status == SND_JACK_HEADSET) { + pr_debug("%s: Disabling micbias\n", __func__); + mbhc->micbias_enable = false; + mbhc->micbias_enable_cb(mbhc->codec, false, + mbhc->mbhc_cfg->micbias); + } + + pr_debug("%s: Reporting removal (%x)\n", + __func__, mbhc->hph_status); + mbhc->zl = mbhc->zr = 0; + wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, + 0, WCD9XXX_JACK_MASK); + mbhc->hph_status &= ~(SND_JACK_HEADSET | + SND_JACK_LINEOUT | + SND_JACK_ANC_HEADPHONE | + SND_JACK_UNSUPPORTED); + if (mbhc->mbhc_cb && + mbhc->mbhc_cb->hph_auto_pulldown_ctrl) + mbhc->mbhc_cb->hph_auto_pulldown_ctrl( + mbhc->codec, + false); + } + + /* Report insertion */ + if (jack_type == SND_JACK_HEADPHONE) { + mbhc->current_plug = PLUG_TYPE_HEADPHONE; + } else if (jack_type == SND_JACK_UNSUPPORTED) { + mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP; + } else if (jack_type == SND_JACK_HEADSET) { + mbhc->polling_active = BUTTON_POLLING_SUPPORTED; + mbhc->current_plug = PLUG_TYPE_HEADSET; + mbhc->update_z = true; + } else if (jack_type == SND_JACK_LINEOUT) { + mbhc->current_plug = PLUG_TYPE_HIGH_HPH; + } else if (jack_type == SND_JACK_ANC_HEADPHONE) { + mbhc->polling_active = BUTTON_POLLING_SUPPORTED; + mbhc->current_plug = PLUG_TYPE_ANC_HEADPHONE; + } + + if (mbhc->impedance_detect && impedance_detect_en) { + wcd9xxx_detect_impedance(mbhc, + &mbhc->zl, &mbhc->zr); + if ((mbhc->zl > WCD9XXX_LINEIN_THRESHOLD) && + (mbhc->zr > WCD9XXX_LINEIN_THRESHOLD)) { + jack_type = SND_JACK_LINEOUT; + mbhc->current_plug = PLUG_TYPE_HIGH_HPH; + pr_debug("%s: Replace with SND_JACK_LINEOUT\n", + __func__); + } + } + + mbhc->hph_status |= jack_type; + + if (mbhc->micbias_enable && mbhc->micbias_enable_cb) { + pr_debug("%s: Enabling micbias\n", __func__); + mbhc->micbias_enable_cb(mbhc->codec, true, + mbhc->mbhc_cfg->micbias); + } + + pr_debug("%s: Reporting insertion %d(%x)\n", __func__, + jack_type, mbhc->hph_status); + wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, + (mbhc->hph_status | SND_JACK_MECHANICAL), + WCD9XXX_JACK_MASK); + /* + * if PA is already on, switch micbias + * source to VDDIO + */ + if (((mbhc->current_plug == PLUG_TYPE_HEADSET) || + (mbhc->current_plug == PLUG_TYPE_ANC_HEADPHONE)) && + ((mbhc->event_state & (1 << MBHC_EVENT_PA_HPHL | + 1 << MBHC_EVENT_PA_HPHR)))) + __wcd9xxx_switch_micbias(mbhc, 1, false, + false); + wcd9xxx_clr_and_turnon_hph_padac(mbhc); + } + /* Setup insert detect */ + wcd9xxx_insert_detect_setup(mbhc, !insertion); + + pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status); +} + +/* should be called under interrupt context that hold suspend */ +static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc, + struct work_struct *work) +{ + pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__); + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + mbhc->hs_detect_work_stop = false; + wcd9xxx_lock_sleep(mbhc->resmgr->core_res); + schedule_work(work); +} + +/* called under codec_resource_lock acquisition */ +static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc, + struct work_struct *work) +{ + pr_debug("%s: Canceling correct_plug_swch\n", __func__); + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + mbhc->hs_detect_work_stop = true; + + /* Make sure mbhc state update complete before unlocking. */ + wmb(); + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + if (cancel_work_sync(work)) { + pr_debug("%s: correct_plug_swch is canceled\n", + __func__); + wcd9xxx_unlock_sleep(mbhc->resmgr->core_res); + } + WCD9XXX_BCL_LOCK(mbhc->resmgr); +} + +static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio) +{ + int r; + int vddio_k, mb_k; + + vddio_k = __wcd9xxx_resmgr_get_k_val(mbhc, VDDIO_MICBIAS_MV); + mb_k = __wcd9xxx_resmgr_get_k_val(mbhc, mbhc->mbhc_data.micb_mv); + if (tovddio) + r = v * (vddio_k + 4) / (mb_k + 4); + else + r = v * (mb_k + 4) / (vddio_k + 4); + return r; +} + +static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc) +{ + s16 v_hs_max; + struct wcd9xxx_mbhc_plug_type_cfg *plug_type; + + plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration); + if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) && + mbhc->mbhc_micbias_switched) + v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true); + else + v_hs_max = plug_type->v_hs_max; + return v_hs_max; +} + +static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec) +{ + u8 bias_msb, bias_lsb; + short bias_value; + + bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS); + bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS); + bias_value = (bias_msb << 8) | bias_lsb; + return bias_value; +} + +static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec) +{ + u8 bias_msb, bias_lsb; + short bias_value; + + bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS); + bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS); + bias_value = (bias_msb << 8) | bias_lsb; + return bias_value; +} + +static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec, + bool on) +{ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1); +} + +static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce, + bool override_bypass, bool noreldetection) +{ + short bias_value; + struct snd_soc_codec *codec = mbhc->codec; + + wcd9xxx_disable_irq(mbhc->resmgr->core_res, + mbhc->intr_ids->dce_est_complete); + if (noreldetection) + wcd9xxx_turn_onoff_rel_detection(codec, false); + + if (mbhc->mbhc_cfg->do_recalibration) + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, + 0x0); + /* Turn on the override */ + if (!override_bypass) + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4); + if (dce) { + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, + 0x8); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, + 0x0); + if (mbhc->mbhc_cfg->do_recalibration) + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, + 0x2, 0x2); + usleep_range(mbhc->mbhc_data.t_sta_dce, + mbhc->mbhc_data.t_sta_dce + 50); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4); + usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce + 50); + bias_value = wcd9xxx_read_dce_result(codec); + } else { + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, + 0x8); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, + 0x0); + if (mbhc->mbhc_cfg->do_recalibration) + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, + 0x2, 0x2); + usleep_range(mbhc->mbhc_data.t_sta_dce, + mbhc->mbhc_data.t_sta_dce + 50); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2); + usleep_range(mbhc->mbhc_data.t_sta, + mbhc->mbhc_data.t_sta + 50); + bias_value = wcd9xxx_read_sta_result(codec); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, + 0x8); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0); + } + /* Turn off the override after measuring mic voltage */ + if (!override_bypass) + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, + 0x00); + + if (noreldetection) + wcd9xxx_turn_onoff_rel_detection(codec, true); + wcd9xxx_enable_irq(mbhc->resmgr->core_res, + mbhc->intr_ids->dce_est_complete); + + return bias_value; +} + +static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce, + bool norel) +{ + bool override_bypass; + + /* Bypass override if it is already enabled */ + override_bypass = (snd_soc_read(mbhc->codec, + WCD9XXX_A_CDC_MBHC_B1_CTL) & + 0x04) ? true : false; + + return __wcd9xxx_codec_sta_dce(mbhc, dce, override_bypass, norel); +} + +static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce, + u16 bias_value, s16 z, u32 micb_mv) +{ + s16 value, mb; + s32 mv = 0; + + value = bias_value; + if (dce) { + mb = (mbhc->mbhc_data.dce_mb); + if (mb - z) + mv = (value - z) * (s32)micb_mv / (mb - z); + } else { + mb = (mbhc->mbhc_data.sta_mb); + if (mb - z) + mv = (value - z) * (s32)micb_mv / (mb - z); + } + + return mv; +} + +static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce, + u16 bias_value) +{ + s16 z; + + z = dce ? (s16)mbhc->mbhc_data.dce_z : (s16)mbhc->mbhc_data.sta_z; + return __wcd9xxx_codec_sta_dce_v(mbhc, dce, bias_value, z, + mbhc->mbhc_data.micb_mv); +} + +/* To enable/disable bandgap and RC oscillator */ +static void wcd9xxx_mbhc_ctrl_clk_bandgap(struct wcd9xxx_mbhc *mbhc, + bool enable) +{ + if (enable) { + WCD9XXX_BG_CLK_LOCK(mbhc->resmgr); + wcd9xxx_resmgr_get_bandgap(mbhc->resmgr, + WCD9XXX_BANDGAP_AUDIO_MODE); + if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_rco_ctrl) { + WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr); + mbhc->mbhc_cb->codec_rco_ctrl(mbhc->codec, true); + } else { + wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, + WCD9XXX_CLK_RCO); + WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr); + } + } else { + if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_rco_ctrl) { + mbhc->mbhc_cb->codec_rco_ctrl(mbhc->codec, false); + WCD9XXX_BG_CLK_LOCK(mbhc->resmgr); + } else { + WCD9XXX_BG_CLK_LOCK(mbhc->resmgr); + wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, + WCD9XXX_CLK_RCO); + } + wcd9xxx_resmgr_put_bandgap(mbhc->resmgr, + WCD9XXX_BANDGAP_AUDIO_MODE); + WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr); + } +} + +/* called only from interrupt which is under codec_resource_lock acquisition */ +static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc, + struct mbhc_micbias_regs *mbhc_micb_regs, + bool is_cs_enable) +{ + struct snd_soc_codec *codec = mbhc->codec; + short bias_value; + u8 cfilt_mode; + + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + + pr_debug("%s: enter\n", __func__); + if (!mbhc->mbhc_cfg->calibration) { + pr_err("%s: Error, no calibration exists\n", __func__); + return -ENODEV; + } + + /* Enable external voltage source to micbias if present */ + if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(codec, true, true); + + /* + * setup internal micbias if codec uses internal micbias for + * headset detection + */ + if (mbhc->mbhc_cfg->use_int_rbias) { + if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) + mbhc->mbhc_cb->setup_int_rbias(codec, true); + else + pr_err("%s: internal bias requested but codec did not provide callback\n", + __func__); + } + + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01); + + /* Make sure CFILT is in fast mode, save current mode */ + cfilt_mode = snd_soc_read(codec, mbhc_micb_regs->cfilt_ctl); + if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode) + mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc); + else + snd_soc_update_bits(codec, mbhc_micb_regs->cfilt_ctl, + 0x70, 0x00); + + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2); + snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, + mbhc->scaling_mux_in); + pr_debug("%s: scaling_mux_input: %d\n", __func__, + mbhc->scaling_mux_in); + + if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block) + mbhc->mbhc_cb->enable_mux_bias_block(codec); + else + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, + 0x80, 0x80); + + snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80); + snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C); + snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40); + + snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00); + + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8); + + if (!mbhc->mbhc_cfg->do_recalibration) { + if (!is_cs_enable) + wcd9xxx_calibrate_hs_polling(mbhc); + } + + /* don't flip override */ + bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true); + snd_soc_write(codec, mbhc_micb_regs->cfilt_ctl, cfilt_mode); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00); + + return bias_value; +} + +static void wcd9xxx_recalibrate(struct wcd9xxx_mbhc *mbhc, + struct mbhc_micbias_regs *mbhc_micb_regs, + bool is_cs_enable) +{ + struct snd_soc_codec *codec = mbhc->codec; + s16 reg; + int change; + struct wcd9xxx_mbhc_btn_detect_cfg *btn_det; + s16 sta_z = 0, dce_z = 0; + + btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration); + + if (mbhc->mbhc_cfg->do_recalibration) { + /* recalibrate dce_z and sta_z */ + reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL); + change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, + 0x78, btn_det->mbhc_nsc << 3); + wcd9xxx_get_z(mbhc, &dce_z, &sta_z, mbhc_micb_regs, true); + if (change) + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg); + if (dce_z && sta_z) { + pr_debug("%s: sta_z 0x%x -> 0x%x, dce_z 0x%x -> 0x%x\n", + __func__, + mbhc->mbhc_data.sta_z, sta_z & 0xffff, + mbhc->mbhc_data.dce_z, dce_z & 0xffff); + mbhc->mbhc_data.dce_z = dce_z; + mbhc->mbhc_data.sta_z = sta_z; + wcd9xxx_mbhc_calc_thres(mbhc); + wcd9xxx_calibrate_hs_polling(mbhc); + } else { + pr_warn("%s: failed get new dce_z/sta_z 0x%x/0x%x\n", + __func__, dce_z, sta_z); + } + + if (is_cs_enable) { + /* recalibrate dce_nsc_cs_z */ + reg = snd_soc_read(mbhc->codec, + WCD9XXX_A_CDC_MBHC_B1_CTL); + snd_soc_update_bits(mbhc->codec, + WCD9XXX_A_CDC_MBHC_B1_CTL, + 0x78, WCD9XXX_MBHC_NSC_CS << 3); + wcd9xxx_get_z(mbhc, &dce_z, NULL, mbhc_micb_regs, + true); + snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL, + reg); + if (dce_z) { + mbhc->mbhc_data.dce_nsc_cs_z = dce_z; + /* update v_cs_ins_h with new dce_nsc_cs_z */ + mbhc->mbhc_data.v_cs_ins_h = + wcd9xxx_codec_v_sta_dce( + mbhc, DCE, + WCD9XXX_V_CS_HS_MAX, + is_cs_enable); + pr_debug("%s: dce_nsc_cs_z 0x%x -> 0x%x, v_cs_ins_h 0x%x\n", + __func__, + mbhc->mbhc_data.dce_nsc_cs_z, + dce_z & 0xffff, + mbhc->mbhc_data.v_cs_ins_h); + } else { + pr_debug("%s: failed get new dce_nsc_cs_z\n", + __func__); + } + } + } +} + +static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + const struct wcd9xxx_mbhc_general_cfg *generic = + WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration); + + /* Need MBHC clock */ + if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_rco_ctrl) + mbhc->mbhc_cb->codec_rco_ctrl(mbhc->codec, true); + else { + WCD9XXX_BG_CLK_LOCK(mbhc->resmgr); + wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO); + WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr); + } + + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0); + __wcd9xxx_switch_micbias(mbhc, 0, false, false); + + usleep_range(generic->t_shutdown_plug_rem, + generic->t_shutdown_plug_rem + 50); + + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8); + + if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_rco_ctrl) + mbhc->mbhc_cb->codec_rco_ctrl(mbhc->codec, false); + else { + WCD9XXX_BG_CLK_LOCK(mbhc->resmgr); + /* Put requested CLK back */ + wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO); + WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr); + } + + snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00); +} + +static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc) +{ + + pr_debug("%s: enter\n", __func__); + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + + wcd9xxx_shutdown_hs_removal_detect(mbhc); + + + /* Disable external voltage source to micbias if present */ + if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(mbhc->codec, false, true); + + mbhc->polling_active = false; + mbhc->mbhc_state = MBHC_STATE_NONE; + pr_debug("%s: leave\n", __func__); +} + +/* called under codec_resource_lock acquisition */ +static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on) +{ + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on); + if (on) + usleep_range(5000, 5100); +} + +static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on) +{ + pr_debug("%s: vddio %d\n", __func__, on); + + if (mbhc->mbhc_cb && mbhc->mbhc_cb->pull_mb_to_vddio) { + mbhc->mbhc_cb->pull_mb_to_vddio(mbhc->codec, on); + goto exit; + } + + if (on) { + snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg, + 1 << 7, 1 << 7); + snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL, + 1 << 4, 0); + } else { + snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL, + 1 << 4, 1 << 4); + snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg, + 1 << 7, 0); + } + +exit: + /* + * Wait for the micbias to settle down to vddio + * when the micbias to vddio switch is enabled. + */ + if (on) + usleep_range(10000, 10100); +} + +static int wcd9xxx_hphl_status(struct wcd9xxx_mbhc *mbhc) +{ + u16 hph, status; + struct snd_soc_codec *codec = mbhc->codec; + + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + hph = snd_soc_read(codec, WCD9XXX_A_MBHC_HPH); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x02); + usleep_range(WCD9XXX_HPHL_STATUS_READY_WAIT_US, + WCD9XXX_HPHL_STATUS_READY_WAIT_US + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + status = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_STATUS); + snd_soc_write(codec, WCD9XXX_A_MBHC_HPH, hph); + return status; +} + +static enum wcd9xxx_mbhc_plug_type +wcd9xxx_cs_find_plug_type(struct wcd9xxx_mbhc *mbhc, + struct wcd9xxx_mbhc_detect *dt, const int size, + bool highhph, + unsigned long event_state) +{ + int i; + int vdce, mb_mv; + int ch, sz, delta_thr; + int minv = 0, maxv = INT_MIN; + struct wcd9xxx_mbhc_detect *d = dt; + struct wcd9xxx_mbhc_detect *dprev = d, *dmicbias = NULL, *dgnd = NULL; + enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID; + + const struct wcd9xxx_mbhc_plug_type_cfg *plug_type = + WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration); + s16 hs_max, no_mic, dce_z; + int highhph_cnt = 0; + + pr_debug("%s: enter\n", __func__); + pr_debug("%s: event_state 0x%lx\n", __func__, event_state); + + sz = size - 1; + for (i = 0, d = dt, ch = 0; i < sz; i++, d++) { + if (d->mic_bias) { + dce_z = mbhc->mbhc_data.dce_z; + mb_mv = mbhc->mbhc_data.micb_mv; + hs_max = plug_type->v_hs_max; + no_mic = plug_type->v_no_mic; + } else { + dce_z = mbhc->mbhc_data.dce_nsc_cs_z; + mb_mv = VDDIO_MICBIAS_MV; + hs_max = WCD9XXX_V_CS_HS_MAX; + no_mic = WCD9XXX_V_CS_NO_MIC; + } + + vdce = __wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce, + dce_z, (u32)mb_mv); + d->_vdces = vdce; + if (d->_vdces < no_mic) + d->_type = PLUG_TYPE_HEADPHONE; + else if (d->_vdces >= hs_max) { + d->_type = PLUG_TYPE_HIGH_HPH; + highhph_cnt++; + } else + d->_type = PLUG_TYPE_HEADSET; + + pr_debug("%s: DCE #%d, %04x, V %04d(%04d), HPHL %d TYPE %d\n", + __func__, i, d->dce, vdce, d->_vdces, + d->hphl_status & 0x01, + d->_type); + + ch += d->hphl_status & 0x01; + if (!d->swap_gnd && !d->mic_bias) { + if (maxv < d->_vdces) + maxv = d->_vdces; + if (!minv || minv > d->_vdces) + minv = d->_vdces; + } + if ((!d->mic_bias && + (d->_vdces >= WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV && + d->_vdces <= WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV)) || + (d->mic_bias && + (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV && + d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV))) { + pr_debug("%s: within invalid range\n", __func__); + type = PLUG_TYPE_INVALID; + goto exit; + } + } + + delta_thr = ((highhph_cnt == sz) || highhph) ? + WCD9XXX_MB_MEAS_DELTA_MAX_MV : + WCD9XXX_CS_MEAS_DELTA_MAX_MV; + + for (i = 0, d = dt; i < sz; i++, d++) { + if ((i > 0) && !d->mic_bias && !d->swap_gnd && + (d->_type != dprev->_type)) { + pr_debug("%s: Invalid, inconsistent types\n", __func__); + type = PLUG_TYPE_INVALID; + goto exit; + } + + if (!d->swap_gnd && !d->mic_bias && + (abs(minv - d->_vdces) > delta_thr || + abs(maxv - d->_vdces) > delta_thr)) { + pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n", + __func__, d->_vdces, minv, maxv); + type = PLUG_TYPE_INVALID; + goto exit; + } else if (d->swap_gnd) { + dgnd = d; + } + + if (!d->mic_bias && !d->swap_gnd) + dprev = d; + else if (d->mic_bias) + dmicbias = d; + } + if (dgnd && dt->_type != PLUG_TYPE_HEADSET && + dt->_type != dgnd->_type) { + pr_debug("%s: Invalid, inconsistent types\n", __func__); + type = PLUG_TYPE_INVALID; + goto exit; + } + + type = dt->_type; + if (dmicbias) { + if (dmicbias->_type == PLUG_TYPE_HEADSET && + (dt->_type == PLUG_TYPE_HIGH_HPH || + dt->_type == PLUG_TYPE_HEADSET)) { + type = PLUG_TYPE_HEADSET; + if (dt->_type == PLUG_TYPE_HIGH_HPH) { + pr_debug("%s: Headset with threshold on MIC detected\n", + __func__); + if (mbhc->mbhc_cfg->micbias_enable_flags & + (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET)) + mbhc->micbias_enable = true; + } + } + } + + if (type == PLUG_TYPE_HEADSET && dgnd && !dgnd->mic_bias) { + /* if plug type is Headphone report as GND_MIC_SWAP */ + if (dgnd->_type == PLUG_TYPE_HEADPHONE) { + pr_debug("%s: GND_MIC_SWAP\n", __func__); + type = PLUG_TYPE_GND_MIC_SWAP; + /* + * if type is GND_MIC_SWAP we should not check + * HPHL status hence goto exit + */ + goto exit; + } else if (dgnd->_type != PLUG_TYPE_HEADSET && !dmicbias) { + pr_debug("%s: Invalid, inconsistent types\n", __func__); + type = PLUG_TYPE_INVALID; + } + } + + if (event_state & (1 << MBHC_EVENT_PA_HPHL)) { + pr_debug("%s: HPHL PA was ON\n", __func__); + } else if (ch != sz && ch > 0) { + pr_debug("%s: Invalid, inconsistent HPHL..\n", __func__); + type = PLUG_TYPE_INVALID; + goto exit; + } + + if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) { + if (((type == PLUG_TYPE_HEADSET || + type == PLUG_TYPE_HEADPHONE) && ch != sz)) { + pr_debug("%s: Invalid, not fully inserted, TYPE %d\n", + __func__, type); + type = PLUG_TYPE_INVALID; + } + } + + if (type == PLUG_TYPE_HEADSET && + (mbhc->mbhc_cfg->micbias_enable_flags & + (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET))) + mbhc->micbias_enable = true; + +exit: + pr_debug("%s: Plug type %d detected\n", __func__, type); + return type; +} + +/* + * wcd9xxx_find_plug_type : Find out and return the best plug type with given + * list of wcd9xxx_mbhc_detect structure. + * param mbhc wcd9xxx_mbhc structure + * param dt collected measurements + * param size array size of dt + * param event_state mbhc->event_state when dt is collected + */ +static enum wcd9xxx_mbhc_plug_type +wcd9xxx_find_plug_type(struct wcd9xxx_mbhc *mbhc, + struct wcd9xxx_mbhc_detect *dt, const int size, + unsigned long event_state) +{ + int i; + int ch; + enum wcd9xxx_mbhc_plug_type type; + int vdce; + struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL, *dvddio = NULL; + int maxv = 0, minv = 0; + const struct wcd9xxx_mbhc_plug_type_cfg *plug_type = + WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration); + const s16 hs_max = plug_type->v_hs_max; + const s16 no_mic = plug_type->v_no_mic; + + pr_debug("%s: event_state 0x%lx\n", __func__, event_state); + + for (i = 0, d = dt, ch = 0; i < size; i++, d++) { + vdce = wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce); + if (d->vddio) + d->_vdces = scale_v_micb_vddio(mbhc, vdce, false); + else + d->_vdces = vdce; + + if (d->_vdces >= no_mic && d->_vdces < hs_max) + d->_type = PLUG_TYPE_HEADSET; + else if (d->_vdces < no_mic) + d->_type = PLUG_TYPE_HEADPHONE; + else + d->_type = PLUG_TYPE_HIGH_HPH; + + ch += d->hphl_status & 0x01; + if (!d->swap_gnd && !d->hwvalue && !d->vddio) { + if (maxv < d->_vdces) + maxv = d->_vdces; + if (!minv || minv > d->_vdces) + minv = d->_vdces; + } + + pr_debug("%s: DCE #%d, %04x, V %04d(%04d), GND %d, VDDIO %d, HPHL %d TYPE %d\n", + __func__, i, d->dce, vdce, d->_vdces, + d->swap_gnd, d->vddio, d->hphl_status & 0x01, + d->_type); + + + /* + * If GND and MIC prongs are aligned to HPHR and GND of + * headphone, codec measures the voltage based on + * impedance between HPHR and GND which results in ~80mv. + * Avoid this. + */ + if (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV && + d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) { + pr_debug("%s: within invalid range\n", __func__); + type = PLUG_TYPE_INVALID; + goto exit; + } + } + + if (event_state & (1 << MBHC_EVENT_PA_HPHL)) { + pr_debug("%s: HPHL PA was ON\n", __func__); + } else if (ch != size && ch > 0) { + pr_debug("%s: Invalid, inconsistent HPHL\n", __func__); + type = PLUG_TYPE_INVALID; + goto exit; + } + + for (i = 0, dprev = NULL, d = dt; i < size; i++, d++) { + if (d->vddio) { + dvddio = d; + continue; + } + + if ((i > 0) && (dprev != NULL) && (d->_type != dprev->_type)) { + pr_debug("%s: Invalid, inconsistent types\n", __func__); + type = PLUG_TYPE_INVALID; + goto exit; + } + + if (!d->swap_gnd && !d->hwvalue && + (abs(minv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV || + abs(maxv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV)) { + pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n", + __func__, d->_vdces, minv, maxv); + type = PLUG_TYPE_INVALID; + goto exit; + } else if (d->swap_gnd) { + dgnd = d; + } + dprev = d; + } + + WARN_ON(i != size); + type = dt->_type; + if (type == PLUG_TYPE_HEADSET && dgnd) { + if ((dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MIN_MV < + minv) && + (dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MAX_MV > + maxv)) + type = PLUG_TYPE_GND_MIC_SWAP; + } + + /* if HPHL PA was on, we cannot use hphl status */ + if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) { + if (((type == PLUG_TYPE_HEADSET || + type == PLUG_TYPE_HEADPHONE) && ch != size) || + (type == PLUG_TYPE_GND_MIC_SWAP && ch)) { + pr_debug("%s: Invalid, not fully inserted, TYPE %d\n", + __func__, type); + type = PLUG_TYPE_INVALID; + } + } + + if (type == PLUG_TYPE_HEADSET) { + if (dvddio && ((dvddio->_vdces > hs_max) || + (dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD))) { + pr_debug("%s: Headset with threshold on MIC detected\n", + __func__); + if (mbhc->mbhc_cfg->micbias_enable_flags & + (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET)) + mbhc->micbias_enable = true; + } else { + pr_debug("%s: Headset with regular MIC detected\n", + __func__); + if (mbhc->mbhc_cfg->micbias_enable_flags & + (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET)) + mbhc->micbias_enable = true; + } + } +exit: + pr_debug("%s: Plug type %d detected, micbias_enable %d\n", __func__, + type, mbhc->micbias_enable); + return type; +} + +/* + * Pull down MBHC micbias for provided duration in microsecond. + */ +static int wcd9xxx_pull_down_micbias(struct wcd9xxx_mbhc *mbhc, int us) +{ + bool micbiasconn = false; + struct snd_soc_codec *codec = mbhc->codec; + const u16 ctlreg = mbhc->mbhc_bias_regs.ctl_reg; + + /* + * Disable MBHC to micbias connection to pull down + * micbias and pull down micbias for a moment. + */ + if ((snd_soc_read(mbhc->codec, ctlreg) & 0x01)) { + WARN_ONCE(1, "MBHC micbias is already pulled down unexpectedly\n"); + return -EFAULT; + } + + if ((snd_soc_read(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL) & 1 << 4)) { + snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL, + 1 << 4, 0); + micbiasconn = true; + } + + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01); + + /* + * Pull down for 1ms to discharge bias. Give small margin (10us) to be + * able to get consistent result across DCEs. + */ + usleep_range(1000, 1000 + 10); + + if (micbiasconn) + snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL, + 1 << 4, 1 << 4); + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00); + usleep_range(us, us + WCD9XXX_USLEEP_RANGE_MARGIN_US); + + return 0; +} + +/* Called under codec resource lock acquisition */ +void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc, + struct mbhc_micbias_regs *mbhc_micb_regs, + bool on, bool highhph) +{ + struct snd_soc_codec *codec; + struct wcd9xxx_mbhc_btn_detect_cfg *btn_det; + const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det = + WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration); + + btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration); + codec = mbhc->codec; + + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + + if ((on && mbhc->is_cs_enabled) || + (!on && !mbhc->is_cs_enabled)) { + pr_debug("%s: Current source is already %s\n", + __func__, on ? "ON" : "OFF"); + return; + } + + if (on) { + pr_debug("%s: enabling current source\n", __func__); + /* Nsc to 9 */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, + 0x78, 0x48); + /* pull down diode bit to 0 */ + snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg, + 0x01, 0x00); + /* + * Keep the low power insertion/removal + * detection (reg 0x3DD) disabled + */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, + 0x01, 0x00); + /* + * Enable the Mic Bias current source + * Write bits[6:5] of register MICB_2_MBHC to 0x3 (V_20_UA) + * Write bit[7] of register MICB_2_MBHC to 1 + * (INS_DET_ISRC_EN__ENABLE) + * MICB_2_MBHC__SCHT_TRIG_EN to 1 + */ + snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg, + 0xF0, 0xF0); + /* Disconnect MBHC Override from MicBias and LDOH */ + snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x00); + mbhc->is_cs_enabled = true; + } else { + pr_debug("%s: disabling current source\n", __func__); + /* Connect MBHC Override from MicBias and LDOH */ + snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x10); + /* INS_DET_ISRC_CTL to acdb value */ + snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg, + 0x60, plug_det->mic_current << 5); + if (!highhph) { + /* INS_DET_ISRC_EN__ENABLE to 0 */ + snd_soc_update_bits(codec, + mbhc_micb_regs->mbhc_reg, + 0x80, 0x00); + /* MICB_2_MBHC__SCHT_TRIG_EN to 0 */ + snd_soc_update_bits(codec, + mbhc_micb_regs->mbhc_reg, + 0x10, 0x00); + } + /* Nsc to acdb value */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78, + btn_det->mbhc_nsc << 3); + mbhc->is_cs_enabled = false; + } +} + +static enum wcd9xxx_mbhc_plug_type +wcd9xxx_codec_cs_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT]; + enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID; + int i; + + pr_debug("%s: enter\n", __func__); + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + + BUG_ON(NUM_DCE_PLUG_INS_DETECT < 4); + + wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true); + rt[0].swap_gnd = false; + rt[0].vddio = false; + rt[0].hwvalue = true; + rt[0].hphl_status = wcd9xxx_hphl_status(mbhc); + rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, &mbhc->mbhc_bias_regs, + true); + rt[0].mic_bias = false; + + for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) { + rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 3); + rt[i].mic_bias = ((i == NUM_DCE_PLUG_INS_DETECT - 4) && + highhph); + rt[i].hphl_status = wcd9xxx_hphl_status(mbhc); + if (rt[i].swap_gnd) + wcd9xxx_codec_hphr_gnd_switch(codec, true); + + if (rt[i].mic_bias) + wcd9xxx_turn_onoff_current_source(mbhc, + &mbhc->mbhc_bias_regs, + false, false); + + rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, !highhph, true); + if (rt[i].mic_bias) + wcd9xxx_turn_onoff_current_source(mbhc, + &mbhc->mbhc_bias_regs, + true, false); + if (rt[i].swap_gnd) + wcd9xxx_codec_hphr_gnd_switch(codec, false); + } + + /* recalibrate DCE/STA GND voltages */ + wcd9xxx_recalibrate(mbhc, &mbhc->mbhc_bias_regs, true); + + type = wcd9xxx_cs_find_plug_type(mbhc, rt, ARRAY_SIZE(rt), highhph, + mbhc->event_state); + + wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false); + pr_debug("%s: plug_type:%d\n", __func__, type); + + return type; +} + +static enum wcd9xxx_mbhc_plug_type +wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph) +{ + int i; + bool vddioon; + struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr; + struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT]; + enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID; + struct snd_soc_codec *codec = mbhc->codec; + + pr_debug("%s: enter\n", __func__); + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + + /* make sure override is on */ + WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04)); + + /* GND and MIC swap detection requires at least 2 rounds of DCE */ + BUG_ON(NUM_DCE_PLUG_INS_DETECT < 2); + detect_use_vddio_switch = mbhc->mbhc_cfg->use_vddio_meas; + + /* + * There are chances vddio switch is on and cfilt voltage is adjusted + * to vddio voltage even after plug type removal reported. + */ + vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false); + pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off"); + + plug_type_ptr = + WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration); + + /* + * cfilter in fast mode requires 1ms to charge up and down micbias + * fully. + */ + (void) wcd9xxx_pull_down_micbias(mbhc, + WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US); + + wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true); + rt[0].hphl_status = wcd9xxx_hphl_status(mbhc); + rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, &mbhc->mbhc_bias_regs, + false); + rt[0].swap_gnd = false; + rt[0].vddio = false; + rt[0].hwvalue = true; + for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) { + rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2); + if (detect_use_vddio_switch) + rt[i].vddio = (i == 1); + else + rt[i].vddio = false; + rt[i].hphl_status = wcd9xxx_hphl_status(mbhc); + rt[i].hwvalue = false; + if (rt[i].swap_gnd) + wcd9xxx_codec_hphr_gnd_switch(codec, true); + if (rt[i].vddio) + wcd9xxx_onoff_vddio_switch(mbhc, true); + /* + * Pull down micbias to detect headset with mic which has + * threshold and to have more consistent voltage measurements. + * + * cfilter in fast mode requires 1ms to charge up and down + * micbias fully. + */ + (void) wcd9xxx_pull_down_micbias(mbhc, + WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US); + rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true); + if (rt[i].vddio) + wcd9xxx_onoff_vddio_switch(mbhc, false); + if (rt[i].swap_gnd) + wcd9xxx_codec_hphr_gnd_switch(codec, false); + } + /* recalibrate DCE/STA GND voltages */ + wcd9xxx_recalibrate(mbhc, &mbhc->mbhc_bias_regs, false); + + if (vddioon) + __wcd9xxx_switch_micbias(mbhc, 1, false, false); + + type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt), + mbhc->event_state); + + wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false); + pr_debug("%s: leave\n", __func__); + return type; +} + +static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc) +{ + if (mbhc->mbhc_cfg->gpio) + return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) != + mbhc->mbhc_cfg->gpio_level_insert); + else if (mbhc->mbhc_cfg->insert_detect) { + if (mbhc->mbhc_cb && mbhc->mbhc_cb->insert_rem_status) + return mbhc->mbhc_cb->insert_rem_status(mbhc->codec); + else + return snd_soc_read(mbhc->codec, + WCD9XXX_A_MBHC_INSERT_DET_STATUS) & + (1 << 2); + } else + WARN(1, "Invalid jack detection configuration\n"); + + return true; +} + +static bool is_clk_active(struct snd_soc_codec *codec) +{ + return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05); +} + +static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc, + int insertion, int trigger, bool padac_off) +{ + struct snd_soc_codec *codec = mbhc->codec; + int central_bias_enabled = 0; + const struct wcd9xxx_mbhc_general_cfg *generic = + WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration); + const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det = + WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration); + + pr_debug("%s: enter insertion(%d) trigger(0x%x)\n", + __func__, insertion, trigger); + + if (!mbhc->mbhc_cfg->calibration) { + pr_err("Error, no wcd9xxx calibration\n"); + return -EINVAL; + } + + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0); + + /* + * Make sure mic bias and Mic line schmitt trigger + * are turned OFF + */ + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01); + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00); + + if (insertion) { + wcd9xxx_switch_micbias(mbhc, 0); + + /* DAPM can manipulate PA/DAC bits concurrently */ + if (padac_off == true) + wcd9xxx_set_and_turnoff_hph_padac(mbhc); + + if (trigger & MBHC_USE_HPHL_TRIGGER) { + /* Enable HPH Schmitt Trigger */ + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11, + 0x11); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C, + plug_det->hph_current << 2); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02, + 0x02); + } + if (trigger & MBHC_USE_MB_TRIGGER) { + /* enable the mic line schmitt trigger */ + snd_soc_update_bits(codec, + mbhc->mbhc_bias_regs.mbhc_reg, + 0x60, plug_det->mic_current << 5); + snd_soc_update_bits(codec, + mbhc->mbhc_bias_regs.mbhc_reg, + 0x80, 0x80); + usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + snd_soc_update_bits(codec, + mbhc->mbhc_bias_regs.ctl_reg, 0x01, + 0x00); + snd_soc_update_bits(codec, + mbhc->mbhc_bias_regs.mbhc_reg, + 0x10, 0x10); + } + + /* setup for insetion detection */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0); + } else { + pr_debug("setup for removal detection\n"); + /* Make sure the HPH schmitt trigger is OFF */ + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00); + + /* enable the mic line schmitt trigger */ + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, + 0x01, 0x00); + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60, + plug_det->mic_current << 5); + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, + 0x80, 0x80); + usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, + 0x10, 0x10); + + /* Setup for low power removal detection */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, + 0x2); + } + + if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) { + /* called by interrupt */ + if (!is_clk_active(codec)) { + wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 1); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, + 0x06, 0); + usleep_range(generic->t_shutdown_plug_rem, + generic->t_shutdown_plug_rem + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0); + } else + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, + 0x06, 0); + } + + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0); + + /* If central bandgap disabled */ + if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) { + snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3); + usleep_range(generic->t_bg_fast_settle, + generic->t_bg_fast_settle + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + central_bias_enabled = 1; + } + + /* If LDO_H disabled */ + if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) { + snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0); + snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80); + usleep_range(generic->t_ldoh, generic->t_ldoh + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0); + + if (central_bias_enabled) + snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1, + 0); + } + + if (mbhc->resmgr->reg_addr && mbhc->resmgr->reg_addr->micb_4_mbhc) + snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, + 0x3, mbhc->mbhc_cfg->micbias); + + wcd9xxx_enable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1); + pr_debug("%s: leave\n", __func__); + + return 0; +} + +/* + * Function to determine whether anc microphone is preset or not. + * Return true if anc microphone is detected or false if not detected. + */ +static bool wcd9xxx_detect_anc_plug_type(struct wcd9xxx_mbhc *mbhc) +{ + struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT - 1]; + bool anc_mic_found = true; + int i, mb_mv; + const struct wcd9xxx_mbhc_plug_type_cfg *plug_type = + WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration); + s16 hs_max, dce_z; + s16 no_mic; + bool override_en; + bool timedout; + unsigned long timeout, retry = 0; + enum wcd9xxx_mbhc_plug_type type; + bool cs_enable; + + if (mbhc->mbhc_cfg->anc_micbias != MBHC_MICBIAS3 && + mbhc->mbhc_cfg->anc_micbias != MBHC_MICBIAS2) + return false; + + pr_debug("%s: enter\n", __func__); + + override_en = (snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & + 0x04) ? true : false; + cs_enable = ((mbhc->mbhc_cfg->cs_enable_flags & + (1 << MBHC_CS_ENABLE_DET_ANC)) != 0) && + (!(snd_soc_read(mbhc->codec, + mbhc->mbhc_anc_bias_regs.ctl_reg) & 0x80)) && + (mbhc->mbhc_cfg->micbias != mbhc->mbhc_cfg->anc_micbias); + + if (cs_enable) { + wcd9xxx_turn_onoff_current_source(mbhc, + &mbhc->mbhc_anc_bias_regs, + true, false); + } else { + if (mbhc->mbhc_cfg->anc_micbias == MBHC_MICBIAS3) { + if (mbhc->micbias_enable_cb) + mbhc->micbias_enable_cb(mbhc->codec, true, + mbhc->mbhc_cfg->anc_micbias); + else + return false; + } else { + /* Enable override */ + if (!override_en) + wcd9xxx_turn_onoff_override(mbhc, true); + } + } + + if (!cs_enable) { + hs_max = plug_type->v_hs_max; + no_mic = plug_type->v_no_mic; + dce_z = mbhc->mbhc_data.dce_z; + mb_mv = mbhc->mbhc_data.micb_mv; + } else { + hs_max = WCD9XXX_V_CS_HS_MAX; + no_mic = WCD9XXX_V_CS_NO_MIC; + mb_mv = VDDIO_MICBIAS_MV; + dce_z = mbhc->mbhc_data.dce_nsc_cs_z; + } + + wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true); + + timeout = jiffies + msecs_to_jiffies(ANC_HPH_DETECT_PLUG_TIME_MS); + anc_mic_found = true; + + while (!(timedout = time_after(jiffies, timeout))) { + retry++; + + if (wcd9xxx_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + anc_mic_found = false; + break; + } + + pr_debug("%s: Retry attempt %lu", __func__, retry - 1); + + rt[0].hphl_status = wcd9xxx_hphl_status(mbhc); + rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, + &mbhc->mbhc_anc_bias_regs, + cs_enable); + rt[0]._vdces = __wcd9xxx_codec_sta_dce_v(mbhc, true, rt[0].dce, + dce_z, (u32)mb_mv); + + if (rt[0]._vdces >= no_mic && rt[0]._vdces < hs_max) + rt[0]._type = PLUG_TYPE_HEADSET; + else if (rt[0]._vdces < no_mic) + rt[0]._type = PLUG_TYPE_HEADPHONE; + else + rt[0]._type = PLUG_TYPE_HIGH_HPH; + + pr_debug("%s: DCE #%d, V %04d, HPHL %d TYPE %d\n", + __func__, 0, rt[0]._vdces, + rt[0].hphl_status & 0x01, + rt[0]._type); + + for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) { + rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, + true, true); + rt[i]._vdces = __wcd9xxx_codec_sta_dce_v(mbhc, true, + rt[i].dce, dce_z, + (u32) mb_mv); + + if (rt[i]._vdces >= no_mic && rt[i]._vdces < hs_max) + rt[i]._type = PLUG_TYPE_HEADSET; + else if (rt[i]._vdces < no_mic) + rt[i]._type = PLUG_TYPE_HEADPHONE; + else + rt[i]._type = PLUG_TYPE_HIGH_HPH; + + rt[i].hphl_status = wcd9xxx_hphl_status(mbhc); + + pr_debug("%s: DCE #%d, V %04d, HPHL %d TYPE %d\n", + __func__, i, rt[i]._vdces, + rt[i].hphl_status & 0x01, + rt[i]._type); + } + + /* + * Check for the "type" of all the 4 measurements + * If all 4 measurements have the Type as PLUG_TYPE_HEADSET + * then it is proper mic and declare that the plug has two mics + */ + for (i = 0; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) { + if (i > 0 && (rt[i - 1]._type != rt[i]._type)) { + type = PLUG_TYPE_INVALID; + break; + } else { + type = rt[0]._type; + } + } + + pr_debug("%s: Plug type found in ANC detection :%d", + __func__, type); + + if (type != PLUG_TYPE_HEADSET) + anc_mic_found = false; + if (anc_mic_found || (type == PLUG_TYPE_HEADPHONE && + mbhc->mbhc_cfg->hw_jack_type == FIVE_POLE_JACK) || + (type == PLUG_TYPE_HIGH_HPH && + mbhc->mbhc_cfg->hw_jack_type == SIX_POLE_JACK)) + break; + } + + wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false); + if (cs_enable) { + wcd9xxx_turn_onoff_current_source(mbhc, + &mbhc->mbhc_anc_bias_regs, + false, false); + } else { + if (mbhc->mbhc_cfg->anc_micbias == MBHC_MICBIAS3) { + if (mbhc->micbias_enable_cb) + mbhc->micbias_enable_cb(mbhc->codec, false, + mbhc->mbhc_cfg->anc_micbias); + } else { + /* Disable override */ + if (!override_en) + wcd9xxx_turn_onoff_override(mbhc, false); + } + } + pr_debug("%s: leave\n", __func__); + return anc_mic_found; +} + +/* called under codec_resource_lock acquisition */ +static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc, + enum wcd9xxx_mbhc_plug_type plug_type) +{ + bool anc_mic_found = false; + + pr_debug("%s: enter current_plug(%d) new_plug(%d)\n", + __func__, mbhc->current_plug, plug_type); + + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + + if (plug_type == PLUG_TYPE_HEADPHONE && + mbhc->current_plug == PLUG_TYPE_NONE) { + /* + * Nothing was reported previously + * report a headphone or unsupported + */ + wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE); + wcd9xxx_cleanup_hs_polling(mbhc); + } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) { + if (!mbhc->mbhc_cfg->detect_extn_cable) { + if (mbhc->current_plug == PLUG_TYPE_HEADSET) + wcd9xxx_report_plug(mbhc, 0, + SND_JACK_HEADSET); + else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) + wcd9xxx_report_plug(mbhc, 0, + SND_JACK_HEADPHONE); + } + wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED); + wcd9xxx_cleanup_hs_polling(mbhc); + } else if (plug_type == PLUG_TYPE_HEADSET) { + + if (mbhc->mbhc_cfg->enable_anc_mic_detect) { + /* + * Do not report Headset, because at this point + * it could be a ANC headphone having two mics. + * So, proceed further to detect if there is a + * second mic. + */ + mbhc->scaling_mux_in = 0x08; + anc_mic_found = wcd9xxx_detect_anc_plug_type(mbhc); + } + + if (anc_mic_found) { + /* Report ANC headphone */ + wcd9xxx_report_plug(mbhc, 1, SND_JACK_ANC_HEADPHONE); + } else { + /* + * If Headphone was reported previously, this will + * only report the mic line + */ + wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET); + } + /* Button detection required RC oscillator */ + wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true); + /* + * sleep so that audio path completely tears down + * before report plug insertion to the user space + */ + msleep(100); + + wcd9xxx_start_hs_polling(mbhc); + } else if (plug_type == PLUG_TYPE_HIGH_HPH) { + if (mbhc->mbhc_cfg->detect_extn_cable) { + /* High impedance device found. Report as LINEOUT*/ + if (mbhc->current_plug == PLUG_TYPE_NONE) + wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT); + wcd9xxx_cleanup_hs_polling(mbhc); + pr_debug("%s: setup mic trigger for further detection\n", + __func__); + mbhc->lpi_enabled = true; + /* + * Do not enable HPHL trigger. If playback is active, + * it might lead to continuous false HPHL triggers + */ + wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER, + false); + } else { + if (mbhc->current_plug == PLUG_TYPE_NONE) + wcd9xxx_report_plug(mbhc, 1, + SND_JACK_HEADPHONE); + wcd9xxx_cleanup_hs_polling(mbhc); + pr_debug("setup mic trigger for further detection\n"); + mbhc->lpi_enabled = true; + wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER | + MBHC_USE_HPHL_TRIGGER, + false); + } + } else { + WARN(1, "Unexpected current plug_type %d, plug_type %d\n", + mbhc->current_plug, plug_type); + } + pr_debug("%s: leave\n", __func__); +} + +/* called under codec_resource_lock acquisition */ +static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc) +{ + enum wcd9xxx_mbhc_plug_type plug_type; + bool current_source_enable; + + pr_debug("%s: enter\n", __func__); + + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + + current_source_enable = (((mbhc->mbhc_cfg->cs_enable_flags & + (1 << MBHC_CS_ENABLE_INSERTION)) != 0) && + (!(snd_soc_read(mbhc->codec, + mbhc->mbhc_bias_regs.ctl_reg) & 0x80))); + + mbhc->scaling_mux_in = 0x04; + + if (current_source_enable) { + wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs, + true, false); + plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false); + /* + * For other plug types, the current source disable + * will be done from wcd9xxx_correct_swch_plug + */ + if (plug_type == PLUG_TYPE_HEADSET) + wcd9xxx_turn_onoff_current_source(mbhc, + &mbhc->mbhc_bias_regs, + false, false); + } else { + wcd9xxx_turn_onoff_override(mbhc, true); + plug_type = wcd9xxx_codec_get_plug_type(mbhc, true); + wcd9xxx_turn_onoff_override(mbhc, false); + } + + if (wcd9xxx_swch_level_remove(mbhc)) { + if (current_source_enable && mbhc->is_cs_enabled) { + wcd9xxx_turn_onoff_current_source(mbhc, + &mbhc->mbhc_bias_regs, + false, false); + } + pr_debug("%s: Switch level is low when determining plug\n", + __func__); + return; + } + + if (plug_type == PLUG_TYPE_INVALID || + plug_type == PLUG_TYPE_GND_MIC_SWAP) { + wcd9xxx_cleanup_hs_polling(mbhc); + wcd9xxx_schedule_hs_detect_plug(mbhc, + &mbhc->correct_plug_swch); + } else if (plug_type == PLUG_TYPE_HEADPHONE) { + wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE); + wcd9xxx_cleanup_hs_polling(mbhc); + wcd9xxx_schedule_hs_detect_plug(mbhc, + &mbhc->correct_plug_swch); + } else if (plug_type == PLUG_TYPE_HIGH_HPH) { + wcd9xxx_cleanup_hs_polling(mbhc); + wcd9xxx_schedule_hs_detect_plug(mbhc, + &mbhc->correct_plug_swch); + } else { + pr_debug("%s: Valid plug found, determine plug type %d\n", + __func__, plug_type); + wcd9xxx_find_plug_and_report(mbhc, plug_type); + } + pr_debug("%s: leave\n", __func__); +} + +/* called under codec_resource_lock acquisition */ +static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc) +{ + pr_debug("%s: enter\n", __func__); + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + + if (wcd9xxx_swch_level_remove(mbhc)) + pr_debug("%s: Switch level low when determining plug\n", + __func__); + else + wcd9xxx_mbhc_decide_swch_plug(mbhc); + pr_debug("%s: leave\n", __func__); +} + +/* called only from interrupt which is under codec_resource_lock acquisition */ +static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc, + bool is_removal) +{ + if (!is_removal) { + pr_debug("%s: MIC trigger insertion interrupt\n", __func__); + + /* Make sure memory read is completed before reading + * lpi_enabled. + */ + rmb(); + if (mbhc->lpi_enabled) + msleep(100); + + /* Make sure memory read is completed before reading + * lpi_enabled. + */ + rmb(); + if (!mbhc->lpi_enabled) { + pr_debug("%s: lpi is disabled\n", __func__); + } else if (!wcd9xxx_swch_level_remove(mbhc)) { + pr_debug("%s: Valid insertion, detect plug type\n", + __func__); + wcd9xxx_mbhc_decide_swch_plug(mbhc); + } else { + pr_debug("%s: Invalid insertion stop plug detection\n", + __func__); + } + } else if (mbhc->mbhc_cfg->detect_extn_cable) { + pr_debug("%s: Removal\n", __func__); + if (!wcd9xxx_swch_level_remove(mbhc)) { + /* + * Switch indicates, something is still inserted. + * This could be extension cable i.e. headset is + * removed from extension cable. + */ + /* cancel detect plug */ + wcd9xxx_cancel_hs_detect_plug(mbhc, + &mbhc->correct_plug_swch); + wcd9xxx_mbhc_decide_swch_plug(mbhc); + } + } else { + pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__); + } +} + +static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv, + bool cs_enable) +{ + const struct wcd9xxx_mbhc_plug_type_cfg *plug_type = + WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration); + const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc); + + if (cs_enable) + return ((mic_mv > WCD9XXX_V_CS_NO_MIC) && + (mic_mv < WCD9XXX_V_CS_HS_MAX)) ? true : false; + else + return (!(mic_mv > WCD9XXX_MEAS_INVALD_RANGE_LOW_MV && + mic_mv < WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) && + (mic_mv > plug_type->v_no_mic) && + (mic_mv < v_hs_max)) ? true : false; +} + +/* + * called under codec_resource_lock acquisition + * returns true if mic voltage range is back to normal insertion + * returns false either if timedout or removed + */ +static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc) +{ + int i; + bool timedout, settled = false; + s32 mic_mv[NUM_DCE_PLUG_DETECT]; + short mb_v[NUM_DCE_PLUG_DETECT]; + unsigned long retry = 0, timeout; + bool cs_enable; + + cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags & + (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) && + (!(snd_soc_read(mbhc->codec, + mbhc->mbhc_bias_regs.ctl_reg) & 0x80))); + if (cs_enable) + wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs, + true, false); + + timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS); + while (!(timedout = time_after(jiffies, timeout))) { + retry++; + if (wcd9xxx_swch_level_remove(mbhc)) { + pr_debug("%s: Switch indicates removal\n", __func__); + break; + } + + if (retry > 1) + msleep(250); + else + msleep(50); + + if (wcd9xxx_swch_level_remove(mbhc)) { + pr_debug("%s: Switch indicates removal\n", __func__); + break; + } + + if (cs_enable) { + for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) { + mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1, + true, true); + mic_mv[i] = __wcd9xxx_codec_sta_dce_v(mbhc, + true, + mb_v[i], + mbhc->mbhc_data.dce_nsc_cs_z, + (u32)VDDIO_MICBIAS_MV); + pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n", + __func__, retry, mic_mv[i], mb_v[i]); + } + } else { + for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) { + mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1, + true); + mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1, + mb_v[i]); + pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n", + __func__, retry, mic_mv[i], + mb_v[i]); + } + } + + if (wcd9xxx_swch_level_remove(mbhc)) { + pr_debug("%s: Switcn indicates removal\n", __func__); + break; + } + + if (mbhc->current_plug == PLUG_TYPE_NONE) { + pr_debug("%s : headset/headphone is removed\n", + __func__); + break; + } + + for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) + if (!is_valid_mic_voltage(mbhc, mic_mv[i], cs_enable)) + break; + + if (i == NUM_DCE_PLUG_DETECT) { + pr_debug("%s: MIC voltage settled\n", __func__); + settled = true; + msleep(200); + break; + } + } + + if (cs_enable) + wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs, + false, false); + + if (timedout) + pr_debug("%s: Microphone did not settle in %d seconds\n", + __func__, HS_DETECT_PLUG_TIME_MS); + return settled; +} + +/* called only from interrupt which is under codec_resource_lock acquisition */ +static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc) +{ + pr_debug("%s: enter\n", __func__); + if (wcd9xxx_hs_remove_settle(mbhc)) + wcd9xxx_start_hs_polling(mbhc); + pr_debug("%s: leave\n", __func__); +} + +/* called only from interrupt which is under codec_resource_lock acquisition */ +static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc) +{ + s16 dce, dcez; + unsigned long timeout; + bool removed = true; + struct snd_soc_codec *codec = mbhc->codec; + const struct wcd9xxx_mbhc_general_cfg *generic = + WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration); + bool cs_enable; + s16 cur_v_ins_h; + u32 mb_mv; + + pr_debug("%s: enter\n", __func__); + if (mbhc->current_plug != PLUG_TYPE_HEADSET && + mbhc->current_plug != PLUG_TYPE_ANC_HEADPHONE) { + pr_debug("%s(): Headset is not inserted, ignore removal\n", + __func__); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, + 0x08, 0x08); + return; + } + + usleep_range(generic->t_shutdown_plug_rem, + generic->t_shutdown_plug_rem + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + + /* If micbias is enabled, don't enable current source */ + cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags & + (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) && + (!(snd_soc_read(codec, + mbhc->mbhc_bias_regs.ctl_reg) & 0x80))); + if (cs_enable) + wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs, + true, false); + + timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS); + do { + if (cs_enable) { + dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true); + dcez = mbhc->mbhc_data.dce_nsc_cs_z; + mb_mv = VDDIO_MICBIAS_MV; + } else { + dce = wcd9xxx_codec_sta_dce(mbhc, 1, true); + dcez = mbhc->mbhc_data.dce_z; + mb_mv = mbhc->mbhc_data.micb_mv; + } + + pr_debug("%s: DCE 0x%x,%d\n", __func__, dce, + __wcd9xxx_codec_sta_dce_v(mbhc, true, dce, + dcez, mb_mv)); + + cur_v_ins_h = cs_enable ? (s16) mbhc->mbhc_data.v_cs_ins_h : + (wcd9xxx_get_current_v(mbhc, + WCD9XXX_CURRENT_V_INS_H)); + + if (dce < cur_v_ins_h) { + removed = false; + break; + } + } while (!time_after(jiffies, timeout)); + pr_debug("%s: headset %sactually removed\n", __func__, + removed ? "" : "not "); + + if (cs_enable) + wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs, + false, false); + + if (removed) { + if (mbhc->mbhc_cfg->detect_extn_cable) { + if (!wcd9xxx_swch_level_remove(mbhc)) { + /* + * extension cable is still plugged in + * report it as LINEOUT device + */ + if (mbhc->hph_status == SND_JACK_HEADSET) + wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, + false); + wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT); + wcd9xxx_cleanup_hs_polling(mbhc); + wcd9xxx_enable_hs_detect(mbhc, 1, + MBHC_USE_MB_TRIGGER, + false); + } + } else { + /* Cancel possibly running hs_detect_work */ + wcd9xxx_cancel_hs_detect_plug(mbhc, + &mbhc->correct_plug_noswch); + /* + * If this removal is not false, first check the micbias + * switch status and switch it to LDOH if it is already + * switched to VDDIO. + */ + wcd9xxx_switch_micbias(mbhc, 0); + + wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET); + wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false); + wcd9xxx_cleanup_hs_polling(mbhc); + wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER | + MBHC_USE_HPHL_TRIGGER, + true); + } + } else { + wcd9xxx_start_hs_polling(mbhc); + } + pr_debug("%s: leave\n", __func__); +} + +/* called only from interrupt which is under codec_resource_lock acquisition */ +static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc, + bool is_mb_trigger) +{ + /* Cancel possibly running hs_detect_work */ + wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + + if (is_mb_trigger) { + pr_debug("%s: Waiting for Headphone left trigger\n", __func__); + wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false); + } else { + pr_debug("%s: HPHL trigger received, detecting plug type\n", + __func__); + wcd9xxx_mbhc_detect_plug_type(mbhc); + } +} + +static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data) +{ + struct wcd9xxx_mbhc *mbhc = data; + + pr_debug("%s: enter, removal interrupt\n", __func__); + WCD9XXX_BCL_LOCK(mbhc->resmgr); + /* + * While we don't know whether MIC is there or not, let the resmgr know + * so micbias can be disabled temporarily + */ + if (mbhc->current_plug == PLUG_TYPE_HEADSET) { + wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr, + WCD9XXX_COND_HPH_MIC, false); + wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr, + WCD9XXX_COND_HPH, false); + } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) { + wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr, + WCD9XXX_COND_HPH, false); + } + + if (mbhc->mbhc_cfg->detect_extn_cable && + !wcd9xxx_swch_level_remove(mbhc)) + wcd9xxx_hs_remove_irq_noswch(mbhc); + else + wcd9xxx_hs_remove_irq_swch(mbhc); + + if (mbhc->current_plug == PLUG_TYPE_HEADSET) { + wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr, + WCD9XXX_COND_HPH, true); + wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr, + WCD9XXX_COND_HPH_MIC, true); + } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) { + wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr, + WCD9XXX_COND_HPH, true); + } + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + + return IRQ_HANDLED; +} + +static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data) +{ + bool is_mb_trigger, is_removal; + struct wcd9xxx_mbhc *mbhc = data; + struct snd_soc_codec *codec = mbhc->codec; + + pr_debug("%s: enter\n", __func__); + WCD9XXX_BCL_LOCK(mbhc->resmgr); + wcd9xxx_disable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion); + + is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) & + 0x10); + is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00); + + /* Turn off both HPH and MIC line schmitt triggers */ + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00); + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00); + + if (mbhc->mbhc_cfg->detect_extn_cable && + mbhc->current_plug == PLUG_TYPE_HIGH_HPH) + wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger); + else + wcd9xxx_hs_insert_irq_swch(mbhc, is_removal); + + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + return IRQ_HANDLED; +} + +static void wcd9xxx_btn_lpress_fn(struct work_struct *work) +{ + struct delayed_work *dwork; + short bias_value; + int dce_mv, sta_mv; + struct wcd9xxx_mbhc *mbhc; + + pr_debug("%s:\n", __func__); + + dwork = to_delayed_work(work); + mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork); + + bias_value = wcd9xxx_read_sta_result(mbhc->codec); + sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value); + + bias_value = wcd9xxx_read_dce_result(mbhc->codec); + dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value); + pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv); + + pr_debug("%s: Reporting long button press event\n", __func__); + wcd9xxx_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed, + mbhc->buttons_pressed); + + pr_debug("%s: leave\n", __func__); + wcd9xxx_unlock_sleep(mbhc->resmgr->core_res); +} + +static void wcd9xxx_mbhc_insert_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd9xxx_mbhc *mbhc; + struct snd_soc_codec *codec; + struct wcd9xxx_core_resource *core_res; + + dwork = to_delayed_work(work); + mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork); + codec = mbhc->codec; + core_res = mbhc->resmgr->core_res; + + pr_debug("%s:\n", __func__); + + /* Turn off both HPH and MIC line schmitt triggers */ + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00); + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00); + wcd9xxx_disable_irq_sync(core_res, mbhc->intr_ids->insertion); + wcd9xxx_mbhc_detect_plug_type(mbhc); + wcd9xxx_unlock_sleep(core_res); +} + +static bool wcd9xxx_mbhc_fw_validate(const void *data, size_t size) +{ + u32 cfg_offset; + struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg; + struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg; + struct firmware_cal fw; + + fw.data = (void *)data; + fw.size = size; + + if (fw.size < WCD9XXX_MBHC_CAL_MIN_SIZE) + return false; + + /* + * Previous check guarantees that there is enough fw data up + * to num_btn + */ + btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw.data); + cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data); + if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg))) + return false; + + /* + * Previous check guarantees that there is enough fw data up + * to start of impedance detection configuration + */ + imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw.data); + cfg_offset = (u32) ((void *) imped_cfg - (void *) fw.data); + + if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ)) + return false; + + if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg))) + return false; + + return true; +} + +static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc, + enum meas_type dce, s16 vin_mv, + bool cs_enable) +{ + s16 diff, zero; + u32 mb_mv, in; + u16 value; + s16 dce_z; + + mb_mv = mbhc->mbhc_data.micb_mv; + dce_z = mbhc->mbhc_data.dce_z; + + if (mb_mv == 0) { + pr_err("%s: Mic Bias voltage is set to zero\n", __func__); + return -EINVAL; + } + if (cs_enable) { + mb_mv = VDDIO_MICBIAS_MV; + dce_z = mbhc->mbhc_data.dce_nsc_cs_z; + } + + if (dce) { + diff = (mbhc->mbhc_data.dce_mb) - (dce_z); + zero = (dce_z); + } else { + diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z); + zero = (mbhc->mbhc_data.sta_z); + } + in = (u32) diff * vin_mv; + + value = (u16) (in / mb_mv) + zero; + return value; +} + +static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc) +{ + struct snd_soc_codec *codec; + s16 adj_v_hs_max; + s16 btn_mv = 0, btn_mv_sta[MBHC_V_IDX_NUM], btn_mv_dce[MBHC_V_IDX_NUM]; + struct wcd9xxx_mbhc_btn_detect_cfg *btn_det; + struct wcd9xxx_mbhc_plug_type_cfg *plug_type; + u16 *btn_high; + int i; + + pr_debug("%s: enter\n", __func__); + codec = mbhc->codec; + btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration); + plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration); + + mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] = + wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max, false); + mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] = + wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max, false); + + mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW; + mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH; + + if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) { + adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, + true); + mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] = + wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max, false); + mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] = + wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max, false); + mbhc->mbhc_data.v_inval_ins_low = + scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low, + false); + mbhc->mbhc_data.v_inval_ins_high = + scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high, + false); + } + mbhc->mbhc_data.v_cs_ins_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE, + WCD9XXX_V_CS_HS_MAX, + true); + pr_debug("%s: v_ins_h for current source: 0x%x\n", __func__, + mbhc->mbhc_data.v_cs_ins_h); + + btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, + MBHC_BTN_DET_V_BTN_HIGH); + for (i = 0; i < btn_det->num_btn; i++) + btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv; + + btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta; + btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic; + btn_mv_sta[MBHC_V_IDX_VDDIO] = + scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true); + btn_mv_dce[MBHC_V_IDX_VDDIO] = + scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true); + + mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] = + wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT], + false); + mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] = + wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT], + false); + mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] = + wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO], + false); + mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] = + wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO], + false); + + mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] = + mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT]; + mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] = + mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO]; + + mbhc->mbhc_data.v_brl = BUTTON_MIN; + + mbhc->mbhc_data.v_no_mic = + wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic, false); + pr_debug("%s: leave\n", __func__); +} + +static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on) +{ + /* + * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK, + * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold + * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk() + */ + if (mbhc && mbhc->mbhc_cfg && mbhc->mbhc_cfg->mclk_cb_fn) + mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false); +} + +/* + * Mic Bias Enable Decision + * Return true if high_hph_cnt is a power of 2 (!= 2) + * otherwise return false + */ +static bool wcd9xxx_mbhc_enable_mb_decision(int high_hph_cnt) +{ + return (high_hph_cnt > 2) && !(high_hph_cnt & (high_hph_cnt - 1)); +} + +static inline void wcd9xxx_handle_gnd_mic_swap(struct wcd9xxx_mbhc *mbhc, + int pt_gnd_mic_swap_cnt, + enum wcd9xxx_mbhc_plug_type plug_type) +{ + if (mbhc->mbhc_cfg->swap_gnd_mic && + (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD)) { + /* + * if switch is toggled, check again, + * otherwise report unsupported plug + */ + mbhc->mbhc_cfg->swap_gnd_mic(mbhc->codec); + } else if (pt_gnd_mic_swap_cnt >= GND_MIC_SWAP_THRESHOLD) { + /* Report UNSUPPORTED plug + * and continue polling + */ + WCD9XXX_BCL_LOCK(mbhc->resmgr); + if (!mbhc->mbhc_cfg->detect_extn_cable) { + if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) + wcd9xxx_report_plug(mbhc, 0, + SND_JACK_HEADPHONE); + else if (mbhc->current_plug == PLUG_TYPE_HEADSET) + wcd9xxx_report_plug(mbhc, 0, + SND_JACK_HEADSET); + } + if (mbhc->current_plug != plug_type) + wcd9xxx_report_plug(mbhc, 1, + SND_JACK_UNSUPPORTED); + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + } +} + +static void wcd9xxx_correct_swch_plug(struct work_struct *work) +{ + struct wcd9xxx_mbhc *mbhc; + struct snd_soc_codec *codec; + enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID; + unsigned long timeout; + int retry = 0, pt_gnd_mic_swap_cnt = 0; + int highhph_cnt = 0; + bool correction = false; + bool current_source_enable; + bool wrk_complete = true, highhph = false; + + pr_debug("%s: enter\n", __func__); + + mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch); + codec = mbhc->codec; + + current_source_enable = (((mbhc->mbhc_cfg->cs_enable_flags & + (1 << MBHC_CS_ENABLE_POLLING)) != 0) && + (!(snd_soc_read(codec, + mbhc->mbhc_bias_regs.ctl_reg) & 0x80))); + + wcd9xxx_onoff_ext_mclk(mbhc, true); + + /* + * Keep override on during entire plug type correction work. + * + * This is okay under the assumption that any switch irqs which use + * MBHC block cancel and sync this work so override is off again + * prior to switch interrupt handler's MBHC block usage. + * Also while this correction work is running, we can guarantee + * DAPM doesn't use any MBHC block as this work only runs with + * headphone detection. + */ + if (current_source_enable) { + WCD9XXX_BCL_LOCK(mbhc->resmgr); + wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs, + true, false); + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + } else { + wcd9xxx_turn_onoff_override(mbhc, true); + } + + timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS); + while (!time_after(jiffies, timeout)) { + ++retry; + + /* Make sure any pending memory read is completed, before + * hs_detect_work_stop value is read. + */ + rmb(); + if (mbhc->hs_detect_work_stop) { + wrk_complete = false; + pr_debug("%s: stop requested\n", __func__); + break; + } + + msleep(HS_DETECT_PLUG_INERVAL_MS); + if (wcd9xxx_swch_level_remove(mbhc)) { + wrk_complete = false; + pr_debug("%s: Switch level is low\n", __func__); + break; + } + + /* can race with removal interrupt */ + WCD9XXX_BCL_LOCK(mbhc->resmgr); + if (current_source_enable) + plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, + highhph); + else + plug_type = wcd9xxx_codec_get_plug_type(mbhc, true); + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + + pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n", + __func__, retry, mbhc->current_plug, plug_type); + + highhph_cnt = (plug_type == PLUG_TYPE_HIGH_HPH) ? + (highhph_cnt + 1) : + 0; + highhph = wcd9xxx_mbhc_enable_mb_decision(highhph_cnt); + if (plug_type == PLUG_TYPE_INVALID) { + pr_debug("Invalid plug in attempt # %d\n", retry); + if (!mbhc->mbhc_cfg->detect_extn_cable && + retry == NUM_ATTEMPTS_TO_REPORT && + mbhc->current_plug == PLUG_TYPE_NONE) { + WCD9XXX_BCL_LOCK(mbhc->resmgr); + wcd9xxx_report_plug(mbhc, 1, + SND_JACK_HEADPHONE); + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + } + } else if (plug_type == PLUG_TYPE_HEADPHONE) { + pr_debug("Good headphone detected, continue polling\n"); + WCD9XXX_BCL_LOCK(mbhc->resmgr); + if (mbhc->mbhc_cfg->detect_extn_cable) { + if (mbhc->current_plug != plug_type) + wcd9xxx_report_plug(mbhc, 1, + SND_JACK_HEADPHONE); + } else if (mbhc->current_plug == PLUG_TYPE_NONE) { + wcd9xxx_report_plug(mbhc, 1, + SND_JACK_HEADPHONE); + } + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + } else if (plug_type == PLUG_TYPE_HIGH_HPH) { + pr_debug("%s: High HPH detected, continue polling\n", + __func__); + WCD9XXX_BCL_LOCK(mbhc->resmgr); + if (mbhc->mbhc_cfg->detect_extn_cable) { + if (mbhc->current_plug != plug_type) + wcd9xxx_report_plug(mbhc, 1, + SND_JACK_LINEOUT); + } else if (mbhc->current_plug == PLUG_TYPE_NONE) { + wcd9xxx_report_plug(mbhc, 1, + SND_JACK_HEADPHONE); + } + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + } else { + if (plug_type == PLUG_TYPE_GND_MIC_SWAP) { + pt_gnd_mic_swap_cnt++; + if (pt_gnd_mic_swap_cnt >= + GND_MIC_SWAP_THRESHOLD) + wcd9xxx_handle_gnd_mic_swap(mbhc, + pt_gnd_mic_swap_cnt, + plug_type); + pr_debug("%s: unsupported HS detected, continue polling\n", + __func__); + continue; + } else { + pt_gnd_mic_swap_cnt = 0; + + WCD9XXX_BCL_LOCK(mbhc->resmgr); + /* Turn off override/current source */ + if (current_source_enable) + wcd9xxx_turn_onoff_current_source(mbhc, + &mbhc->mbhc_bias_regs, + false, false); + else + wcd9xxx_turn_onoff_override(mbhc, + false); + /* + * The valid plug also includes + * PLUG_TYPE_GND_MIC_SWAP + */ + wcd9xxx_find_plug_and_report(mbhc, plug_type); + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + pr_debug("Attempt %d found correct plug %d\n", + retry, + plug_type); + correction = true; + } + break; + } + } + + highhph = false; + if (wrk_complete && plug_type == PLUG_TYPE_HIGH_HPH) { + pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n", + __func__); + WCD9XXX_BCL_LOCK(mbhc->resmgr); + wcd9xxx_find_plug_and_report(mbhc, plug_type); + highhph = true; + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + } + + if (plug_type == PLUG_TYPE_HEADPHONE) { + if (mbhc->mbhc_cb && mbhc->mbhc_cb->hph_auto_pulldown_ctrl) + mbhc->mbhc_cb->hph_auto_pulldown_ctrl(codec, true); + } + + if (!correction && current_source_enable) { + WCD9XXX_BCL_LOCK(mbhc->resmgr); + wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs, + false, highhph); + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + } else if (!correction) { + wcd9xxx_turn_onoff_override(mbhc, false); + } + + wcd9xxx_onoff_ext_mclk(mbhc, false); + + if (mbhc->mbhc_cfg->detect_extn_cable) { + WCD9XXX_BCL_LOCK(mbhc->resmgr); + if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE && + wrk_complete) || + mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP || + mbhc->current_plug == PLUG_TYPE_INVALID || + (plug_type == PLUG_TYPE_INVALID && wrk_complete)) { + /* Enable removal detection */ + wcd9xxx_cleanup_hs_polling(mbhc); + wcd9xxx_enable_hs_detect(mbhc, 0, 0, false); + } + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + } + pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug); + /* unlock sleep */ + wcd9xxx_unlock_sleep(mbhc->resmgr->core_res); +} + +static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc) +{ + bool insert; + bool is_removed = false; + struct snd_soc_codec *codec = mbhc->codec; + + pr_debug("%s: enter\n", __func__); + + mbhc->in_swch_irq_handler = true; + /* Wait here for debounce time */ + usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + + WCD9XXX_BCL_LOCK(mbhc->resmgr); + + /* cancel pending button press */ + if (wcd9xxx_cancel_btn_work(mbhc)) + pr_debug("%s: button press is canceled\n", __func__); + + insert = !wcd9xxx_swch_level_remove(mbhc); + pr_debug("%s: Current plug type %d, insert %d\n", __func__, + mbhc->current_plug, insert); + if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) { + + mbhc->lpi_enabled = false; + + /* Make sure mbhc state update complete before cancel detect + * plug. + */ + wmb(); + /* cancel detect plug */ + wcd9xxx_cancel_hs_detect_plug(mbhc, + &mbhc->correct_plug_swch); + + if ((mbhc->current_plug != PLUG_TYPE_NONE) && + (mbhc->current_plug != PLUG_TYPE_HIGH_HPH) && + !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) & + (1 << 1))) { + pr_debug("%s: current plug: %d\n", __func__, + mbhc->current_plug); + goto exit; + } + + /* Disable Mic Bias pull down and HPH Switch to GND */ + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, + 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00); + wcd9xxx_mbhc_detect_plug_type(mbhc); + } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) { + mbhc->lpi_enabled = false; + + /* Make sure mbhc state update complete before cancel detect + * plug. + */ + wmb(); + /* cancel detect plug */ + wcd9xxx_cancel_hs_detect_plug(mbhc, + &mbhc->correct_plug_swch); + + if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) { + wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE); + is_removed = true; + } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) { + wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED); + is_removed = true; + } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) { + wcd9xxx_pause_hs_polling(mbhc); + wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false); + wcd9xxx_cleanup_hs_polling(mbhc); + wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET); + is_removed = true; + } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) { + wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT); + is_removed = true; + } else if (mbhc->current_plug == PLUG_TYPE_ANC_HEADPHONE) { + wcd9xxx_pause_hs_polling(mbhc); + wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false); + wcd9xxx_cleanup_hs_polling(mbhc); + wcd9xxx_report_plug(mbhc, 0, SND_JACK_ANC_HEADPHONE); + is_removed = true; + } + + if (is_removed) { + snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, + 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, + 0x02, 0x00); + + /* Enable Mic Bias pull down and HPH Switch to GND */ + snd_soc_update_bits(codec, + mbhc->mbhc_bias_regs.ctl_reg, 0x01, + 0x01); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, + 0x01); + /* Make sure mic trigger is turned off */ + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, + 0x01, 0x01); + snd_soc_update_bits(codec, + mbhc->mbhc_bias_regs.mbhc_reg, + 0x90, 0x00); + /* Reset MBHC State Machine */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, + 0x08, 0x08); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, + 0x08, 0x00); + /* Turn off override */ + wcd9xxx_turn_onoff_override(mbhc, false); + } + } +exit: + mbhc->in_swch_irq_handler = false; + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + pr_debug("%s: leave\n", __func__); +} + +static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data) +{ + int r = IRQ_HANDLED; + struct wcd9xxx_mbhc *mbhc = data; + + pr_debug("%s: enter\n", __func__); + if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == false)) { + pr_warn("%s: failed to hold suspend\n", __func__); + r = IRQ_NONE; + } else { + /* Call handler */ + wcd9xxx_swch_irq_handler(mbhc); + wcd9xxx_unlock_sleep(mbhc->resmgr->core_res); + } + + pr_debug("%s: leave %d\n", __func__, r); + return r; +} + +static int wcd9xxx_is_false_press(struct wcd9xxx_mbhc *mbhc) +{ + s16 mb_v; + int i = 0; + int r = 0; + const s16 v_ins_hu = + wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU); + const s16 v_ins_h = + wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H); + const s16 v_b1_hu = + wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU); + const s16 v_b1_h = + wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H); + const unsigned long timeout = + jiffies + msecs_to_jiffies(BTN_RELEASE_DEBOUNCE_TIME_MS); + + while (time_before(jiffies, timeout)) { + /* + * This function needs to run measurements just few times during + * release debounce time. Make 1ms interval to avoid + * unnecessary excessive measurements. + */ + usleep_range(1000, 1000 + WCD9XXX_USLEEP_RANGE_MARGIN_US); + if (i == 0) { + mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true); + pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v, + wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v)); + if (mb_v < v_b1_hu || mb_v > v_ins_hu) { + r = 1; + break; + } + } else { + mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true); + pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v, + wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v)); + if (mb_v < v_b1_h || mb_v > v_ins_h) { + r = 1; + break; + } + } + i++; + } + + return r; +} + +/* called under codec_resource_lock acquisition */ +static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc, + const s32 micmv) +{ + s16 *v_btn_low, *v_btn_high; + struct wcd9xxx_mbhc_btn_detect_cfg *btn_det; + int i, btn = -1; + + btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration); + v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, + MBHC_BTN_DET_V_BTN_LOW); + v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, + MBHC_BTN_DET_V_BTN_HIGH); + + for (i = 0; i < btn_det->num_btn; i++) { + if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) { + btn = i; + break; + } + } + + if (btn == -1) + pr_debug("%s: couldn't find button number for mic mv %d\n", + __func__, micmv); + + return btn; +} + +static int wcd9xxx_get_button_mask(const int btn) +{ + int mask = 0; + + switch (btn) { + case 0: + mask = SND_JACK_BTN_0; + break; + case 1: + mask = SND_JACK_BTN_1; + break; + case 2: + mask = SND_JACK_BTN_2; + break; + case 3: + mask = SND_JACK_BTN_3; + break; + case 4: + mask = SND_JACK_BTN_4; + break; + case 5: + mask = SND_JACK_BTN_5; + break; + } + return mask; +} + +static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z, + struct mbhc_micbias_regs *micb_regs, + bool norel_detection) +{ + s16 reg0, reg1; + int change; + struct snd_soc_codec *codec = mbhc->codec; + + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + /* Pull down micbias to ground and disconnect vddio switch */ + reg0 = snd_soc_read(codec, micb_regs->ctl_reg); + snd_soc_update_bits(codec, micb_regs->ctl_reg, 0x81, 0x1); + reg1 = snd_soc_read(codec, micb_regs->mbhc_reg); + snd_soc_update_bits(codec, micb_regs->mbhc_reg, 1 << 7, 0); + + /* Disconnect override from micbias */ + change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, + 1 << 0); + usleep_range(1000, 1000 + 1000); + if (sta_z) { + *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, norel_detection); + pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF); + } + if (dce_z) { + *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, norel_detection); + pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF); + } + + /* Connect override from micbias */ + if (change) + snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, + 1 << 4); + /* Disable pull down micbias to ground */ + snd_soc_write(codec, micb_regs->mbhc_reg, reg1); + snd_soc_write(codec, micb_regs->ctl_reg, reg0); +} + +/* + * This function recalibrates dce_z and sta_z parameters. + * No release detection will be false when this function is + * used. + */ +void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc) +{ + const u16 sta_z = mbhc->mbhc_data.sta_z; + const u16 dce_z = mbhc->mbhc_data.dce_z; + + wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z, + &mbhc->mbhc_bias_regs, false); + pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n", + __func__, sta_z & 0xFFFF, dce_z & 0xFFFF, + mbhc->mbhc_data.sta_z & 0xFFFF, + mbhc->mbhc_data.dce_z & 0xFFFF); + + wcd9xxx_mbhc_calc_thres(mbhc); + wcd9xxx_calibrate_hs_polling(mbhc); +} + +/* + * wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold + * to ceilmv + buffer + */ +static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv, + bool vddio) +{ + u16 v_brh, v_b1_hu; + int mv; + struct wcd9xxx_mbhc_btn_detect_cfg *btn_det; + void *calibration = mbhc->mbhc_cfg->calibration; + struct snd_soc_codec *codec = mbhc->codec; + + btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration); + mv = ceilmv + btn_det->v_btn_press_delta_cic; + if (vddio) + mv = scale_v_micb_vddio(mbhc, mv, true); + pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv); + + if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) { + /* + * update LSB first so mbhc hardware block + * doesn't see too low value. + */ + v_b1_hu = wcd9xxx_codec_v_sta_dce(mbhc, STA, mv, false); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & + 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, + (v_b1_hu >> 8) & 0xFF); + v_brh = wcd9xxx_codec_v_sta_dce(mbhc, DCE, mv, false); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & + 0xFF); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL, + (v_brh >> 8) & 0xFF); + } + return 0; +} + +irqreturn_t wcd9xxx_dce_handler(int irq, void *data) +{ + int i, mask; + bool vddio; + u8 mbhc_status; + s16 dce_z, sta_z; + s32 stamv, stamv_s; + s16 *v_btn_high; + struct wcd9xxx_mbhc_btn_detect_cfg *btn_det; + int btn = -1, meas = 0; + struct wcd9xxx_mbhc *mbhc = data; + const struct wcd9xxx_mbhc_btn_detect_cfg *d = + WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration); + short btnmeas[d->n_btn_meas + 1]; + short dce[d->n_btn_meas + 1], sta; + s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1]; + struct snd_soc_codec *codec = mbhc->codec; + struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res; + int n_btn_meas = d->n_btn_meas; + void *calibration = mbhc->mbhc_cfg->calibration; + + pr_debug("%s: enter\n", __func__); + + WCD9XXX_BCL_LOCK(mbhc->resmgr); + mutex_lock(&mbhc->mbhc_lock); + mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E; + + if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) { + pr_debug("%s: mbhc is being recovered, skip button press\n", + __func__); + goto done; + } + + mbhc->mbhc_state = MBHC_STATE_POTENTIAL; + + if (!mbhc->polling_active) { + pr_warn("%s: mbhc polling is not active, skip button press\n", + __func__); + goto done; + } + + /* If switch nterrupt already kicked in, ignore button press */ + if (mbhc->in_swch_irq_handler) { + pr_debug("%s: Swtich level changed, ignore button press\n", + __func__); + btn = -1; + goto done; + } + + /* + * setup internal micbias if codec uses internal micbias for + * headset detection + */ + if (mbhc->mbhc_cfg->use_int_rbias) { + if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) + mbhc->mbhc_cb->setup_int_rbias(codec, true); + else + pr_err("%s: internal bias requested but codec did not provide callback\n", + __func__); + } + + + /* Measure scaled HW DCE */ + vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV && + mbhc->mbhc_micbias_switched); + + dce_z = mbhc->mbhc_data.dce_z; + sta_z = mbhc->mbhc_data.sta_z; + + /* Measure scaled HW STA */ + dce[0] = wcd9xxx_read_dce_result(codec); + sta = wcd9xxx_read_sta_result(codec); + if (mbhc_status != STATUS_REL_DETECTION) { + if (mbhc->mbhc_last_resume && + !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) { + pr_debug("%s: Button is released after resume\n", + __func__); + n_btn_meas = 0; + } else { + pr_debug("%s: Button is released without resume", + __func__); + if (mbhc->update_z) { + wcd9xxx_update_z(mbhc); + dce_z = mbhc->mbhc_data.dce_z; + sta_z = mbhc->mbhc_data.sta_z; + mbhc->update_z = true; + } + stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z, + mbhc->mbhc_data.micb_mv); + if (vddio) + stamv_s = scale_v_micb_vddio(mbhc, stamv, + false); + else + stamv_s = stamv; + mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], + dce_z, mbhc->mbhc_data.micb_mv); + mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], + false) : mv[0]; + btn = wcd9xxx_determine_button(mbhc, mv_s[0]); + if (btn != wcd9xxx_determine_button(mbhc, stamv_s)) + btn = -1; + goto done; + } + } + + for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); + meas++) + dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false); + + if (mbhc->update_z) { + wcd9xxx_update_z(mbhc); + dce_z = mbhc->mbhc_data.dce_z; + sta_z = mbhc->mbhc_data.sta_z; + mbhc->update_z = true; + } + + stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z, + mbhc->mbhc_data.micb_mv); + if (vddio) + stamv_s = scale_v_micb_vddio(mbhc, stamv, false); + else + stamv_s = stamv; + pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__, + sta & 0xFFFF, stamv, stamv_s); + + /* determine pressed button */ + mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z, + mbhc->mbhc_data.micb_mv); + mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0]; + btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]); + pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__, + dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]); + if (n_btn_meas == 0) + btn = btnmeas[0]; + for (meas = 1; (n_btn_meas && d->n_btn_meas && + (meas < (d->n_btn_meas + 1))); meas++) { + mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z, + mbhc->mbhc_data.micb_mv); + mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) : + mv[meas]; + btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]); + pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n", + __func__, meas, dce[meas] & 0xFFFF, mv[meas], + mv_s[meas], btnmeas[meas]); + /* + * if large enough measurements are collected, + * start to check if last all n_btn_con measurements were + * in same button low/high range + */ + if (meas + 1 >= d->n_btn_con) { + for (i = 0; i < d->n_btn_con; i++) + if ((btnmeas[meas] < 0) || + (btnmeas[meas] != btnmeas[meas - i])) + break; + if (i == d->n_btn_con) { + /* button pressed */ + btn = btnmeas[meas]; + break; + } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) { + /* + * if left measurements are less than n_btn_con, + * it's impossible to find button number + */ + break; + } + } + } + + if (btn >= 0) { + if (mbhc->in_swch_irq_handler) { + pr_debug( + "%s: Switch irq triggered, ignore button press\n", + __func__); + goto done; + } + btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration); + v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, + MBHC_BTN_DET_V_BTN_HIGH); + WARN_ON(btn >= btn_det->num_btn); + /* reprogram release threshold to catch voltage ramp up early */ + wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn], vddio); + + mask = wcd9xxx_get_button_mask(btn); + mbhc->buttons_pressed |= mask; + wcd9xxx_lock_sleep(core_res); + if (schedule_delayed_work(&mbhc->mbhc_btn_dwork, + msecs_to_jiffies(400)) == 0) { + WARN(1, "Button pressed twice without release event\n"); + wcd9xxx_unlock_sleep(core_res); + } + } else { + pr_debug("%s: bogus button press, too short press?\n", + __func__); + } + + done: + pr_debug("%s: leave\n", __func__); + mutex_unlock(&mbhc->mbhc_lock); + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + return IRQ_HANDLED; +} + +static irqreturn_t wcd9xxx_release_handler(int irq, void *data) +{ + int ret; + bool waitdebounce = true; + struct wcd9xxx_mbhc *mbhc = data; + + pr_debug("%s: enter\n", __func__); + WCD9XXX_BCL_LOCK(mbhc->resmgr); + mbhc->mbhc_state = MBHC_STATE_RELEASE; + + if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) { + ret = wcd9xxx_cancel_btn_work(mbhc); + if (ret == 0) { + pr_debug("%s: Reporting long button release event\n", + __func__); + wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0, + mbhc->buttons_pressed); + } else { + if (wcd9xxx_is_false_press(mbhc)) { + pr_debug("%s: Fake button press interrupt\n", + __func__); + } else { + if (mbhc->in_swch_irq_handler) { + pr_debug("%s: Switch irq kicked in, ignore\n", + __func__); + } else { + pr_debug("%s: Reporting btn press\n", + __func__); + wcd9xxx_jack_report(mbhc, + &mbhc->button_jack, + mbhc->buttons_pressed, + mbhc->buttons_pressed); + pr_debug("%s: Reporting btn release\n", + __func__); + wcd9xxx_jack_report(mbhc, + &mbhc->button_jack, + 0, mbhc->buttons_pressed); + waitdebounce = false; + } + } + } + + mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK; + } + + wcd9xxx_calibrate_hs_polling(mbhc); + + if (waitdebounce) + msleep(SWCH_REL_DEBOUNCE_TIME_MS); + wcd9xxx_start_hs_polling(mbhc); + + pr_debug("%s: leave\n", __func__); + WCD9XXX_BCL_UNLOCK(mbhc->resmgr); + return IRQ_HANDLED; +} + +static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data) +{ + struct wcd9xxx_mbhc *mbhc = data; + struct snd_soc_codec *codec; + + pr_info("%s: received HPHL OCP irq\n", __func__); + + if (mbhc) { + codec = mbhc->codec; + if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) && + (!mbhc->hphrocp_cnt)) { + pr_info("%s: retry\n", __func__); + mbhc->hphlocp_cnt++; + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, + 0x10, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, + 0x10, 0x10); + } else { + wcd9xxx_disable_irq(mbhc->resmgr->core_res, + mbhc->intr_ids->hph_left_ocp); + mbhc->hph_status |= SND_JACK_OC_HPHL; + wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, + WCD9XXX_JACK_MASK); + } + } else { + pr_err("%s: Bad wcd9xxx private data\n", __func__); + } + + return IRQ_HANDLED; +} + +static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data) +{ + struct wcd9xxx_mbhc *mbhc = data; + struct snd_soc_codec *codec; + + pr_info("%s: received HPHR OCP irq\n", __func__); + codec = mbhc->codec; + if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) && + (!mbhc->hphlocp_cnt)) { + pr_info("%s: retry\n", __func__); + mbhc->hphrocp_cnt++; + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10, + 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10, + 0x10); + } else { + wcd9xxx_disable_irq(mbhc->resmgr->core_res, + mbhc->intr_ids->hph_right_ocp); + mbhc->hph_status |= SND_JACK_OC_HPHR; + wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, WCD9XXX_JACK_MASK); + } + + return IRQ_HANDLED; +} + +static int wcd9xxx_acdb_mclk_index(const int rate) +{ + if (rate == MCLK_RATE_12288KHZ) + return 0; + else if (rate == MCLK_RATE_9600KHZ) + return 1; + else { + BUG_ON(1); + return -EINVAL; + } +} + +static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate) +{ + u32 dce_wait, sta_wait; + u8 ncic, nmeas, navg; + void *calibration; + u8 *n_cic, *n_ready; + struct wcd9xxx_mbhc_btn_detect_cfg *btn_det; + u8 npoll = 4, nbounce_wait = 30; + struct snd_soc_codec *codec = mbhc->codec; + int idx = wcd9xxx_acdb_mclk_index(rate); + int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate); + + pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__, + rate); + calibration = mbhc->mbhc_cfg->calibration; + + /* + * First compute the DCE / STA wait times depending on tunable + * parameters. The value is computed in microseconds + */ + btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration); + n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY); + n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC); + nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas; + navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg; + + /* ncic stays with the same what we had during calibration */ + ncic = n_cic[idxmclk]; + dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000); + sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000); + mbhc->mbhc_data.t_dce = dce_wait; + /* give extra margin to sta for safety */ + mbhc->mbhc_data.t_sta = sta_wait + 250; + mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) * + n_ready[idx]) + 10; + + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic); + + if (rate == MCLK_RATE_12288KHZ) { + npoll = 4; + nbounce_wait = 30; + } else if (rate == MCLK_RATE_9600KHZ) { + npoll = 3; + nbounce_wait = 23; + } + + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait); + pr_debug("%s: leave\n", __func__); +} + +static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc) +{ + u8 cfilt_mode; + u16 reg0, reg1, reg2; + struct snd_soc_codec *codec = mbhc->codec; + + pr_debug("%s: enter\n", __func__); + wcd9xxx_disable_irq(mbhc->resmgr->core_res, + mbhc->intr_ids->dce_est_complete); + wcd9xxx_turn_onoff_rel_detection(codec, false); + + /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */ + WARN_ON(!mbhc->mbhc_data.t_dce); + WARN_ON(!mbhc->mbhc_data.t_sta); + + /* + * LDOH and CFILT are already configured during pdata handling. + * Only need to make sure CFILT and bandgap are in Fast mode. + * Need to restore defaults once calculation is done. + * + * In case when Micbias is powered by external source, request + * turn on the external voltage source for Calibration. + */ + if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(codec, true, false); + + cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl); + if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode) + mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc); + else + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, + 0x40, 0x00); + + if (mbhc->mbhc_cb && mbhc->mbhc_cb->micbias_pulldown_ctrl) + mbhc->mbhc_cb->micbias_pulldown_ctrl(mbhc, false); + + /* + * Micbias, CFILT, LDOH, MBHC MUX mode settings + * to perform ADC calibration + */ + if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt) + mbhc->mbhc_cb->select_cfilt(codec, mbhc); + else + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60, + mbhc->mbhc_cfg->micbias << 5); + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60); + snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78); + if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal) + mbhc->mbhc_cb->codec_specific_cal(codec, mbhc); + else + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, + 0x04, 0x04); + + /* Pull down micbias to ground */ + reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg); + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1); + /* Disconnect override from micbias */ + reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL); + snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0); + /* Connect the MUX to micbias */ + snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02); + if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block) + mbhc->mbhc_cb->enable_mux_bias_block(codec); + else + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, + 0x80, 0x80); + /* + * Hardware that has external cap can delay mic bias ramping down up + * to 50ms. + */ + msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS); + /* DCE measurement for 0 voltage */ + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02); + mbhc->mbhc_data.dce_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, false); + + /* compute dce_z for current source */ + reg2 = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78, + WCD9XXX_MBHC_NSC_CS << 3); + + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02); + mbhc->mbhc_data.dce_nsc_cs_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, + false); + pr_debug("%s: dce_z with nsc cs: 0x%x\n", __func__, + mbhc->mbhc_data.dce_nsc_cs_z); + + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg2); + + /* STA measurement for 0 voltage */ + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02); + mbhc->mbhc_data.sta_z = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false); + + /* Restore registers */ + snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0); + snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, reg1); + + /* DCE measurment for MB voltage */ + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02); + snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02); + if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block) + mbhc->mbhc_cb->enable_mux_bias_block(codec); + else + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, + 0x80, 0x80); + /* + * Hardware that has external cap can delay mic bias ramping down up + * to 50ms. + */ + msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04); + usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec); + + /* STA Measurement for MB Voltage */ + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02); + snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02); + if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block) + mbhc->mbhc_cb->enable_mux_bias_block(codec); + else + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, + 0x80, 0x80); + /* + * Hardware that has external cap can delay mic bias ramping down up + * to 50ms. + */ + msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02); + usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec); + + /* Restore default settings. */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00); + snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode); + snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04); + if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block) + mbhc->mbhc_cb->enable_mux_bias_block(codec); + else + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, + 0x80, 0x80); + usleep_range(100, 110); + + if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(codec, false, false); + + if (mbhc->mbhc_cb && mbhc->mbhc_cb->micbias_pulldown_ctrl) + mbhc->mbhc_cb->micbias_pulldown_ctrl(mbhc, true); + + wcd9xxx_enable_irq(mbhc->resmgr->core_res, + mbhc->intr_ids->dce_est_complete); + wcd9xxx_turn_onoff_rel_detection(codec, true); + + pr_debug("%s: leave\n", __func__); +} + +static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc) +{ + int n; + u8 *gain; + struct wcd9xxx_mbhc_general_cfg *generic; + struct wcd9xxx_mbhc_btn_detect_cfg *btn_det; + struct snd_soc_codec *codec = mbhc->codec; + const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate); + + pr_debug("%s: enter\n", __func__); + generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration); + btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration); + + for (n = 0; n < 8; n++) { + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG, + 0x07, n); + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG, + btn_det->c[n]); + } + + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07, + btn_det->nc); + + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70, + generic->mbhc_nsa << 4); + + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F, + btn_det->n_meas); + + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL, + generic->mbhc_navg); + + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80); + + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78, + btn_det->mbhc_nsc << 3); + + if (mbhc->mbhc_cb && mbhc->mbhc_cb->get_cdc_type && + mbhc->mbhc_cb->get_cdc_type() != + WCD9XXX_CDC_TYPE_HELICON) { + if (mbhc->resmgr->reg_addr->micb_4_mbhc) + snd_soc_update_bits(codec, + mbhc->resmgr->reg_addr->micb_4_mbhc, + 0x03, MBHC_MICBIAS2); + } + + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02); + + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0); + + gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78, + gain[idx] << 3); + snd_soc_update_bits(codec, WCD9XXX_A_MICB_2_MBHC, 0x04, 0x04); + + pr_debug("%s: leave\n", __func__); +} + +static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc) +{ + int ret = 0; + void *core_res = mbhc->resmgr->core_res; + + if (mbhc->mbhc_cfg->gpio) { + ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL, + wcd9xxx_mech_plug_detect_irq, + (IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING), + "headset detect", mbhc); + if (ret) { + pr_err("%s: Failed to request gpio irq %d\n", __func__, + mbhc->mbhc_cfg->gpio_irq); + } else { + ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq); + if (ret) + pr_err("%s: Failed to enable wake up irq %d\n", + __func__, mbhc->mbhc_cfg->gpio_irq); + } + } else if (mbhc->mbhc_cfg->insert_detect) { + /* Enable HPHL_10K_SW */ + snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL, + 1 << 1, 1 << 1); + + ret = wcd9xxx_request_irq(core_res, + mbhc->intr_ids->hs_jack_switch, + wcd9xxx_mech_plug_detect_irq, + "Jack Detect", + mbhc); + if (ret) + pr_err("%s: Failed to request insert detect irq %d\n", + __func__, mbhc->intr_ids->hs_jack_switch); + } + + return ret; +} + +static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc) +{ + int ret = 0; + struct snd_soc_codec *codec = mbhc->codec; + + pr_debug("%s: enter\n", __func__); + + /* Enable MCLK during calibration */ + wcd9xxx_onoff_ext_mclk(mbhc, true); + wcd9xxx_mbhc_setup(mbhc); + wcd9xxx_mbhc_cal(mbhc); + wcd9xxx_mbhc_calc_thres(mbhc); + wcd9xxx_onoff_ext_mclk(mbhc, false); + wcd9xxx_calibrate_hs_polling(mbhc); + + /* Enable Mic Bias pull down and HPH Switch to GND */ + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01); + INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug); + + if (!IS_ERR_VALUE(ret)) { + snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10, + 0x10); + wcd9xxx_enable_irq(mbhc->resmgr->core_res, + mbhc->intr_ids->hph_left_ocp); + wcd9xxx_enable_irq(mbhc->resmgr->core_res, + mbhc->intr_ids->hph_right_ocp); + + /* Initialize mechanical mbhc */ + ret = wcd9xxx_setup_jack_detect_irq(mbhc); + + if (!ret && mbhc->mbhc_cfg->gpio) { + /* Requested with IRQF_DISABLED */ + enable_irq(mbhc->mbhc_cfg->gpio_irq); + + /* Bootup time detection */ + wcd9xxx_swch_irq_handler(mbhc); + } else if (!ret && mbhc->mbhc_cfg->insert_detect) { + pr_debug("%s: Setting up codec own insert detection\n", + __func__); + /* Setup for insertion detection */ + wcd9xxx_insert_detect_setup(mbhc, true); + } + } + + pr_debug("%s: leave\n", __func__); + + return ret; +} + +static void wcd9xxx_mbhc_fw_read(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd9xxx_mbhc *mbhc; + struct snd_soc_codec *codec; + const struct firmware *fw; + struct firmware_cal *fw_data = NULL; + int ret = -1, retry = 0; + bool use_default_cal = false; + + dwork = to_delayed_work(work); + mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork); + codec = mbhc->codec; + + while (retry < FW_READ_ATTEMPTS) { + retry++; + pr_info("%s:Attempt %d to request MBHC firmware\n", + __func__, retry); + if (mbhc->mbhc_cb->get_hwdep_fw_cal) + fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(codec, + WCD9XXX_MBHC_CAL); + if (!fw_data) + ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin", + codec->dev); + /* + * if request_firmware and hwdep cal both fail then + * retry for few times before bailing out + */ + if ((ret != 0) && !fw_data) { + usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + } else { + pr_info("%s: MBHC Firmware read successful\n", + __func__); + break; + } + } + if (!fw_data) + pr_info("%s: using request_firmware\n", __func__); + else + pr_info("%s: using hwdep cal\n", __func__); + if (ret != 0 && !fw_data) { + pr_err("%s: Cannot load MBHC firmware use default cal\n", + __func__); + use_default_cal = true; + } + if (!use_default_cal) { + const void *data; + size_t size; + + if (fw_data) { + data = fw_data->data; + size = fw_data->size; + } else { + data = fw->data; + size = fw->size; + } + if (wcd9xxx_mbhc_fw_validate(data, size) == false) { + pr_err("%s: Invalid MBHC cal data size use default cal\n", + __func__); + if (!fw_data) + release_firmware(fw); + } else { + if (fw_data) { + mbhc->mbhc_cfg->calibration = + (void *)fw_data->data; + mbhc->mbhc_cal = fw_data; + } else { + mbhc->mbhc_cfg->calibration = + (void *)fw->data; + mbhc->mbhc_fw = fw; + } + } + } + + (void) wcd9xxx_init_and_calibrate(mbhc); +} + +#ifdef CONFIG_DEBUG_FS +ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + const int size = 768; + char buffer[size]; + int n = 0; + struct wcd9xxx_mbhc *mbhc = file->private_data; + const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data; + const s16 v_ins_hu = + wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU); + const s16 v_ins_h = + wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H); + const s16 v_b1_hu = + wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU); + const s16 v_b1_h = + wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H); + const s16 v_br_h = + wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H); + + n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n", + p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z)); + n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n", + p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb)); + n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n", + p->dce_nsc_cs_z, + __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z, + p->dce_nsc_cs_z, + VDDIO_MICBIAS_MV)); + n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n", + p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z)); + n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n", + p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb)); + n += scnprintf(buffer + n, size - n, "t_dce = %d\n", p->t_dce); + n += scnprintf(buffer + n, size - n, "t_sta = %d\n", p->t_sta); + n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv); + n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n", + v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu)); + n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n", + v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h)); + n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n", + v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu)); + n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n", + v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h)); + n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n", + v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h)); + n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl, + wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl)); + n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n", + p->v_no_mic, + wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic)); + n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n", + p->v_inval_ins_low); + n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n", + p->v_inval_ins_high); + n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n", + !wcd9xxx_swch_level_remove(mbhc)); + buffer[n] = 0; + + return simple_read_from_buffer(buf, count, pos, buffer, n); +} + +static int codec_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t codec_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, + loff_t *ppos) +{ + char lbuf[32]; + char *buf; + int rc; + struct wcd9xxx_mbhc *mbhc = filp->private_data; + + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + buf = (char *)lbuf; + mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ? + false : true; + return rc; +} + +static const struct file_operations mbhc_trrs_debug_ops = { + .open = codec_debug_open, + .write = codec_debug_write, +}; + +static const struct file_operations mbhc_debug_ops = { + .open = codec_debug_open, + .read = codec_mbhc_debug_read, +}; + +static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc) +{ + mbhc->debugfs_poke = + debugfs_create_file("TRRS", S_IFREG | 0444, NULL, mbhc, + &mbhc_trrs_debug_ops); + mbhc->debugfs_mbhc = + debugfs_create_file("wcd9xxx_mbhc", S_IFREG | 0444, + NULL, mbhc, &mbhc_debug_ops); +} + +static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc) +{ + debugfs_remove(mbhc->debugfs_poke); + debugfs_remove(mbhc->debugfs_mbhc); +} +#else +static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc) +{ +} + +static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc) +{ +} +#endif + +int wcd9xxx_mbhc_set_keycode(struct wcd9xxx_mbhc *mbhc) +{ + enum snd_jack_types type = SND_JACK_BTN_0; + int i, ret, result = 0; + int *btn_key_code; + + btn_key_code = mbhc->mbhc_cfg->key_code; + + for (i = 0 ; i < 8 ; i++) { + if (btn_key_code[i] != 0) { + switch (i) { + case 0: + type = SND_JACK_BTN_0; + break; + case 1: + type = SND_JACK_BTN_1; + break; + case 2: + type = SND_JACK_BTN_2; + break; + case 3: + type = SND_JACK_BTN_3; + break; + case 4: + type = SND_JACK_BTN_4; + break; + case 5: + type = SND_JACK_BTN_5; + break; + default: + WARN_ONCE(1, "Wrong button number:%d\n", i); + result = -1; + break; + } + ret = snd_jack_set_key(mbhc->button_jack.jack, + type, + btn_key_code[i]); + if (ret) { + pr_err("%s: Failed to set code for %d\n", + __func__, btn_key_code[i]); + result = -1; + } + input_set_capability( + mbhc->button_jack.jack->input_dev, + EV_KEY, btn_key_code[i]); + pr_debug("%s: set btn%d key code:%d\n", __func__, + i, btn_key_code[i]); + } + } + return result; +} + +int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc, + struct wcd9xxx_mbhc_config *mbhc_cfg) +{ + int rc = 0; + struct snd_soc_codec *codec = mbhc->codec; + + pr_debug("%s: enter\n", __func__); + + if (!codec) { + pr_err("%s: no codec\n", __func__); + return -EINVAL; + } + + if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ && + mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) { + pr_err("Error: unsupported clock rate %d\n", + mbhc_cfg->mclk_rate); + return -EINVAL; + } + + /* Save mbhc config */ + mbhc->mbhc_cfg = mbhc_cfg; + + /* Set btn key code */ + if (wcd9xxx_mbhc_set_keycode(mbhc)) + pr_err("Set btn key code error!!!\n"); + + /* Get HW specific mbhc registers' address */ + wcd9xxx_get_mbhc_micbias_regs(mbhc, MBHC_PRIMARY_MIC_MB); + + /* Get HW specific mbhc registers' address for anc */ + wcd9xxx_get_mbhc_micbias_regs(mbhc, MBHC_ANC_MIC_MB); + + /* Put CFILT in fast mode by default */ + if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode) + mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc); + else + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, + 0x40, WCD9XXX_CFILT_FAST_MODE); + + /* + * setup internal micbias if codec uses internal micbias for + * headset detection + */ + if (mbhc->mbhc_cfg->use_int_rbias) { + if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) { + mbhc->mbhc_cb->setup_int_rbias(codec, true); + } else { + pr_info("%s: internal bias requested but codec did not provide callback\n", + __func__); + } + } + + /* + * If codec has specific clock gating for MBHC, + * remove the clock gate + */ + if (mbhc->mbhc_cb && + mbhc->mbhc_cb->enable_clock_gate) + mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true); + + if (!mbhc->mbhc_cfg->read_fw_bin || + (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) || + (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) { + rc = wcd9xxx_init_and_calibrate(mbhc); + } else { + if (!mbhc->mbhc_fw || !mbhc->mbhc_cal) + schedule_delayed_work(&mbhc->mbhc_firmware_dwork, + usecs_to_jiffies(FW_READ_TIMEOUT)); + else + pr_debug("%s: Skipping to read mbhc fw, 0x%pK %pK\n", + __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); + } + + pr_debug("%s: leave %d\n", __func__, rc); + return rc; +} +EXPORT_SYMBOL(wcd9xxx_mbhc_start); + +void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc) +{ + if (mbhc->mbhc_fw || mbhc->mbhc_cal) { + cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork); + if (!mbhc->mbhc_cal) + release_firmware(mbhc->mbhc_fw); + mbhc->mbhc_fw = NULL; + mbhc->mbhc_cal = NULL; + } +} +EXPORT_SYMBOL(wcd9xxx_mbhc_stop); + +static enum wcd9xxx_micbias_num +wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event) +{ + enum wcd9xxx_micbias_num ret; + + switch (event) { + case WCD9XXX_EVENT_PRE_MICBIAS_1_ON: + case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF: + case WCD9XXX_EVENT_POST_MICBIAS_1_ON: + case WCD9XXX_EVENT_POST_MICBIAS_1_OFF: + ret = MBHC_MICBIAS1; + break; + case WCD9XXX_EVENT_PRE_MICBIAS_2_ON: + case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF: + case WCD9XXX_EVENT_POST_MICBIAS_2_ON: + case WCD9XXX_EVENT_POST_MICBIAS_2_OFF: + ret = MBHC_MICBIAS2; + break; + case WCD9XXX_EVENT_PRE_MICBIAS_3_ON: + case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF: + case WCD9XXX_EVENT_POST_MICBIAS_3_ON: + case WCD9XXX_EVENT_POST_MICBIAS_3_OFF: + ret = MBHC_MICBIAS3; + break; + case WCD9XXX_EVENT_PRE_MICBIAS_4_ON: + case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF: + case WCD9XXX_EVENT_POST_MICBIAS_4_ON: + case WCD9XXX_EVENT_POST_MICBIAS_4_OFF: + ret = MBHC_MICBIAS4; + break; + default: + WARN_ONCE(1, "Cannot convert event %d to micbias\n", event); + ret = MBHC_MICBIAS_INVALID; + break; + } + return ret; +} + +static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event) +{ + int ret; + + switch (event) { + case WCD9XXX_EVENT_PRE_CFILT_1_OFF: + case WCD9XXX_EVENT_POST_CFILT_1_OFF: + case WCD9XXX_EVENT_PRE_CFILT_1_ON: + case WCD9XXX_EVENT_POST_CFILT_1_ON: + ret = WCD9XXX_CFILT1_SEL; + break; + case WCD9XXX_EVENT_PRE_CFILT_2_OFF: + case WCD9XXX_EVENT_POST_CFILT_2_OFF: + case WCD9XXX_EVENT_PRE_CFILT_2_ON: + case WCD9XXX_EVENT_POST_CFILT_2_ON: + ret = WCD9XXX_CFILT2_SEL; + break; + case WCD9XXX_EVENT_PRE_CFILT_3_OFF: + case WCD9XXX_EVENT_POST_CFILT_3_OFF: + case WCD9XXX_EVENT_PRE_CFILT_3_ON: + case WCD9XXX_EVENT_POST_CFILT_3_ON: + ret = WCD9XXX_CFILT3_SEL; + break; + default: + ret = -1; + } + return ret; +} + +static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc) +{ + int cfilt; + const struct wcd9xxx_micbias_setting *mb_pdata = + mbhc->resmgr->micbias_pdata; + + switch (mbhc->mbhc_cfg->micbias) { + case MBHC_MICBIAS1: + cfilt = mb_pdata->bias1_cfilt_sel; + break; + case MBHC_MICBIAS2: + cfilt = mb_pdata->bias2_cfilt_sel; + break; + case MBHC_MICBIAS3: + cfilt = mb_pdata->bias3_cfilt_sel; + break; + case MBHC_MICBIAS4: + cfilt = mb_pdata->bias4_cfilt_sel; + break; + default: + cfilt = MBHC_MICBIAS_INVALID; + break; + } + return cfilt; +} + +static void wcd9xxx_enable_mbhc_txfe(struct wcd9xxx_mbhc *mbhc, bool on) +{ + if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mbhc_txfe) + mbhc->mbhc_cb->enable_mbhc_txfe(mbhc->codec, on); + else + snd_soc_update_bits(mbhc->codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, + 0x40, on ? 0x40 : 0x00); +} + +static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val, + void *data) +{ + int ret = 0; + struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc; + struct snd_soc_codec *codec; + enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val; + + pr_debug("%s: enter event %s(%d)\n", __func__, + wcd9xxx_get_event_string(event), event); + + if (!mbhc || !mbhc->mbhc_cfg) { + pr_debug("mbhc not initialized\n"); + return 0; + } + codec = mbhc->codec; + mutex_lock(&mbhc->mbhc_lock); + switch (event) { + /* MICBIAS usage change */ + case WCD9XXX_EVENT_PRE_MICBIAS_1_ON: + case WCD9XXX_EVENT_PRE_MICBIAS_2_ON: + case WCD9XXX_EVENT_PRE_MICBIAS_3_ON: + case WCD9XXX_EVENT_PRE_MICBIAS_4_ON: + if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias == + wcd9xxx_event_to_micbias(event)) { + wcd9xxx_switch_micbias(mbhc, 0); + /* + * Enable MBHC TxFE whenever micbias is + * turned ON and polling is active + */ + if (mbhc->polling_active) + wcd9xxx_enable_mbhc_txfe(mbhc, true); + } + break; + case WCD9XXX_EVENT_POST_MICBIAS_1_ON: + case WCD9XXX_EVENT_POST_MICBIAS_2_ON: + case WCD9XXX_EVENT_POST_MICBIAS_3_ON: + case WCD9XXX_EVENT_POST_MICBIAS_4_ON: + if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias == + wcd9xxx_event_to_micbias(event) && + wcd9xxx_mbhc_polling(mbhc)) { + /* if polling is on, restart it */ + wcd9xxx_pause_hs_polling(mbhc); + wcd9xxx_start_hs_polling(mbhc); + } + break; + case WCD9XXX_EVENT_POST_MICBIAS_1_OFF: + case WCD9XXX_EVENT_POST_MICBIAS_2_OFF: + case WCD9XXX_EVENT_POST_MICBIAS_3_OFF: + case WCD9XXX_EVENT_POST_MICBIAS_4_OFF: + if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias == + wcd9xxx_event_to_micbias(event)) { + if (mbhc->event_state & + (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)) + wcd9xxx_switch_micbias(mbhc, 1); + /* + * Disable MBHC TxFE, in case it was enabled earlier + * when micbias was enabled and polling is not active. + */ + if (!mbhc->polling_active) + wcd9xxx_enable_mbhc_txfe(mbhc, false); + } + if (mbhc->micbias_enable && mbhc->polling_active && + !(snd_soc_read(mbhc->codec, mbhc->mbhc_bias_regs.ctl_reg) + & 0x80)) { + pr_debug("%s:Micbias turned off by recording, set up again", + __func__); + snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, + 0x80, 0x80); + } + break; + /* PA usage change */ + case WCD9XXX_EVENT_PRE_HPHL_PA_ON: + set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state); + if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80)) + /* if micbias is not enabled, switch to vddio */ + wcd9xxx_switch_micbias(mbhc, 1); + break; + case WCD9XXX_EVENT_PRE_HPHR_PA_ON: + set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state); + break; + case WCD9XXX_EVENT_POST_HPHL_PA_OFF: + clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state); + /* if HPH PAs are off, report OCP and switch back to CFILT */ + clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state); + if (mbhc->hph_status & SND_JACK_OC_HPHL) + hphlocp_off_report(mbhc, SND_JACK_OC_HPHL); + if (!(mbhc->event_state & + (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR | + 1 << MBHC_EVENT_PRE_TX_3_ON))) + wcd9xxx_switch_micbias(mbhc, 0); + break; + case WCD9XXX_EVENT_POST_HPHR_PA_OFF: + clear_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state); + /* if HPH PAs are off, report OCP and switch back to CFILT */ + clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state); + if (mbhc->hph_status & SND_JACK_OC_HPHR) + hphrocp_off_report(mbhc, SND_JACK_OC_HPHL); + if (!(mbhc->event_state & + (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR | + 1 << MBHC_EVENT_PRE_TX_3_ON))) + wcd9xxx_switch_micbias(mbhc, 0); + break; + /* Clock usage change */ + case WCD9XXX_EVENT_PRE_MCLK_ON: + break; + case WCD9XXX_EVENT_POST_MCLK_ON: + /* Change to lower TxAAF frequency */ + snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4, + 1 << 4); + /* Re-calibrate clock rate dependent values */ + wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate); + /* If clock source changes, stop and restart polling */ + if (wcd9xxx_mbhc_polling(mbhc)) { + wcd9xxx_calibrate_hs_polling(mbhc); + wcd9xxx_start_hs_polling(mbhc); + } + break; + case WCD9XXX_EVENT_PRE_MCLK_OFF: + /* If clock source changes, stop and restart polling */ + if (wcd9xxx_mbhc_polling(mbhc)) + wcd9xxx_pause_hs_polling(mbhc); + break; + case WCD9XXX_EVENT_POST_MCLK_OFF: + break; + case WCD9XXX_EVENT_PRE_RCO_ON: + break; + case WCD9XXX_EVENT_POST_RCO_ON: + /* Change to higher TxAAF frequency */ + snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4, + 0 << 4); + /* Re-calibrate clock rate dependent values */ + wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate); + /* If clock source changes, stop and restart polling */ + if (wcd9xxx_mbhc_polling(mbhc)) { + wcd9xxx_calibrate_hs_polling(mbhc); + wcd9xxx_start_hs_polling(mbhc); + } + break; + case WCD9XXX_EVENT_PRE_RCO_OFF: + /* If clock source changes, stop and restart polling */ + if (wcd9xxx_mbhc_polling(mbhc)) + wcd9xxx_pause_hs_polling(mbhc); + break; + case WCD9XXX_EVENT_POST_RCO_OFF: + break; + /* CFILT usage change */ + case WCD9XXX_EVENT_PRE_CFILT_1_ON: + case WCD9XXX_EVENT_PRE_CFILT_2_ON: + case WCD9XXX_EVENT_PRE_CFILT_3_ON: + if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) == + wcd9xxx_event_to_cfilt(event)) + /* + * Switch CFILT to slow mode if MBHC CFILT is being + * used. + */ + wcd9xxx_codec_switch_cfilt_mode(mbhc, false); + break; + case WCD9XXX_EVENT_POST_CFILT_1_OFF: + case WCD9XXX_EVENT_POST_CFILT_2_OFF: + case WCD9XXX_EVENT_POST_CFILT_3_OFF: + if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) == + wcd9xxx_event_to_cfilt(event)) + /* + * Switch CFILT to fast mode if MBHC CFILT is not + * used anymore. + */ + wcd9xxx_codec_switch_cfilt_mode(mbhc, true); + break; + /* System resume */ + case WCD9XXX_EVENT_POST_RESUME: + mbhc->mbhc_last_resume = jiffies; + break; + /* BG mode chage */ + case WCD9XXX_EVENT_PRE_BG_OFF: + case WCD9XXX_EVENT_POST_BG_OFF: + case WCD9XXX_EVENT_PRE_BG_AUDIO_ON: + case WCD9XXX_EVENT_POST_BG_AUDIO_ON: + case WCD9XXX_EVENT_PRE_BG_MBHC_ON: + case WCD9XXX_EVENT_POST_BG_MBHC_ON: + /* Not used for now */ + break; + case WCD9XXX_EVENT_PRE_TX_3_ON: + /* + * if polling is ON, mbhc micbias not enabled + * switch micbias source to VDDIO + */ + set_bit(MBHC_EVENT_PRE_TX_3_ON, &mbhc->event_state); + if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) + & 0x80) && + mbhc->polling_active && !mbhc->mbhc_micbias_switched) + wcd9xxx_switch_micbias(mbhc, 1); + break; + case WCD9XXX_EVENT_POST_TX_3_OFF: + /* + * Switch back to micbias if HPH PA or TX3 path + * is disabled + */ + clear_bit(MBHC_EVENT_PRE_TX_3_ON, &mbhc->event_state); + if (mbhc->polling_active && mbhc->mbhc_micbias_switched && + !(mbhc->event_state & (1 << MBHC_EVENT_PA_HPHL | + 1 << MBHC_EVENT_PA_HPHR))) + wcd9xxx_switch_micbias(mbhc, 0); + break; + default: + WARN(1, "Unknown event %d\n", event); + ret = -EINVAL; + } + mutex_unlock(&mbhc->mbhc_lock); + + pr_debug("%s: leave\n", __func__); + + return ret; +} + +static s16 wcd9xxx_read_impedance_regs(struct wcd9xxx_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + short bias_value; + int i; + s32 z_t = 0; + s32 z_loop = z_det_box_car_avg; + + /* Box Car avrg of less than a particular loop count will not be + * accomodated. Similarly if the count is more than a particular number + * it will not be counted. Set z_loop counter to a limit, if its more + * or less than the value in WCD9XXX_BOX_CAR_AVRG_MAX or + * WCD9XXX_BOX_CAR_AVRG_MIN + */ + if (z_loop < WCD9XXX_BOX_CAR_AVRG_MIN) { + dev_dbg(codec->dev, + "%s: Box Car avrg counter < %d. Limiting it to %d\n", + __func__, WCD9XXX_BOX_CAR_AVRG_MIN, + WCD9XXX_BOX_CAR_AVRG_MIN); + z_loop = WCD9XXX_BOX_CAR_AVRG_MIN; + } else if (z_loop > WCD9XXX_BOX_CAR_AVRG_MAX) { + dev_dbg(codec->dev, + "%s: Box Car avrg counter > %d. Limiting it to %d\n", + __func__, WCD9XXX_BOX_CAR_AVRG_MAX, + WCD9XXX_BOX_CAR_AVRG_MAX); + z_loop = WCD9XXX_BOX_CAR_AVRG_MAX; + } + + /* Take box car average if needed */ + for (i = 0; i < z_loop; i++) { + snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2); + /* Wait for atleast 1800uS to let register write to settle */ + usleep_range(1800, 1800 + WCD9XXX_USLEEP_RANGE_MARGIN_US); + z_t += wcd9xxx_read_sta_result(codec); + } + /* Take average of the Z values read */ + bias_value = (s16) (z_t / z_loop); + return bias_value; +} + +static int wcd9xxx_remeasure_z_values(struct wcd9xxx_mbhc *mbhc, + s16 l[3], s16 r[3], + uint32_t *zl, uint32_t *zr, + u32 *zl_stereo, u32 *zl_mono) +{ + s16 l_t[3] = {0}, r_t[3] = {0}; + s16 l2_stereo, l2_mono; + bool left, right; + struct snd_soc_codec *codec = mbhc->codec; + + if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet || + !mbhc->mbhc_cb->compute_impedance) { + dev_err(codec->dev, "%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + left = !!(l); + right = !!(r); + + dev_dbg(codec->dev, "%s: Remeasuring impedance values\n", __func__); + dev_dbg(codec->dev, "%s: l: %pK, r: %pK, left=%d, right=%d\n", __func__, + l, r, left, right); + + /* Remeasure V2 values */ + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0); + if (right) + r_t[2] = wcd9xxx_read_impedance_regs(mbhc); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0); + if (left) + l_t[2] = wcd9xxx_read_impedance_regs(mbhc); + + /* Ramp down HPHR */ + mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_HPHR_RAMP_DISABLE); + + if (right) { + /* Take R0'/R1' */ + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, + 0xFF, 0xF8); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, + 0xFF, 0xA0); + r_t[1] = wcd9xxx_read_impedance_regs(mbhc); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, + 0xFF, 0xF0); + r_t[0] = wcd9xxx_read_impedance_regs(mbhc); + } + + /* Put back gain to 1x */ + if (!left && right) + mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_GAIN_0); + + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0); + /* Take L2'' measurement */ + l2_stereo = wcd9xxx_read_impedance_regs(mbhc); + + /* Turn off HPHR PA and take L2''' */ + mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_HPHR_PA_DISABLE); + l2_mono = wcd9xxx_read_impedance_regs(mbhc); + + /* Ramp HPHL from -15mV to 0V */ + mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_HPHL_RAMP_DISABLE); + + /* Take L0' and L1' with iCal */ + l_t[0] = wcd9xxx_read_impedance_regs(mbhc); + snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8); + l_t[1] = wcd9xxx_read_impedance_regs(mbhc); + + if (left) { + l[0] = l_t[0]; + l[1] = l_t[1]; + l[2] = l_t[2]; + } + if (right) { + r[0] = r_t[0]; + r[1] = r_t[1]; + r[2] = r_t[2]; + } + + /* compute the new impedance values */ + mbhc->mbhc_cb->compute_impedance(mbhc, l, r, zl, zr); + + if (!left && right) + mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_GAIN_UPDATE_1X); + /* compute the new ZL'' value */ + l_t[2] = l2_stereo; + mbhc->mbhc_cb->compute_impedance(mbhc, l_t, NULL, zl_stereo, NULL); + /* compute the new ZL''' value */ + l_t[2] = l2_mono; + mbhc->mbhc_cb->compute_impedance(mbhc, l_t, NULL, zl_mono, NULL); + + pr_debug("%s: L0': 0x%x, L1': 0x%x L2_stereo: 0x%x, L2_mono: 0x%x\n", + __func__, l_t[0] & 0xffff, l_t[1] & 0xffff, + l2_stereo & 0xffff, l2_mono & 0xffff); + pr_debug("%s: ZL_stereo = %u, ZL_mono = %u\n", + __func__, *zl_stereo, *zl_mono); + + return 0; +} + +static enum mbhc_zdet_zones wcd9xxx_assign_zdet_zone(uint32_t zl, uint32_t zr, + int32_t *gain) +{ + enum mbhc_zdet_zones zdet_zone; + + if (WCD9XXX_IS_IN_ZDET_ZONE_1(zl) && + WCD9XXX_IS_IN_ZDET_ZONE_1(zr)) { + zdet_zone = ZL_ZONE1__ZR_ZONE1; + *gain = 0; + } else if (WCD9XXX_IS_IN_ZDET_ZONE_2(zl) && + WCD9XXX_IS_IN_ZDET_ZONE_2(zr)) { + zdet_zone = ZL_ZONE2__ZR_ZONE2; + *gain = MBHC_ZDET_GAIN_1; + } else if (WCD9XXX_IS_IN_ZDET_ZONE_3(zl) && + WCD9XXX_IS_IN_ZDET_ZONE_3(zr)) { + zdet_zone = ZL_ZONE3__ZR_ZONE3; + *gain = MBHC_ZDET_GAIN_2; + } else if (WCD9XXX_IS_IN_ZDET_ZONE_2(zl) && + WCD9XXX_IS_IN_ZDET_ZONE_1(zr)) { + zdet_zone = ZL_ZONE2__ZR_ZONE1; + *gain = MBHC_ZDET_GAIN_1; + } else if (WCD9XXX_IS_IN_ZDET_ZONE_3(zl) && + WCD9XXX_IS_IN_ZDET_ZONE_1(zr)) { + zdet_zone = ZL_ZONE3__ZR_ZONE1; + *gain = MBHC_ZDET_GAIN_2; + } else if (WCD9XXX_IS_IN_ZDET_ZONE_1(zl) && + WCD9XXX_IS_IN_ZDET_ZONE_2(zr)) { + zdet_zone = ZL_ZONE1__ZR_ZONE2; + *gain = MBHC_ZDET_GAIN_1; + } else if (WCD9XXX_IS_IN_ZDET_ZONE_1(zl) && + WCD9XXX_IS_IN_ZDET_ZONE_3(zr)) { + zdet_zone = ZL_ZONE1__ZR_ZONE3; + *gain = MBHC_ZDET_GAIN_2; + } else { + zdet_zone = ZL_ZR_NOT_IN_ZONE1; + *gain = MBHC_ZDET_GAIN_1; + } + + return zdet_zone; +} + +static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + int i; + int ret = 0; + u8 micb_mbhc_val; + s16 l[3], r[3]; + s16 *z[] = { + &l[0], &r[0], &r[1], &l[1], &l[2], &r[2], + }; + u32 zl_stereo, zl_mono; + u32 zl_diff_1, zl_diff_2; + bool override_en; + struct snd_soc_codec *codec = mbhc->codec; + const int mux_wait_us = 25; + const struct wcd9xxx_reg_mask_val reg_set_mux[] = { + /* Phase 1 */ + /* Set MBHC_MUX for HPHL without ical */ + {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0}, + /* Set MBHC_MUX for HPHR without ical */ + {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0}, + /* Set MBHC_MUX for HPHR with ical */ + {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8}, + /* Set MBHC_MUX for HPHL with ical */ + {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0}, + + /* Phase 2 */ + {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0}, + /* Set MBHC_MUX for HPHR without ical and wait for 25us */ + {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0}, + }; + + pr_debug("%s: enter\n", __func__); + WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr); + + if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet || + !mbhc->mbhc_cb->compute_impedance || !zl || !zr) { + return -EINVAL; + } + + /* + * Impedance detection is an intrusive function as it mutes RX paths, + * enable PAs and etc. Therefore codec drvier including ALSA + * shouldn't read and write hardware registers during detection. + */ + wcd9xxx_onoff_ext_mclk(mbhc, true); + + /* + * For impedance detection, make sure to disable micbias from + * override signal so that override does not cause micbias + * to be enabled. This setting will be undone after completing + * impedance measurement. + */ + micb_mbhc_val = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL); + snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, + 0x10, 0x00); + + override_en = (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04) ? + true : false; + if (!override_en) + wcd9xxx_turn_onoff_override(mbhc, true); + pr_debug("%s: Setting impedance detection\n", __func__); + + /* Codec specific setup for L0, R0, L1 and R1 measurements */ + mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_PRE_MEASURE); + + pr_debug("%s: Performing impedance detection\n", __func__); + for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) { + snd_soc_update_bits(codec, reg_set_mux[i].reg, + reg_set_mux[i].mask, + reg_set_mux[i].val); + if (mbhc->mbhc_cb->get_cdc_type && + mbhc->mbhc_cb->get_cdc_type() == + WCD9XXX_CDC_TYPE_TOMTOM) { + *(z[i]) = wcd9xxx_read_impedance_regs(mbhc); + } else { + if (mbhc->mbhc_cb->enable_mux_bias_block) + mbhc->mbhc_cb->enable_mux_bias_block(codec); + else + snd_soc_update_bits(codec, + WCD9XXX_A_MBHC_SCALING_MUX_1, + 0x80, 0x80); + /* 25us is required after mux change to settle down */ + usleep_range(mux_wait_us, + mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US); + *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, + true, false); + } + } + + /* Codec specific setup for L2 and R2 measurements */ + mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_POST_MEASURE); + + for (; i < ARRAY_SIZE(reg_set_mux); i++) { + snd_soc_update_bits(codec, reg_set_mux[i].reg, + reg_set_mux[i].mask, + reg_set_mux[i].val); + if (mbhc->mbhc_cb->get_cdc_type && + mbhc->mbhc_cb->get_cdc_type() == + WCD9XXX_CDC_TYPE_TOMTOM) { + *(z[i]) = wcd9xxx_read_impedance_regs(mbhc); + } else { + if (mbhc->mbhc_cb->enable_mux_bias_block) + mbhc->mbhc_cb->enable_mux_bias_block(codec); + else + snd_soc_update_bits(codec, + WCD9XXX_A_MBHC_SCALING_MUX_1, + 0x80, 0x80); + /* 25us is required after mux change to settle down */ + usleep_range(mux_wait_us, + mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US); + *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, + true, false); + } + } + + mbhc->mbhc_cb->compute_impedance(mbhc, l, r, zl, zr); + + /* + * For some codecs, an additional step of zdet is needed + * to overcome effects of noise and for better accuracy of + * z values + */ + if (mbhc->mbhc_cb->get_cdc_type && + mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_TOMTOM) { + uint32_t zl_t = 0, zr_t = 0; + s16 *l_p, *r_p; + enum mbhc_zdet_zones zdet_zone; + int32_t gain; + + zdet_zone = wcd9xxx_assign_zdet_zone(*zl, *zr, &gain); + switch (zdet_zone) { + case ZL_ZONE1__ZR_ZONE1: + l_p = NULL; + r_p = NULL; + break; + case ZL_ZONE2__ZR_ZONE2: + case ZL_ZONE3__ZR_ZONE3: + case ZL_ZR_NOT_IN_ZONE1: + l_p = l; + r_p = r; + break; + case ZL_ZONE2__ZR_ZONE1: + case ZL_ZONE3__ZR_ZONE1: + /* If ZR falls in Zone 1, further computations with + * gain update are not required + */ + l_p = l; + r_p = NULL; + break; + case ZL_ZONE1__ZR_ZONE2: + case ZL_ZONE1__ZR_ZONE3: + /* If ZL falls in Zone 1, further computations with + * gain update are not required + */ + l_p = NULL; + r_p = r; + break; + } + pr_debug("%s:zdet_zone = %d, gain = %d\n", __func__, + zdet_zone, gain); + if (gain) + mbhc->mbhc_cb->setup_zdet(mbhc, gain); + + wcd9xxx_remeasure_z_values(mbhc, l_p, r_p, &zl_t, &zr_t, + &zl_stereo, &zl_mono); + + *zl = (zl_t) ? zl_t : *zl; + *zr = (zr_t) ? zr_t : *zr; + + /* Check for Mono/Stereo Type + * Conditions to classify Mono/Stereo + * i. Difference of zl_stereo and zl_mono > (1/2) of zl_mono + * ii. Absolute difference of zl and zr above a threshold + */ + zl_diff_1 = (zl_mono > zl_stereo) ? (zl_mono - zl_stereo) : + (zl_stereo - zl_mono); + zl_diff_2 = (*zl > *zr) ? (*zl - *zr) : (*zr - *zl); + + mbhc->hph_type = MBHC_HPH_NONE; + if (mbhc->current_plug != PLUG_TYPE_HIGH_HPH) { + if ((zl_diff_1 > (zl_mono >> 1)) || + (zl_diff_2 > WCD9XXX_MONO_HS_DIFF_THR) || + ((*zl < WCD9XXX_MONO_HS_MIN_THR) && + (*zr > WCD9XXX_MONO_HS_MIN_THR)) || + ((*zr < WCD9XXX_MONO_HS_MIN_THR) && + (*zl > WCD9XXX_MONO_HS_MIN_THR))) { + pr_debug("%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = MBHC_HPH_MONO; + *zl = zl_mono; + } else { + pr_debug("%s: STEREO plug type detected\n", + __func__); + mbhc->hph_type = MBHC_HPH_STEREO; + } + } + } + + mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_PA_DISABLE); + + /* Calculate z values based on the Q-fuse registers, if used */ + if (mbhc->mbhc_cb->zdet_error_approx) + mbhc->mbhc_cb->zdet_error_approx(mbhc, zl, zr); + + wcd9xxx_onoff_ext_mclk(mbhc, false); + + if (!override_en) + wcd9xxx_turn_onoff_override(mbhc, false); + + /* Undo the micbias disable for override */ + snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, micb_mbhc_val); + + pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n", + __func__, + l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]); + pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n", + __func__, + r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]); + pr_debug("%s: RL %u milliohm, RR %u milliohm\n", __func__, *zl, *zr); + pr_debug("%s: Impedance detection completed\n", __func__); + + return ret; +} + +int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + *zl = mbhc->zl; + *zr = mbhc->zr; + + if (*zl && *zr) + return 0; + else + return -EINVAL; +} + +/* + * wcd9xxx_mbhc_init : initialize MBHC internal structures. + * + * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used + */ +int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr, + struct snd_soc_codec *codec, + int (*micbias_enable_cb)(struct snd_soc_codec*, bool, + enum wcd9xxx_micbias_num), + const struct wcd9xxx_mbhc_cb *mbhc_cb, + const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids, + int rco_clk_rate, + bool impedance_det_en) +{ + int ret; + void *core_res; + + pr_debug("%s: enter\n", __func__); + memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs)); + memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data)); + + mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT; + mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT; + mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT; + mbhc->mbhc_micbias_switched = false; + mbhc->polling_active = false; + mbhc->mbhc_state = MBHC_STATE_NONE; + mbhc->in_swch_irq_handler = false; + mbhc->current_plug = PLUG_TYPE_NONE; + mbhc->lpi_enabled = false; + mbhc->no_mic_headset_override = false; + mbhc->mbhc_last_resume = 0; + mbhc->codec = codec; + mbhc->resmgr = resmgr; + mbhc->resmgr->mbhc = mbhc; + mbhc->micbias_enable_cb = micbias_enable_cb; + mbhc->rco_clk_rate = rco_clk_rate; + mbhc->mbhc_cb = mbhc_cb; + mbhc->intr_ids = mbhc_cdc_intr_ids; + mbhc->impedance_detect = impedance_det_en; + mbhc->hph_type = MBHC_HPH_NONE; + + if (mbhc->intr_ids == NULL) { + pr_err("%s: Interrupt mapping not provided\n", __func__); + return -EINVAL; + } + + if (mbhc->headset_jack.jack == NULL) { + ret = snd_soc_card_jack_new(codec->component.card, + "Headset Jack", WCD9XXX_JACK_MASK, + &mbhc->headset_jack, NULL, 0); + if (ret) { + pr_err("%s: Failed to create new jack\n", __func__); + return ret; + } + + ret = snd_soc_card_jack_new(codec->component.card, + "Button Jack", + WCD9XXX_JACK_BUTTON_MASK, + &mbhc->button_jack, NULL, 0); + if (ret) { + pr_err("Failed to create new jack\n"); + return ret; + } + + ret = snd_jack_set_key(mbhc->button_jack.jack, + SND_JACK_BTN_0, + KEY_MEDIA); + if (ret) { + pr_err("%s: Failed to set code for btn-0\n", + __func__); + return ret; + } + + set_bit(INPUT_PROP_NO_DUMMY_RELEASE, + mbhc->button_jack.jack->input_dev->propbit); + + INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork, + wcd9xxx_mbhc_fw_read); + INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn); + INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork, + wcd9xxx_mbhc_insert_work); + } + + mutex_init(&mbhc->mbhc_lock); + + /* Register event notifier */ + mbhc->nblock.notifier_call = wcd9xxx_event_notify; + ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock); + if (ret) { + pr_err("%s: Failed to register notifier %d\n", __func__, ret); + mutex_destroy(&mbhc->mbhc_lock); + return ret; + } + + wcd9xxx_init_debugfs(mbhc); + + /* Disable Impedance detection by default for certain codec types */ + if (mbhc->mbhc_cb && mbhc->mbhc_cb->get_cdc_type && + (mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_HELICON)) + impedance_detect_en = 0; + else + impedance_detect_en = impedance_det_en ? 1 : 0; + + core_res = mbhc->resmgr->core_res; + ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->insertion, + wcd9xxx_hs_insert_irq, + "Headset insert detect", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d, ret = %d\n", __func__, + mbhc->intr_ids->insertion, ret); + goto err_insert_irq; + } + wcd9xxx_disable_irq(core_res, mbhc->intr_ids->insertion); + + ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->poll_plug_rem, + wcd9xxx_hs_remove_irq, + "Headset remove detect", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->poll_plug_rem); + goto err_remove_irq; + } + + ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->dce_est_complete, + wcd9xxx_dce_handler, "DC Estimation detect", + mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->dce_est_complete); + goto err_potential_irq; + } + + ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->button_release, + wcd9xxx_release_handler, + "Button Release detect", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->button_release); + goto err_release_irq; + } + + ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_left_ocp, + wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect", + mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->hph_left_ocp); + goto err_hphl_ocp_irq; + } + wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_left_ocp); + + ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_right_ocp, + wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect", + mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->hph_right_ocp); + goto err_hphr_ocp_irq; + } + wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_right_ocp); + + wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC | + 1 << WCD9XXX_COND_HPH); + + pr_debug("%s: leave ret %d\n", __func__, ret); + return ret; + +err_hphr_ocp_irq: + wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc); +err_hphl_ocp_irq: + wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc); +err_release_irq: + wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc); +err_potential_irq: + wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc); +err_remove_irq: + wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc); +err_insert_irq: + wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock); + + mutex_destroy(&mbhc->mbhc_lock); + + pr_debug("%s: leave ret %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(wcd9xxx_mbhc_init); + +void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc) +{ + struct wcd9xxx_core_resource *core_res = + mbhc->resmgr->core_res; + + wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC | + 1 << WCD9XXX_COND_HPH); + + wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc); + wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc); + wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc); + wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc); + wcd9xxx_free_irq(core_res, mbhc->intr_ids->hs_jack_switch, mbhc); + wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc); + wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc); + + mutex_destroy(&mbhc->mbhc_lock); + wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock); + wcd9xxx_cleanup_debugfs(mbhc); +} +EXPORT_SYMBOL(wcd9xxx_mbhc_deinit); + +MODULE_DESCRIPTION("wcd9xxx MBHC module"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h new file mode 100644 index 0000000000000000000000000000000000000000..e35f7d4adc2dc31e8a4cb5033644f12bb02ab153 --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-mbhc.h @@ -0,0 +1,492 @@ +/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __WCD9XXX_MBHC_H__ +#define __WCD9XXX_MBHC_H__ + +#include "wcd9xxx-resmgr.h" +#include "wcdcal-hwdep.h" + +#define WCD9XXX_CFILT_FAST_MODE 0x00 +#define WCD9XXX_CFILT_SLOW_MODE 0x40 +#define WCD9XXX_CFILT_EXT_PRCHG_EN 0x30 +#define WCD9XXX_CFILT_EXT_PRCHG_DSBL 0x00 + +#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100 + +struct mbhc_micbias_regs { + u16 cfilt_val; + u16 cfilt_ctl; + u16 mbhc_reg; + u16 int_rbias; + u16 ctl_reg; + u8 cfilt_sel; +}; + +enum mbhc_v_index { + MBHC_V_IDX_CFILT, + MBHC_V_IDX_VDDIO, + MBHC_V_IDX_NUM, +}; + +enum mbhc_cal_type { + MBHC_CAL_MCLK, + MBHC_CAL_RCO, + MBHC_CAL_NUM, +}; + +enum mbhc_impedance_detect_stages { + MBHC_ZDET_PRE_MEASURE, + MBHC_ZDET_POST_MEASURE, + MBHC_ZDET_GAIN_0, + MBHC_ZDET_GAIN_1, + MBHC_ZDET_GAIN_2, + MBHC_ZDET_HPHR_RAMP_DISABLE, + MBHC_ZDET_HPHL_RAMP_DISABLE, + MBHC_ZDET_RAMP_DISABLE, + MBHC_ZDET_HPHR_PA_DISABLE, + MBHC_ZDET_PA_DISABLE, + MBHC_ZDET_GAIN_UPDATE_1X, +}; + +/* Zone assignments used in WCD9330 for Zdet */ +enum mbhc_zdet_zones { + ZL_ZONE1__ZR_ZONE1, + ZL_ZONE2__ZR_ZONE2, + ZL_ZONE3__ZR_ZONE3, + ZL_ZONE2__ZR_ZONE1, + ZL_ZONE3__ZR_ZONE1, + ZL_ZONE1__ZR_ZONE2, + ZL_ZONE1__ZR_ZONE3, + ZL_ZR_NOT_IN_ZONE1, +}; + +/* Data used by MBHC */ +struct mbhc_internal_cal_data { + u16 dce_z; + u16 dce_nsc_cs_z; + u16 dce_mb; + u16 sta_z; + u16 sta_mb; + u32 t_sta_dce; + u32 t_dce; + u32 t_sta; + u32 micb_mv; + u16 v_ins_hu[MBHC_V_IDX_NUM]; + u16 v_ins_h[MBHC_V_IDX_NUM]; + u16 v_b1_hu[MBHC_V_IDX_NUM]; + u16 v_b1_h[MBHC_V_IDX_NUM]; + u16 v_brh[MBHC_V_IDX_NUM]; + u16 v_brl; + u16 v_no_mic; + s16 v_inval_ins_low; + s16 v_inval_ins_high; + u16 v_cs_ins_h; +}; + +enum wcd9xxx_mbhc_plug_type { + PLUG_TYPE_INVALID = -1, + PLUG_TYPE_NONE, + PLUG_TYPE_HEADSET, + PLUG_TYPE_HEADPHONE, + PLUG_TYPE_HIGH_HPH, + PLUG_TYPE_GND_MIC_SWAP, + PLUG_TYPE_ANC_HEADPHONE, +}; + +enum wcd9xxx_mbhc_micbias_type { + MBHC_PRIMARY_MIC_MB, + MBHC_ANC_MIC_MB, +}; + +enum wcd9xxx_micbias_num { + MBHC_MICBIAS_INVALID = -1, + MBHC_MICBIAS1, + MBHC_MICBIAS2, + MBHC_MICBIAS3, + MBHC_MICBIAS4, +}; + +enum hw_jack_type { + FOUR_POLE_JACK = 0, + FIVE_POLE_JACK, + SIX_POLE_JACK, +}; + +enum wcd9xx_mbhc_micbias_enable_bits { + MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET, + MBHC_MICBIAS_ENABLE_REGULAR_HEADSET, +}; + +enum wcd9xx_mbhc_cs_enable_bits { + MBHC_CS_ENABLE_POLLING, + MBHC_CS_ENABLE_INSERTION, + MBHC_CS_ENABLE_REMOVAL, + MBHC_CS_ENABLE_DET_ANC, +}; + +enum wcd9xxx_mbhc_state { + MBHC_STATE_NONE = -1, + MBHC_STATE_POTENTIAL, + MBHC_STATE_POTENTIAL_RECOVERY, + MBHC_STATE_RELEASE, +}; + +enum wcd9xxx_mbhc_btn_det_mem { + MBHC_BTN_DET_V_BTN_LOW, + MBHC_BTN_DET_V_BTN_HIGH, + MBHC_BTN_DET_N_READY, + MBHC_BTN_DET_N_CIC, + MBHC_BTN_DET_GAIN +}; + +enum wcd9xxx_mbhc_clk_freq { + TAIKO_MCLK_12P2MHZ = 0, + TAIKO_MCLK_9P6MHZ, + TAIKO_NUM_CLK_FREQS, +}; + +enum wcd9xxx_mbhc_event_state { + MBHC_EVENT_PA_HPHL, + MBHC_EVENT_PA_HPHR, + MBHC_EVENT_PRE_TX_3_ON, + MBHC_EVENT_POST_TX_3_OFF, +}; + +enum mbhc_hph_type { + MBHC_HPH_NONE = 0, + MBHC_HPH_MONO, + MBHC_HPH_STEREO, +}; + +struct wcd9xxx_mbhc_general_cfg { + u8 t_ldoh; + u8 t_bg_fast_settle; + u8 t_shutdown_plug_rem; + u8 mbhc_nsa; + u8 mbhc_navg; + u8 v_micbias_l; + u8 v_micbias; + u8 mbhc_reserved; + u16 settle_wait; + u16 t_micbias_rampup; + u16 t_micbias_rampdown; + u16 t_supply_bringup; +} __packed; + +struct wcd9xxx_mbhc_plug_detect_cfg { + u32 mic_current; + u32 hph_current; + u16 t_mic_pid; + u16 t_ins_complete; + u16 t_ins_retry; + u16 v_removal_delta; + u8 micbias_slow_ramp; + u8 reserved0; + u8 reserved1; + u8 reserved2; +} __packed; + +struct wcd9xxx_mbhc_plug_type_cfg { + u8 av_detect; + u8 mono_detect; + u8 num_ins_tries; + u8 reserved0; + s16 v_no_mic; + s16 v_av_min; + s16 v_av_max; + s16 v_hs_min; + s16 v_hs_max; + u16 reserved1; +} __packed; + +struct wcd9xxx_mbhc_btn_detect_cfg { + s8 c[8]; + u8 nc; + u8 n_meas; + u8 mbhc_nsc; + u8 n_btn_meas; + u8 n_btn_con; + u8 num_btn; + u8 reserved0; + u8 reserved1; + u16 t_poll; + u16 t_bounce_wait; + u16 t_rel_timeout; + s16 v_btn_press_delta_sta; + s16 v_btn_press_delta_cic; + u16 t_btn0_timeout; + s16 _v_btn_low[0]; /* v_btn_low[num_btn] */ + s16 _v_btn_high[0]; /* v_btn_high[num_btn] */ + u8 _n_ready[TAIKO_NUM_CLK_FREQS]; + u8 _n_cic[TAIKO_NUM_CLK_FREQS]; + u8 _gain[TAIKO_NUM_CLK_FREQS]; +} __packed; + +struct wcd9xxx_mbhc_imped_detect_cfg { + u8 _hs_imped_detect; + u8 _n_rload; + u8 _hph_keep_on; + u8 _repeat_rload_calc; + u16 _t_dac_ramp_time; + u16 _rhph_high; + u16 _rhph_low; + u16 _rload[0]; /* rload[n_rload] */ + u16 _alpha[0]; /* alpha[n_rload] */ + u16 _beta[3]; +} __packed; + +struct wcd9xxx_mbhc_config { + bool read_fw_bin; + /* + * void* calibration contains: + * struct wcd9xxx_mbhc_general_cfg generic; + * struct wcd9xxx_mbhc_plug_detect_cfg plug_det; + * struct wcd9xxx_mbhc_plug_type_cfg plug_type; + * struct wcd9xxx_mbhc_btn_detect_cfg btn_det; + * struct wcd9xxx_mbhc_imped_detect_cfg imped_det; + * Note: various size depends on btn_det->num_btn + */ + void *calibration; + enum wcd9xxx_micbias_num micbias; + enum wcd9xxx_micbias_num anc_micbias; + int (*mclk_cb_fn)(struct snd_soc_codec*, int, bool); + unsigned int mclk_rate; + unsigned int gpio; + unsigned int gpio_irq; + int gpio_level_insert; + bool insert_detect; /* codec has own MBHC_INSERT_DETECT */ + bool detect_extn_cable; + /* bit mask of enum wcd9xx_mbhc_micbias_enable_bits */ + unsigned long micbias_enable_flags; + /* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */ + bool (*swap_gnd_mic)(struct snd_soc_codec *); + unsigned long cs_enable_flags; + bool use_int_rbias; + bool do_recalibration; + bool use_vddio_meas; + bool enable_anc_mic_detect; + enum hw_jack_type hw_jack_type; + int key_code[8]; +}; + +struct wcd9xxx_cfilt_mode { + u8 reg_mode_val; + u8 cur_mode_val; + u8 reg_mask; +}; + +struct wcd9xxx_mbhc_intr { + int poll_plug_rem; + int shortavg_complete; + int potential_button_press; + int button_release; + int dce_est_complete; + int insertion; + int hph_left_ocp; + int hph_right_ocp; + int hs_jack_switch; +}; + +struct wcd9xxx_mbhc_cb { + void (*enable_mux_bias_block)(struct snd_soc_codec *); + void (*cfilt_fast_mode)(struct snd_soc_codec *, struct wcd9xxx_mbhc *); + void (*codec_specific_cal)(struct snd_soc_codec *, + struct wcd9xxx_mbhc *); + struct wcd9xxx_cfilt_mode (*switch_cfilt_mode)(struct wcd9xxx_mbhc *, + bool); + void (*select_cfilt)(struct snd_soc_codec *, struct wcd9xxx_mbhc *); + enum wcd9xxx_cdc_type (*get_cdc_type)(void); + void (*enable_clock_gate)(struct snd_soc_codec *, bool); + int (*setup_zdet)(struct wcd9xxx_mbhc *, + enum mbhc_impedance_detect_stages stage); + void (*compute_impedance)(struct wcd9xxx_mbhc *, s16 *, s16 *, + uint32_t *, uint32_t *); + void (*zdet_error_approx)(struct wcd9xxx_mbhc *, uint32_t *, + uint32_t *); + void (*enable_mbhc_txfe)(struct snd_soc_codec *, bool); + int (*enable_mb_source)(struct snd_soc_codec *, bool, bool); + void (*setup_int_rbias)(struct snd_soc_codec *, bool); + void (*pull_mb_to_vddio)(struct snd_soc_codec *, bool); + bool (*insert_rem_status)(struct snd_soc_codec *); + void (*micbias_pulldown_ctrl)(struct wcd9xxx_mbhc *, bool); + int (*codec_rco_ctrl)(struct snd_soc_codec *, bool); + void (*hph_auto_pulldown_ctrl)(struct snd_soc_codec *, bool); + struct firmware_cal * (*get_hwdep_fw_cal)(struct snd_soc_codec *, + enum wcd_cal_type); +}; + +struct wcd9xxx_mbhc { + bool polling_active; + /* Delayed work to report long button press */ + struct delayed_work mbhc_btn_dwork; + int buttons_pressed; + enum wcd9xxx_mbhc_state mbhc_state; + struct wcd9xxx_mbhc_config *mbhc_cfg; + const struct wcd9xxx_mbhc_cb *mbhc_cb; + + struct mbhc_internal_cal_data mbhc_data; + + struct mbhc_micbias_regs mbhc_bias_regs; + struct mbhc_micbias_regs mbhc_anc_bias_regs; + + bool mbhc_micbias_switched; + + u32 hph_status; /* track headhpone status */ + u8 hphlocp_cnt; /* headphone left ocp retry */ + u8 hphrocp_cnt; /* headphone right ocp retry */ + + /* Work to perform MBHC Firmware Read */ + struct delayed_work mbhc_firmware_dwork; + const struct firmware *mbhc_fw; + struct firmware_cal *mbhc_cal; + + struct delayed_work mbhc_insert_dwork; + + u8 current_plug; + struct work_struct correct_plug_swch; + /* + * Work to perform polling on microphone voltage + * in order to correct plug type once plug type + * is detected as headphone + */ + struct work_struct correct_plug_noswch; + bool hs_detect_work_stop; + + bool lpi_enabled; /* low power insertion detection */ + bool in_swch_irq_handler; + + struct wcd9xxx_resmgr *resmgr; + struct snd_soc_codec *codec; + + bool no_mic_headset_override; + + /* track PA/DAC state to sync with userspace */ + unsigned long hph_pa_dac_state; + /* + * save codec's state with resmgr event notification + * bit flags of enum wcd9xxx_mbhc_event_state + */ + unsigned long event_state; + + unsigned long mbhc_last_resume; /* in jiffies */ + + bool insert_detect_level_insert; + + struct snd_soc_jack headset_jack; + struct snd_soc_jack button_jack; + + struct notifier_block nblock; + + bool micbias_enable; + int (*micbias_enable_cb)(struct snd_soc_codec*, bool, + enum wcd9xxx_micbias_num); + + bool impedance_detect; + /* impedance of hphl and hphr */ + uint32_t zl, zr; + + u32 rco_clk_rate; + + bool update_z; + + u8 scaling_mux_in; + /* Holds codec specific interrupt mapping */ + const struct wcd9xxx_mbhc_intr *intr_ids; + + /* Indicates status of current source switch */ + bool is_cs_enabled; + + /* Holds type of Headset - Mono/Stereo */ + enum mbhc_hph_type hph_type; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_poke; + struct dentry *debugfs_mbhc; +#endif + + struct mutex mbhc_lock; +}; + +#define WCD9XXX_MBHC_CAL_SIZE(buttons, rload) ( \ + sizeof(enum wcd9xxx_micbias_num) + \ + sizeof(struct wcd9xxx_mbhc_general_cfg) + \ + sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \ + ((sizeof(s16) + sizeof(s16)) * buttons) + \ + sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \ + sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \ + sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \ + ((sizeof(u16) + sizeof(u16)) * rload) \ + ) + +#define WCD9XXX_MBHC_CAL_GENERAL_PTR(cali) ( \ + (struct wcd9xxx_mbhc_general_cfg *) cali) +#define WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali) ( \ + (struct wcd9xxx_mbhc_plug_detect_cfg *) \ + &(WCD9XXX_MBHC_CAL_GENERAL_PTR(cali)[1])) +#define WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \ + (struct wcd9xxx_mbhc_plug_type_cfg *) \ + &(WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali)[1])) +#define WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali) ( \ + (struct wcd9xxx_mbhc_btn_detect_cfg *) \ + &(WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali)[1])) +#define WCD9XXX_MBHC_CAL_IMPED_DET_PTR(cali) ( \ + (struct wcd9xxx_mbhc_imped_detect_cfg *) \ + (((void *)&WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \ + (WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \ + (sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \ + sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \ + ) + +/* minimum size of calibration data assuming there is only one button and + * one rload. + */ +#define WCD9XXX_MBHC_CAL_MIN_SIZE ( \ + sizeof(struct wcd9xxx_mbhc_general_cfg) + \ + sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \ + sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \ + sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \ + sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \ + (sizeof(u16) * 2) \ + ) + +#define WCD9XXX_MBHC_CAL_BTN_SZ(cfg_ptr) ( \ + sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \ + (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \ + sizeof(cfg_ptr->_v_btn_high[0])))) + +#define WCD9XXX_MBHC_CAL_IMPED_MIN_SZ ( \ + sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + sizeof(u16) * 2) + +#define WCD9XXX_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \ + sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \ + (cfg_ptr->_n_rload * \ + (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0])))) + +int wcd9xxx_mbhc_set_keycode(struct wcd9xxx_mbhc *mbhc); +int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc, + struct wcd9xxx_mbhc_config *mbhc_cfg); +void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc); +int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr, + struct snd_soc_codec *codec, + int (*micbias_enable_cb)(struct snd_soc_codec*, bool, + enum wcd9xxx_micbias_num), + const struct wcd9xxx_mbhc_cb *mbhc_cb, + const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids, + int rco_clk_rate, + bool impedance_det_en); +void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc); +void *wcd9xxx_mbhc_cal_btn_det_mp( + const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det, + const enum wcd9xxx_mbhc_btn_det_mem mem); +int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl, + uint32_t *zr); +#endif /* __WCD9XXX_MBHC_H__ */ diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.c b/sound/soc/codecs/wcd9xxx-resmgr-v2.c new file mode 100644 index 0000000000000000000000000000000000000000..fde13d274b40b87e599ed13d57dbd17d90f99a8f --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-resmgr-v2.c @@ -0,0 +1,676 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd9xxx-resmgr-v2.h" + +#define WCD9XXX_RCO_CALIBRATION_DELAY_INC_US 5000 +#define WCD93XX_ANA_BIAS 0x0601 +#define WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41 +#define WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42 + +static void wcd_resmgr_set_sido_input_src(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src); +static const char *wcd_resmgr_clk_type_to_str(enum wcd_clock_type clk_type) +{ + if (clk_type == WCD_CLK_OFF) + return "WCD_CLK_OFF"; + else if (clk_type == WCD_CLK_RCO) + return "WCD_CLK_RCO"; + else if (clk_type == WCD_CLK_MCLK) + return "WCD_CLK_MCLK"; + else + return "WCD_CLK_UNDEFINED"; +} + +static int wcd_resmgr_codec_reg_update_bits(struct wcd9xxx_resmgr_v2 *resmgr, + u16 reg, u8 mask, u8 val) +{ + bool change; + int ret; + + if (resmgr->codec_type == WCD934X) { + /* Tavil does not support ANA_CLK_TOP register */ + if (reg == WCD9335_ANA_CLK_TOP) + return 0; + } else { + /* Tasha does not support CLK_SYS_MCLK_PRG register */ + if (reg == WCD934X_CLK_SYS_MCLK_PRG) + return 0; + } + if (resmgr->codec) { + ret = snd_soc_update_bits(resmgr->codec, reg, mask, val); + } else if (resmgr->core_res->wcd_core_regmap) { + ret = regmap_update_bits_check( + resmgr->core_res->wcd_core_regmap, + reg, mask, val, &change); + if (!ret) + ret = change; + } else { + pr_err("%s: codec/regmap not defined\n", __func__); + ret = -EINVAL; + } + + return ret; +} + +static int wcd_resmgr_codec_reg_read(struct wcd9xxx_resmgr_v2 *resmgr, + unsigned int reg) +{ + int val, ret; + + if (resmgr->codec_type == WCD934X) { + if (reg == WCD9335_ANA_CLK_TOP) + return 0; + } else { + if (reg == WCD934X_CLK_SYS_MCLK_PRG) + return 0; + } + if (resmgr->codec) { + val = snd_soc_read(resmgr->codec, reg); + } else if (resmgr->core_res->wcd_core_regmap) { + ret = regmap_read(resmgr->core_res->wcd_core_regmap, + reg, &val); + if (ret) + val = ret; + } else { + pr_err("%s: wcd regmap is null\n", __func__); + return -EINVAL; + } + + return val; +} + +/* + * wcd_resmgr_get_clk_type() + * Returns clk type that is currently enabled + */ +int wcd_resmgr_get_clk_type(struct wcd9xxx_resmgr_v2 *resmgr) +{ + if (!resmgr) { + pr_err("%s: resmgr not initialized\n", __func__); + return -EINVAL; + } + return resmgr->clk_type; +} + +static void wcd_resmgr_cdc_specific_get_clk(struct wcd9xxx_resmgr_v2 *resmgr, + int clk_users) +{ + /* Caller of this function should have acquired BG_CLK lock */ + if (clk_users) { + if (resmgr->resmgr_cb && + resmgr->resmgr_cb->cdc_rco_ctrl) { + while (clk_users--) + resmgr->resmgr_cb->cdc_rco_ctrl(resmgr->codec, + true); + } + } +} + +void wcd_resmgr_post_ssr_v2(struct wcd9xxx_resmgr_v2 *resmgr) +{ + int old_bg_audio_users; + int old_clk_rco_users, old_clk_mclk_users; + + WCD9XXX_V2_BG_CLK_LOCK(resmgr); + + old_bg_audio_users = resmgr->master_bias_users; + old_clk_mclk_users = resmgr->clk_mclk_users; + old_clk_rco_users = resmgr->clk_rco_users; + resmgr->master_bias_users = 0; + resmgr->clk_mclk_users = 0; + resmgr->clk_rco_users = 0; + resmgr->clk_type = WCD_CLK_OFF; + + pr_debug("%s: old_bg_audio_users=%d old_clk_mclk_users=%d old_clk_rco_users=%d\n", + __func__, old_bg_audio_users, + old_clk_mclk_users, old_clk_rco_users); + + if (old_bg_audio_users) { + while (old_bg_audio_users--) + wcd_resmgr_enable_master_bias(resmgr); + } + + if (old_clk_mclk_users) { + while (old_clk_mclk_users--) + wcd_resmgr_enable_clk_block(resmgr, WCD_CLK_MCLK); + } + + if (old_clk_rco_users) + wcd_resmgr_cdc_specific_get_clk(resmgr, old_clk_rco_users); + + WCD9XXX_V2_BG_CLK_UNLOCK(resmgr); +} + + +/* + * wcd_resmgr_enable_master_bias: enable codec master bias + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +int wcd_resmgr_enable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr) +{ + mutex_lock(&resmgr->master_bias_lock); + + resmgr->master_bias_users++; + if (resmgr->master_bias_users == 1) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x80, 0x80); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x40, 0x40); + /* + * 1ms delay is required after pre-charge is enabled + * as per HW requirement + */ + usleep_range(1000, 1100); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x40, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_BIAS, 0x20, 0x00); + } + + pr_debug("%s: current master bias users: %d\n", __func__, + resmgr->master_bias_users); + + mutex_unlock(&resmgr->master_bias_lock); + return 0; +} + +/* + * wcd_resmgr_disable_master_bias: disable codec master bias + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +int wcd_resmgr_disable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr) +{ + mutex_lock(&resmgr->master_bias_lock); + if (resmgr->master_bias_users <= 0) { + mutex_unlock(&resmgr->master_bias_lock); + return -EINVAL; + } + + resmgr->master_bias_users--; + if (resmgr->master_bias_users == 0) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x80, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_BIAS, 0x20, 0x00); + } + mutex_unlock(&resmgr->master_bias_lock); + return 0; +} + +static int wcd_resmgr_enable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr) +{ + /* Enable mclk requires master bias to be enabled first */ + if (resmgr->master_bias_users <= 0) { + pr_err("%s: Cannot turn on MCLK, BG is not enabled\n", + __func__); + return -EINVAL; + } + + if (((resmgr->clk_mclk_users == 0) && + (resmgr->clk_type == WCD_CLK_MCLK)) || + ((resmgr->clk_mclk_users > 0) && + (resmgr->clk_type != WCD_CLK_MCLK))) { + pr_err("%s: Error enabling MCLK, clk_type: %s\n", + __func__, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + return -EINVAL; + } + + if (++resmgr->clk_mclk_users == 1) { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, 0x80, 0x80); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, 0x08, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, 0x04, 0x04); + if (resmgr->codec_type == WCD934X) { + /* + * In tavil clock contrl register is changed + * to CLK_SYS_MCLK_PRG + */ + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x91, 0x91); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x04, 0x04); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CODEC_RPM_CLK_GATE, 0x03, 0x00); + wcd_resmgr_set_sido_input_src(resmgr, + SIDO_SOURCE_RCO_BG); + } else { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + } + /* + * 10us sleep is required after clock is enabled + * as per HW requirement + */ + usleep_range(10, 15); + } + + resmgr->clk_type = WCD_CLK_MCLK; + + pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__, + resmgr->clk_mclk_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +static int wcd_resmgr_disable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr) +{ + if (resmgr->clk_mclk_users <= 0) { + pr_err("%s: No mclk users, cannot disable mclk\n", __func__); + return -EINVAL; + } + + if (--resmgr->clk_mclk_users == 0) { + if (resmgr->clk_rco_users > 0) { + /* MCLK to RCO switch */ + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, + 0x08, 0x08); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x02); + resmgr->clk_type = WCD_CLK_RCO; + } else { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, + 0x04, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x81, 0x00); + resmgr->clk_type = WCD_CLK_OFF; + } + + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP, + 0x80, 0x00); + } + + if ((resmgr->codec_type == WCD934X) && + (resmgr->clk_type == WCD_CLK_OFF)) + wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL); + + pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__, + resmgr->clk_mclk_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +static void wcd_resmgr_set_buck_accuracy(struct wcd9xxx_resmgr_v2 *resmgr) +{ + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x02, 0x02); + /* 100us sleep needed after HIGH_ACCURACY_PRE_EN1 */ + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x01, 0x01); + /* 100us sleep needed after HIGH_ACCURACY_PRE_EN2 */ + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x04, 0x04); + /* 100us sleep needed after HIGH_ACCURACY_EN */ + usleep_range(100, 110); +} + +static int wcd_resmgr_enable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr) +{ + bool rco_cal_done = true; + + resmgr->clk_rco_users++; + if ((resmgr->clk_rco_users == 1) && + ((resmgr->clk_type == WCD_CLK_OFF) || + (resmgr->clk_mclk_users == 0))) { + pr_warn("%s: RCO enable requires MCLK to be ON first\n", + __func__); + resmgr->clk_rco_users--; + return -EINVAL; + } else if ((resmgr->clk_rco_users == 1) && + (resmgr->clk_mclk_users)) { + /* RCO Enable */ + if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_RCO, + 0x80, 0x80); + if (resmgr->codec_type == WCD934X) + wcd_resmgr_set_buck_accuracy(resmgr); + } + + /* + * 20us required after RCO BG is enabled as per HW + * requirements + */ + usleep_range(20, 25); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x40, 0x40); + /* + * 20us required after RCO is enabled as per HW + * requirements + */ + usleep_range(20, 25); + /* RCO Calibration */ + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x04, 0x04); + if (resmgr->codec_type == WCD934X) + /* + * For wcd934x codec, 20us sleep is needed + * after enabling RCO calibration + */ + usleep_range(20, 25); + + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x04, 0x00); + if (resmgr->codec_type == WCD934X) + /* + * For wcd934x codec, 20us sleep is needed + * after disabling RCO calibration + */ + usleep_range(20, 25); + + /* RCO calibration takes app. 5ms to complete */ + usleep_range(WCD9XXX_RCO_CALIBRATION_DELAY_INC_US, + WCD9XXX_RCO_CALIBRATION_DELAY_INC_US + 100); + if (wcd_resmgr_codec_reg_read(resmgr, WCD9335_ANA_RCO) & 0x02) + rco_cal_done = false; + + WARN((!rco_cal_done), "RCO Calibration failed\n"); + + /* Switch MUX to RCO */ + if (resmgr->clk_mclk_users == 1) { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, + 0x08, 0x08); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, + 0x02, 0x02); + resmgr->clk_type = WCD_CLK_RCO; + } + } + pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__, + resmgr->clk_rco_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +static int wcd_resmgr_disable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr) +{ + if ((resmgr->clk_rco_users <= 0) || + (resmgr->clk_type == WCD_CLK_OFF)) { + pr_err("%s: rco_clk_users = %d, clk_type = %d, cannot disable\n", + __func__, resmgr->clk_rco_users, resmgr->clk_type); + return -EINVAL; + } + + resmgr->clk_rco_users--; + + if ((resmgr->clk_rco_users == 0) && + (resmgr->clk_type == WCD_CLK_RCO)) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP, + 0x08, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, + 0x02, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP, + 0x04, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x40, 0x00); + if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_RCO, + 0x80, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, + 0x01, 0x00); + resmgr->clk_type = WCD_CLK_OFF; + } else if ((resmgr->clk_rco_users == 0) && + (resmgr->clk_mclk_users)) { + /* Disable RCO while MCLK is ON */ + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x40, 0x00); + if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_RCO, + 0x80, 0x00); + } + + if ((resmgr->codec_type == WCD934X) && + (resmgr->clk_type == WCD_CLK_OFF)) + wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL); + + pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__, + resmgr->clk_rco_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +/* + * wcd_resmgr_enable_clk_block: enable MCLK or RCO + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + * @type: Clock type to enable + */ +int wcd_resmgr_enable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type) +{ + int ret; + + switch (type) { + case WCD_CLK_MCLK: + ret = wcd_resmgr_enable_clk_mclk(resmgr); + break; + case WCD_CLK_RCO: + ret = wcd_resmgr_enable_clk_rco(resmgr); + break; + default: + pr_err("%s: Unknown Clock type: %s\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + ret = -EINVAL; + break; + }; + + if (ret) + pr_err("%s: Enable clock %s failed\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + + return ret; +} + +static void wcd_resmgr_set_sido_input_src(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src) +{ + if (!resmgr) + return; + + if (sido_src == resmgr->sido_input_src) + return; + + if (sido_src == SIDO_SOURCE_INTERNAL) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x04, 0x00); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x03, 0x00); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_RCO, + 0x80, 0x00); + usleep_range(100, 110); + resmgr->sido_input_src = SIDO_SOURCE_INTERNAL; + pr_debug("%s: sido input src to internal\n", __func__); + } else if (sido_src == SIDO_SOURCE_RCO_BG) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_RCO, + 0x80, 0x80); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x02, 0x02); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x01, 0x01); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x04, 0x04); + usleep_range(100, 110); + resmgr->sido_input_src = SIDO_SOURCE_RCO_BG; + pr_debug("%s: sido input src to external\n", __func__); + } +} + +/* + * wcd_resmgr_set_sido_input_src_locked: + * Set SIDO input in BG_CLK locked context + * + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + * @sido_src: Select the SIDO input source + */ +void wcd_resmgr_set_sido_input_src_locked(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src) +{ + if (!resmgr) + return; + + WCD9XXX_V2_BG_CLK_LOCK(resmgr); + wcd_resmgr_set_sido_input_src(resmgr, sido_src); + WCD9XXX_V2_BG_CLK_UNLOCK(resmgr); +} +EXPORT_SYMBOL(wcd_resmgr_set_sido_input_src_locked); + +/* + * wcd_resmgr_disable_clk_block: disable MCLK or RCO + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + * @type: Clock type to disable + */ +int wcd_resmgr_disable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type) +{ + int ret; + + switch (type) { + case WCD_CLK_MCLK: + ret = wcd_resmgr_disable_clk_mclk(resmgr); + break; + case WCD_CLK_RCO: + ret = wcd_resmgr_disable_clk_rco(resmgr); + break; + default: + pr_err("%s: Unknown Clock type: %s\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + ret = -EINVAL; + break; + }; + + if (ret) + pr_err("%s: Disable clock %s failed\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + + return ret; +} + +/* + * wcd_resmgr_init: initialize wcd resource manager + * @core_res: handle to struct wcd9xxx_core_resource + * + * Early init call without a handle to snd_soc_codec * + */ +struct wcd9xxx_resmgr_v2 *wcd_resmgr_init( + struct wcd9xxx_core_resource *core_res, + struct snd_soc_codec *codec) +{ + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd9xxx *wcd9xxx; + + resmgr = kzalloc(sizeof(struct wcd9xxx_resmgr_v2), GFP_KERNEL); + if (!resmgr) + return ERR_PTR(-ENOMEM); + + wcd9xxx = container_of(core_res, struct wcd9xxx, core_res); + if (!wcd9xxx) { + kfree(resmgr); + pr_err("%s: Cannot get wcd9xx pointer\n", __func__); + return ERR_PTR(-EINVAL); + } + + mutex_init(&resmgr->codec_bg_clk_lock); + mutex_init(&resmgr->master_bias_lock); + resmgr->master_bias_users = 0; + resmgr->clk_mclk_users = 0; + resmgr->clk_rco_users = 0; + resmgr->master_bias_users = 0; + resmgr->codec = codec; + resmgr->core_res = core_res; + resmgr->sido_input_src = SIDO_SOURCE_INTERNAL; + resmgr->codec_type = wcd9xxx->type; + + return resmgr; +} + +/* + * wcd_resmgr_remove: Clean-up wcd resource manager + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +void wcd_resmgr_remove(struct wcd9xxx_resmgr_v2 *resmgr) +{ + mutex_destroy(&resmgr->master_bias_lock); + kfree(resmgr); +} + +/* + * wcd_resmgr_post_init: post init call to assign codec handle + * @resmgr: handle to struct wcd9xxx_resmgr_v2 created during early init + * @resmgr_cb: codec callback function for resmgr + * @codec: handle to struct snd_soc_codec + */ +int wcd_resmgr_post_init(struct wcd9xxx_resmgr_v2 *resmgr, + const struct wcd_resmgr_cb *resmgr_cb, + struct snd_soc_codec *codec) +{ + if (!resmgr) { + pr_err("%s: resmgr not allocated\n", __func__); + return -EINVAL; + } + + if (!codec) { + pr_err("%s: Codec memory is NULL, nothing to post init\n", + __func__); + return -EINVAL; + } + + resmgr->codec = codec; + resmgr->resmgr_cb = resmgr_cb; + + return 0; +} +MODULE_DESCRIPTION("wcd9xxx resmgr v2 module"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.h b/sound/soc/codecs/wcd9xxx-resmgr-v2.h new file mode 100644 index 0000000000000000000000000000000000000000..f605a249a620badae1c9bcfa5fbc8d2e8dad6c08 --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-resmgr-v2.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __WCD9XXX_COMMON_V2_H__ +#define __WCD9XXX_COMMON_V2_H__ + +#include +#include + +enum wcd_clock_type { + WCD_CLK_OFF, + WCD_CLK_RCO, + WCD_CLK_MCLK, +}; + +enum { + SIDO_SOURCE_INTERNAL, + SIDO_SOURCE_RCO_BG, +}; + +struct wcd_resmgr_cb { + int (*cdc_rco_ctrl)(struct snd_soc_codec *, bool); +}; + +struct wcd9xxx_resmgr_v2 { + struct snd_soc_codec *codec; + struct wcd9xxx_core_resource *core_res; + + int master_bias_users; + int clk_mclk_users; + int clk_rco_users; + + struct mutex codec_bg_clk_lock; + struct mutex master_bias_lock; + + enum codec_variant codec_type; + enum wcd_clock_type clk_type; + + const struct wcd_resmgr_cb *resmgr_cb; + int sido_input_src; +}; + +#define WCD9XXX_V2_BG_CLK_LOCK(resmgr) \ +{ \ + struct wcd9xxx_resmgr_v2 *__resmgr = resmgr; \ + pr_debug("%s: Acquiring BG_CLK\n", __func__); \ + mutex_lock(&__resmgr->codec_bg_clk_lock); \ + pr_debug("%s: Acquiring BG_CLK done\n", __func__); \ +} + +#define WCD9XXX_V2_BG_CLK_UNLOCK(resmgr) \ +{ \ + struct wcd9xxx_resmgr_v2 *__resmgr = resmgr; \ + pr_debug("%s: Releasing BG_CLK\n", __func__); \ + mutex_unlock(&__resmgr->codec_bg_clk_lock); \ +} + +#define WCD9XXX_V2_BG_CLK_ASSERT_LOCKED(resmgr) \ +{ \ + WARN_ONCE(!mutex_is_locked(&resmgr->codec_bg_clk_lock), \ + "%s: BG_CLK lock should have acquired\n", __func__); \ +} + +int wcd_resmgr_enable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr); +int wcd_resmgr_disable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr); +struct wcd9xxx_resmgr_v2 *wcd_resmgr_init( + struct wcd9xxx_core_resource *core_res, + struct snd_soc_codec *codec); +void wcd_resmgr_remove(struct wcd9xxx_resmgr_v2 *resmgr); +int wcd_resmgr_post_init(struct wcd9xxx_resmgr_v2 *resmgr, + const struct wcd_resmgr_cb *resmgr_cb, + struct snd_soc_codec *codec); +int wcd_resmgr_enable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type); +int wcd_resmgr_disable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type); +int wcd_resmgr_get_clk_type(struct wcd9xxx_resmgr_v2 *resmgr); +void wcd_resmgr_post_ssr_v2(struct wcd9xxx_resmgr_v2 *resmgr); +void wcd_resmgr_set_sido_input_src_locked(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src); +#endif diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c new file mode 100644 index 0000000000000000000000000000000000000000..4b02652419013586a7e1ea6dafde01de9a28493e --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-resmgr.c @@ -0,0 +1,1099 @@ +/* Copyright (c) 2012-2014, 2016-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd9xxx-resmgr.h" + +static char wcd9xxx_event_string[][64] = { + "WCD9XXX_EVENT_INVALID", + + "WCD9XXX_EVENT_PRE_RCO_ON", + "WCD9XXX_EVENT_POST_RCO_ON", + "WCD9XXX_EVENT_PRE_RCO_OFF", + "WCD9XXX_EVENT_POST_RCO_OFF", + + "WCD9XXX_EVENT_PRE_MCLK_ON", + "WCD9XXX_EVENT_POST_MCLK_ON", + "WCD9XXX_EVENT_PRE_MCLK_OFF", + "WCD9XXX_EVENT_POST_MCLK_OFF", + + "WCD9XXX_EVENT_PRE_BG_OFF", + "WCD9XXX_EVENT_POST_BG_OFF", + "WCD9XXX_EVENT_PRE_BG_AUDIO_ON", + "WCD9XXX_EVENT_POST_BG_AUDIO_ON", + "WCD9XXX_EVENT_PRE_BG_MBHC_ON", + "WCD9XXX_EVENT_POST_BG_MBHC_ON", + + "WCD9XXX_EVENT_PRE_MICBIAS_1_OFF", + "WCD9XXX_EVENT_POST_MICBIAS_1_OFF", + "WCD9XXX_EVENT_PRE_MICBIAS_2_OFF", + "WCD9XXX_EVENT_POST_MICBIAS_2_OFF", + "WCD9XXX_EVENT_PRE_MICBIAS_3_OFF", + "WCD9XXX_EVENT_POST_MICBIAS_3_OFF", + "WCD9XXX_EVENT_PRE_MICBIAS_4_OFF", + "WCD9XXX_EVENT_POST_MICBIAS_4_OFF", + "WCD9XXX_EVENT_PRE_MICBIAS_1_ON", + "WCD9XXX_EVENT_POST_MICBIAS_1_ON", + "WCD9XXX_EVENT_PRE_MICBIAS_2_ON", + "WCD9XXX_EVENT_POST_MICBIAS_2_ON", + "WCD9XXX_EVENT_PRE_MICBIAS_3_ON", + "WCD9XXX_EVENT_POST_MICBIAS_3_ON", + "WCD9XXX_EVENT_PRE_MICBIAS_4_ON", + "WCD9XXX_EVENT_POST_MICBIAS_4_ON", + + "WCD9XXX_EVENT_PRE_CFILT_1_OFF", + "WCD9XXX_EVENT_POST_CFILT_1_OFF", + "WCD9XXX_EVENT_PRE_CFILT_2_OFF", + "WCD9XXX_EVENT_POST_CFILT_2_OFF", + "WCD9XXX_EVENT_PRE_CFILT_3_OFF", + "WCD9XXX_EVENT_POST_CFILT_3_OFF", + "WCD9XXX_EVENT_PRE_CFILT_1_ON", + "WCD9XXX_EVENT_POST_CFILT_1_ON", + "WCD9XXX_EVENT_PRE_CFILT_2_ON", + "WCD9XXX_EVENT_POST_CFILT_2_ON", + "WCD9XXX_EVENT_PRE_CFILT_3_ON", + "WCD9XXX_EVENT_POST_CFILT_3_ON", + + "WCD9XXX_EVENT_PRE_HPHL_PA_ON", + "WCD9XXX_EVENT_POST_HPHL_PA_OFF", + "WCD9XXX_EVENT_PRE_HPHR_PA_ON", + "WCD9XXX_EVENT_POST_HPHR_PA_OFF", + + "WCD9XXX_EVENT_POST_RESUME", + + "WCD9XXX_EVENT_PRE_TX_3_ON", + "WCD9XXX_EVENT_POST_TX_3_OFF", + + "WCD9XXX_EVENT_LAST", +}; + +#define WCD9XXX_RCO_CALIBRATION_RETRY_COUNT 5 +#define WCD9XXX_RCO_CALIBRATION_DELAY_US 5000 +#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100 +#define WCD9XXX_RCO_CALIBRATION_DELAY_INC_US 1000 + +struct wcd9xxx_resmgr_cond_entry { + unsigned short reg; + int shift; + bool invert; + enum wcd9xxx_resmgr_cond cond; + struct list_head list; +}; + +static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr + *resmgr); +static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_clock_type type); + +const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type) +{ + return wcd9xxx_event_string[type]; +} + +void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr, + const enum wcd9xxx_notify_event e) +{ + pr_debug("%s: notifier call event %d\n", __func__, e); + blocking_notifier_call_chain(&resmgr->notifier, e, resmgr); +} + +static void wcd9xxx_disable_bg(struct wcd9xxx_resmgr *resmgr) +{ + /* Notify bg mode change */ + wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_OFF); + /* Disable bg */ + snd_soc_update_bits(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, + 0x03, 0x00); + usleep_range(100, 110); + /* Notify bg mode change */ + wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF); +} + +/* + * BG enablement should always enable in slow mode. + * The fast mode doesn't need to be enabled as fast mode BG is to be driven + * by MBHC override. + */ +static void wcd9xxx_enable_bg(struct wcd9xxx_resmgr *resmgr) +{ + struct snd_soc_codec *codec = resmgr->codec; + + /* Enable BG in slow mode and precharge */ + snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80); + snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x04, 0x04); + snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01); + usleep_range(1000, 1100); + snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00); +} + +static void wcd9xxx_enable_bg_audio(struct wcd9xxx_resmgr *resmgr) +{ + /* Notify bandgap mode change */ + wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_AUDIO_ON); + wcd9xxx_enable_bg(resmgr); + /* Notify bandgap mode change */ + wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_AUDIO_ON); +} + +static void wcd9xxx_enable_bg_mbhc(struct wcd9xxx_resmgr *resmgr) +{ + struct snd_soc_codec *codec = resmgr->codec; + + /* Notify bandgap mode change */ + wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_MBHC_ON); + + /* + * mclk should be off or clk buff source souldn't be VBG + * Let's turn off mclk always + */ + WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2)); + + wcd9xxx_enable_bg(resmgr); + /* Notify bandgap mode change */ + wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_MBHC_ON); +} + +static void wcd9xxx_disable_clock_block(struct wcd9xxx_resmgr *resmgr) +{ + struct snd_soc_codec *codec = resmgr->codec; + + pr_debug("%s: enter\n", __func__); + WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr); + + /* Notify */ + if (resmgr->clk_type == WCD9XXX_CLK_RCO) + wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_OFF); + else + wcd9xxx_resmgr_notifier_call(resmgr, + WCD9XXX_EVENT_PRE_MCLK_OFF); + + switch (resmgr->codec_type) { + case WCD9XXX_CDC_TYPE_TOMTOM: + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00); + usleep_range(50, 55); + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02); + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x40, 0x40); + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x40, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x00); + break; + default: + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00); + usleep_range(50, 55); + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02); + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x00); + break; + } + usleep_range(50, 55); + /* Notify */ + if (resmgr->clk_type == WCD9XXX_CLK_RCO) { + wcd9xxx_resmgr_notifier_call(resmgr, + WCD9XXX_EVENT_POST_RCO_OFF); + } else { + wcd9xxx_resmgr_notifier_call(resmgr, + WCD9XXX_EVENT_POST_MCLK_OFF); + } + pr_debug("%s: leave\n", __func__); +} + +static void wcd9xxx_resmgr_cdc_specific_get_clk(struct wcd9xxx_resmgr *resmgr, + int clk_users) +{ + /* Caller of this function should have acquired + * BG_CLK lock + */ + WCD9XXX_BG_CLK_UNLOCK(resmgr); + if (clk_users) { + if (resmgr->resmgr_cb && + resmgr->resmgr_cb->cdc_rco_ctrl) { + while (clk_users--) + resmgr->resmgr_cb->cdc_rco_ctrl(resmgr->codec, + true); + } + } + /* Acquire BG_CLK lock before return */ + WCD9XXX_BG_CLK_LOCK(resmgr); +} + +void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr) +{ + int old_bg_audio_users, old_bg_mbhc_users; + int old_clk_rco_users, old_clk_mclk_users; + + pr_debug("%s: enter\n", __func__); + + WCD9XXX_BG_CLK_LOCK(resmgr); + old_bg_audio_users = resmgr->bg_audio_users; + old_bg_mbhc_users = resmgr->bg_mbhc_users; + old_clk_rco_users = resmgr->clk_rco_users; + old_clk_mclk_users = resmgr->clk_mclk_users; + resmgr->bg_audio_users = 0; + resmgr->bg_mbhc_users = 0; + resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF; + resmgr->clk_rco_users = 0; + resmgr->clk_mclk_users = 0; + resmgr->clk_type = WCD9XXX_CLK_OFF; + + if (old_bg_audio_users) { + while (old_bg_audio_users--) + wcd9xxx_resmgr_get_bandgap(resmgr, + WCD9XXX_BANDGAP_AUDIO_MODE); + } + + if (old_bg_mbhc_users) { + while (old_bg_mbhc_users--) + wcd9xxx_resmgr_get_bandgap(resmgr, + WCD9XXX_BANDGAP_MBHC_MODE); + } + + if (old_clk_mclk_users) { + while (old_clk_mclk_users--) + wcd9xxx_resmgr_get_clk_block(resmgr, WCD9XXX_CLK_MCLK); + } + + if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) { + wcd9xxx_resmgr_cdc_specific_get_clk(resmgr, old_clk_rco_users); + } else if (old_clk_rco_users) { + while (old_clk_rco_users--) + wcd9xxx_resmgr_get_clk_block(resmgr, + WCD9XXX_CLK_RCO); + } + WCD9XXX_BG_CLK_UNLOCK(resmgr); + pr_debug("%s: leave\n", __func__); +} + +/* + * wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref + * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE + */ +void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr, + const enum wcd9xxx_bandgap_type choice) +{ + enum wcd9xxx_clock_type clock_save = WCD9XXX_CLK_OFF; + + pr_debug("%s: enter, wants %d\n", __func__, choice); + + WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr); + switch (choice) { + case WCD9XXX_BANDGAP_AUDIO_MODE: + resmgr->bg_audio_users++; + if (resmgr->bg_audio_users == 1 && resmgr->bg_mbhc_users) { + /* + * Current bg is MBHC mode, about to switch to + * audio mode. + */ + WARN_ON(resmgr->bandgap_type != + WCD9XXX_BANDGAP_MBHC_MODE); + + /* BG mode can be changed only with clock off */ + if (resmgr->codec_type != WCD9XXX_CDC_TYPE_TOMTOM) + clock_save = wcd9xxx_save_clock(resmgr); + /* Swtich BG mode */ + wcd9xxx_disable_bg(resmgr); + wcd9xxx_enable_bg_audio(resmgr); + /* restore clock */ + if (resmgr->codec_type != WCD9XXX_CDC_TYPE_TOMTOM) + wcd9xxx_restore_clock(resmgr, clock_save); + } else if (resmgr->bg_audio_users == 1) { + /* currently off, just enable it */ + WARN_ON(resmgr->bandgap_type != WCD9XXX_BANDGAP_OFF); + wcd9xxx_enable_bg_audio(resmgr); + } + resmgr->bandgap_type = WCD9XXX_BANDGAP_AUDIO_MODE; + break; + case WCD9XXX_BANDGAP_MBHC_MODE: + resmgr->bg_mbhc_users++; + if (resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE || + resmgr->bandgap_type == WCD9XXX_BANDGAP_AUDIO_MODE) + /* do nothing */ + break; + + /* bg mode can be changed only with clock off */ + clock_save = wcd9xxx_save_clock(resmgr); + /* enable bg with MBHC mode */ + wcd9xxx_enable_bg_mbhc(resmgr); + /* restore clock */ + wcd9xxx_restore_clock(resmgr, clock_save); + /* save current mode */ + resmgr->bandgap_type = WCD9XXX_BANDGAP_MBHC_MODE; + break; + default: + pr_err("%s: Error, Invalid bandgap settings\n", __func__); + break; + } + + pr_debug("%s: bg users audio %d, mbhc %d\n", __func__, + resmgr->bg_audio_users, resmgr->bg_mbhc_users); +} + +/* + * wcd9xxx_resmgr_put_bandgap : Unvote bandgap ref that has been voted + * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE + */ +void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_bandgap_type choice) +{ + enum wcd9xxx_clock_type clock_save; + + pr_debug("%s: enter choice %d\n", __func__, choice); + + WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr); + switch (choice) { + case WCD9XXX_BANDGAP_AUDIO_MODE: + if (--resmgr->bg_audio_users == 0) { + if (resmgr->bg_mbhc_users) { + /* bg mode can be changed only with clock off */ + clock_save = wcd9xxx_save_clock(resmgr); + /* switch to MBHC mode */ + wcd9xxx_enable_bg_mbhc(resmgr); + /* restore clock */ + wcd9xxx_restore_clock(resmgr, clock_save); + resmgr->bandgap_type = + WCD9XXX_BANDGAP_MBHC_MODE; + } else { + /* turn off */ + wcd9xxx_disable_bg(resmgr); + resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF; + } + } + break; + case WCD9XXX_BANDGAP_MBHC_MODE: + WARN(resmgr->bandgap_type == WCD9XXX_BANDGAP_OFF, + "Unexpected bandgap type %d\n", resmgr->bandgap_type); + if (--resmgr->bg_mbhc_users == 0 && + resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE) { + wcd9xxx_disable_bg(resmgr); + resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF; + } + break; + default: + pr_err("%s: Error, Invalid bandgap settings\n", __func__); + break; + } + + pr_debug("%s: bg users audio %d, mbhc %d\n", __func__, + resmgr->bg_audio_users, resmgr->bg_mbhc_users); +} + +void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable) +{ + struct snd_soc_codec *codec = resmgr->codec; + + if (enable) { + resmgr->rx_bias_count++; + if (resmgr->rx_bias_count == 1) + snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS, + 0x80, 0x80); + } else { + resmgr->rx_bias_count--; + if (!resmgr->rx_bias_count) + snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS, + 0x80, 0x00); + } +} + +int wcd9xxx_resmgr_enable_config_mode(struct wcd9xxx_resmgr *resmgr, int enable) +{ + struct snd_soc_codec *codec = resmgr->codec; + + pr_debug("%s: enable = %d\n", __func__, enable); + if (enable) { + snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0); + /* bandgap mode to fast */ + if (resmgr->pdata->mclk_rate == WCD9XXX_MCLK_CLK_12P288MHZ) + /* Set current value to 200nA for 12.288MHz clock */ + snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x37); + else + snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17); + + usleep_range(5, 10); + snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80); + snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80); + usleep_range(10, 20); + snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0); + usleep_range(10000, 10100); + + if (resmgr->pdata->mclk_rate != WCD9XXX_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, + 0x08, 0x08); + } else { + snd_soc_update_bits(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x1, 0); + snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0); + } + + return 0; +} + +static void wcd9xxx_enable_clock_block(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_clock_config_mode config_mode) +{ + struct snd_soc_codec *codec = resmgr->codec; + unsigned long delay = WCD9XXX_RCO_CALIBRATION_DELAY_US; + int num_retry = 0; + unsigned int valr; + unsigned int valr1; + unsigned int valw[] = {0x01, 0x01, 0x10, 0x00}; + + pr_debug("%s: config_mode = %d\n", __func__, config_mode); + + /* transit to RCO requires mclk off */ + if (resmgr->codec_type != WCD9XXX_CDC_TYPE_TOMTOM) + WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2)); + + if (config_mode == WCD9XXX_CFG_RCO) { + /* Notify */ + wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_ON); + /* enable RCO and switch to it */ + wcd9xxx_resmgr_enable_config_mode(resmgr, 1); + snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02); + usleep_range(1000, 1100); + } else if (config_mode == WCD9XXX_CFG_CAL_RCO) { + snd_soc_update_bits(codec, TOMTOM_A_BIAS_OSC_BG_CTL, + 0x01, 0x01); + /* 1ms sleep required after BG enabled */ + usleep_range(1000, 1100); + + if (resmgr->pdata->mclk_rate == WCD9XXX_MCLK_CLK_12P288MHZ) { + /* + * Set RCO clock rate as 12.288MHz rate explicitly + * as the Qfuse values are incorrect for this rate + */ + snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL, + 0x50, 0x50); + } else { + snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL, + 0x18, 0x10); + valr = snd_soc_read(codec, + TOMTOM_A_QFUSE_DATA_OUT0) & (0x04); + valr1 = snd_soc_read(codec, + TOMTOM_A_QFUSE_DATA_OUT1) & (0x08); + valr = (valr >> 1) | (valr1 >> 3); + snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL, 0x60, + valw[valr] << 5); + } + snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL, 0x80, 0x80); + + do { + snd_soc_update_bits(codec, + TOMTOM_A_RCO_CALIBRATION_CTRL1, + 0x80, 0x80); + snd_soc_update_bits(codec, + TOMTOM_A_RCO_CALIBRATION_CTRL1, + 0x80, 0x00); + /* RCO calibration takes approx. 5ms */ + usleep_range(delay, delay + + WCD9XXX_USLEEP_RANGE_MARGIN_US); + if (!(snd_soc_read(codec, + TOMTOM_A_RCO_CALIBRATION_RESULT1) & 0x10)) + break; + if (num_retry >= 3) { + delay = delay + + WCD9XXX_RCO_CALIBRATION_DELAY_INC_US; + } + } while (num_retry++ < WCD9XXX_RCO_CALIBRATION_RETRY_COUNT); + } else { + /* Notify */ + wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_MCLK_ON); + /* switch to MCLK */ + + switch (resmgr->codec_type) { + case WCD9XXX_CDC_TYPE_TOMTOM: + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, + 0x08, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, + 0x40, 0x40); + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, + 0x40, 0x00); + /* clk source to ext clk and clk buff ref to VBG */ + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, + 0x0C, 0x04); + break; + default: + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, + 0x08, 0x00); + /* if RCO is enabled, switch from it */ + if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80) { + snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, + 0x02); + wcd9xxx_resmgr_enable_config_mode(resmgr, 0); + } + /* clk source to ext clk and clk buff ref to VBG */ + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, + 0x0C, 0x04); + break; + } + } + + if (config_mode != WCD9XXX_CFG_CAL_RCO) { + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, + 0x01, 0x01); + /* + * sleep required by codec hardware to + * enable clock buffer + */ + usleep_range(1000, 1200); + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, + 0x02, 0x00); + /* on MCLK */ + snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL, + 0x01, 0x01); + } + usleep_range(50, 55); + + /* Notify */ + if (config_mode == WCD9XXX_CFG_RCO) + wcd9xxx_resmgr_notifier_call(resmgr, + WCD9XXX_EVENT_POST_RCO_ON); + else if (config_mode == WCD9XXX_CFG_MCLK) + wcd9xxx_resmgr_notifier_call(resmgr, + WCD9XXX_EVENT_POST_MCLK_ON); +} + +/* + * disable clock and return previous clock state + */ +static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr *resmgr) +{ + WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr); + if (resmgr->clk_type != WCD9XXX_CLK_OFF) + wcd9xxx_disable_clock_block(resmgr); + return resmgr->clk_type != WCD9XXX_CLK_OFF; +} + +static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_clock_type type) +{ + if (type != WCD9XXX_CLK_OFF) + wcd9xxx_enable_clock_block(resmgr, type == WCD9XXX_CLK_RCO); +} + +void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_clock_type type) +{ + struct snd_soc_codec *codec = resmgr->codec; + + pr_debug("%s: current %d, requested %d, rco_users %d, mclk_users %d\n", + __func__, resmgr->clk_type, type, + resmgr->clk_rco_users, resmgr->clk_mclk_users); + WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr); + switch (type) { + case WCD9XXX_CLK_RCO: + if (++resmgr->clk_rco_users == 1 && + resmgr->clk_type == WCD9XXX_CLK_OFF) { + /* enable RCO and switch to it */ + wcd9xxx_enable_clock_block(resmgr, WCD9XXX_CFG_RCO); + resmgr->clk_type = WCD9XXX_CLK_RCO; + } else if (resmgr->clk_rco_users == 1 && + resmgr->clk_type == WCD9XXX_CLK_MCLK && + resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) { + /* + * Enable RCO but do not switch CLK MUX to RCO + * unless ext_clk_users is 1, which indicates + * EXT CLK is enabled for RCO calibration + */ + wcd9xxx_enable_clock_block(resmgr, WCD9XXX_CFG_CAL_RCO); + if (resmgr->ext_clk_users == 1) { + /* Notify */ + wcd9xxx_resmgr_notifier_call(resmgr, + WCD9XXX_EVENT_PRE_RCO_ON); + /* CLK MUX to RCO */ + if (resmgr->pdata->mclk_rate != + WCD9XXX_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, + WCD9XXX_A_CLK_BUFF_EN1, + 0x08, 0x08); + resmgr->clk_type = WCD9XXX_CLK_RCO; + wcd9xxx_resmgr_notifier_call(resmgr, + WCD9XXX_EVENT_POST_RCO_ON); + } + } + break; + case WCD9XXX_CLK_MCLK: + if (++resmgr->clk_mclk_users == 1 && + resmgr->clk_type == WCD9XXX_CLK_OFF) { + /* switch to MCLK */ + wcd9xxx_enable_clock_block(resmgr, WCD9XXX_CFG_MCLK); + resmgr->clk_type = WCD9XXX_CLK_MCLK; + } else if (resmgr->clk_mclk_users == 1 && + resmgr->clk_type == WCD9XXX_CLK_RCO) { + /* RCO to MCLK switch, with RCO still powered on */ + if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) { + wcd9xxx_resmgr_notifier_call(resmgr, + WCD9XXX_EVENT_PRE_MCLK_ON); + snd_soc_update_bits(codec, + WCD9XXX_A_BIAS_CENTRAL_BG_CTL, + 0x40, 0x00); + /* Enable clock buffer */ + snd_soc_update_bits(codec, + WCD9XXX_A_CLK_BUFF_EN1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9XXX_A_CLK_BUFF_EN1, + 0x08, 0x00); + wcd9xxx_resmgr_notifier_call(resmgr, + WCD9XXX_EVENT_POST_MCLK_ON); + } else { + /* if RCO is enabled, switch from it */ + WARN_ON(!(snd_soc_read(resmgr->codec, + WCD9XXX_A_RC_OSC_FREQ) & 0x80)); + /* disable clock block */ + wcd9xxx_disable_clock_block(resmgr); + /* switch to MCLK */ + wcd9xxx_enable_clock_block(resmgr, + WCD9XXX_CFG_MCLK); + } + resmgr->clk_type = WCD9XXX_CLK_MCLK; + } + break; + default: + pr_err("%s: Error, Invalid clock get request %d\n", __func__, + type); + break; + } + pr_debug("%s: leave\n", __func__); +} + +void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_clock_type type) +{ + struct snd_soc_codec *codec = resmgr->codec; + + pr_debug("%s: current %d, put %d\n", __func__, resmgr->clk_type, type); + + WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr); + switch (type) { + case WCD9XXX_CLK_RCO: + if (--resmgr->clk_rco_users == 0 && + resmgr->clk_type == WCD9XXX_CLK_RCO) { + wcd9xxx_disable_clock_block(resmgr); + if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) { + /* Powerdown RCO */ + snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL, + 0x80, 0x00); + snd_soc_update_bits(codec, + TOMTOM_A_BIAS_OSC_BG_CTL, + 0x01, 0x00); + } else { + /* if RCO is enabled, switch from it */ + if (snd_soc_read(resmgr->codec, + WCD9XXX_A_RC_OSC_FREQ) + & 0x80) { + snd_soc_write(resmgr->codec, + WCD9XXX_A_CLK_BUFF_EN2, + 0x02); + wcd9xxx_resmgr_enable_config_mode( + resmgr, 0); + } + } + resmgr->clk_type = WCD9XXX_CLK_OFF; + } + break; + case WCD9XXX_CLK_MCLK: + if (--resmgr->clk_mclk_users == 0 && + resmgr->clk_rco_users == 0) { + wcd9xxx_disable_clock_block(resmgr); + + if ((resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) && + (snd_soc_read(codec, TOMTOM_A_RCO_CTRL) & 0x80)) { + /* powerdown RCO*/ + snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL, + 0x80, 0x00); + snd_soc_update_bits(codec, + TOMTOM_A_BIAS_OSC_BG_CTL, + 0x01, 0x00); + } + resmgr->clk_type = WCD9XXX_CLK_OFF; + } else if (resmgr->clk_mclk_users == 0 && + resmgr->clk_rco_users) { + if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) { + if (!(snd_soc_read(codec, TOMTOM_A_RCO_CTRL) & + 0x80)) { + dev_dbg(codec->dev, "%s: Enabling RCO\n", + __func__); + wcd9xxx_enable_clock_block(resmgr, + WCD9XXX_CFG_CAL_RCO); + snd_soc_update_bits(codec, + WCD9XXX_A_CLK_BUFF_EN1, + 0x01, 0x00); + } else { + wcd9xxx_resmgr_notifier_call(resmgr, + WCD9XXX_EVENT_PRE_MCLK_OFF); + snd_soc_update_bits(codec, + WCD9XXX_A_CLK_BUFF_EN1, + 0x08, 0x08); + snd_soc_update_bits(codec, + WCD9XXX_A_CLK_BUFF_EN1, + 0x01, 0x00); + wcd9xxx_resmgr_notifier_call(resmgr, + WCD9XXX_EVENT_POST_MCLK_OFF); + /* CLK Mux changed to RCO, notify that + * RCO is ON + */ + wcd9xxx_resmgr_notifier_call(resmgr, + WCD9XXX_EVENT_POST_RCO_ON); + } + } else { + /* disable clock */ + wcd9xxx_disable_clock_block(resmgr); + /* switch to RCO */ + wcd9xxx_enable_clock_block(resmgr, + WCD9XXX_CFG_RCO); + } + resmgr->clk_type = WCD9XXX_CLK_RCO; + } + break; + default: + pr_err("%s: Error, Invalid clock get request %d\n", __func__, + type); + break; + } + WARN_ON(resmgr->clk_rco_users < 0); + WARN_ON(resmgr->clk_mclk_users < 0); + + pr_debug("%s: new rco_users %d, mclk_users %d\n", __func__, + resmgr->clk_rco_users, resmgr->clk_mclk_users); +} + +/* + * wcd9xxx_resmgr_get_clk_type() + * Returns clk type that is currently enabled + */ +int wcd9xxx_resmgr_get_clk_type(struct wcd9xxx_resmgr *resmgr) +{ + return resmgr->clk_type; +} + +static void wcd9xxx_resmgr_update_cfilt_usage(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_cfilt_sel cfilt_sel, + bool inc) +{ + u16 micb_cfilt_reg; + enum wcd9xxx_notify_event e_pre_on, e_post_off; + struct snd_soc_codec *codec = resmgr->codec; + + switch (cfilt_sel) { + case WCD9XXX_CFILT1_SEL: + micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_1_CTL; + e_pre_on = WCD9XXX_EVENT_PRE_CFILT_1_ON; + e_post_off = WCD9XXX_EVENT_POST_CFILT_1_OFF; + break; + case WCD9XXX_CFILT2_SEL: + micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_2_CTL; + e_pre_on = WCD9XXX_EVENT_PRE_CFILT_2_ON; + e_post_off = WCD9XXX_EVENT_POST_CFILT_2_OFF; + break; + case WCD9XXX_CFILT3_SEL: + micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_3_CTL; + e_pre_on = WCD9XXX_EVENT_PRE_CFILT_3_ON; + e_post_off = WCD9XXX_EVENT_POST_CFILT_3_OFF; + break; + default: + WARN(1, "Invalid CFILT selection %d\n", cfilt_sel); + return; /* should not happen */ + } + + if (inc) { + if ((resmgr->cfilt_users[cfilt_sel]++) == 0) { + /* Notify */ + wcd9xxx_resmgr_notifier_call(resmgr, e_pre_on); + /* Enable CFILT */ + snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80); + } + } else { + /* + * Check if count not zero, decrease + * then check if zero, go ahead disable cfilter + */ + WARN(resmgr->cfilt_users[cfilt_sel] == 0, + "Invalid CFILT use count 0\n"); + if ((--resmgr->cfilt_users[cfilt_sel]) == 0) { + /* Disable CFILT */ + snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0); + /* Notify MBHC so MBHC can switch CFILT to fast mode */ + wcd9xxx_resmgr_notifier_call(resmgr, e_post_off); + } + } +} + +void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_cfilt_sel cfilt_sel) +{ + return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, true); +} + +void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_cfilt_sel cfilt_sel) +{ + return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, false); +} + +int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr, + unsigned int cfilt_mv) +{ + int rc = -EINVAL; + unsigned int ldoh_v = resmgr->micbias_pdata->ldoh_v; + unsigned int min_mv, max_mv; + + switch (ldoh_v) { + case WCD9XXX_LDOH_1P95_V: + min_mv = 160; + max_mv = 1800; + break; + case WCD9XXX_LDOH_2P35_V: + min_mv = 200; + max_mv = 2200; + break; + case WCD9XXX_LDOH_2P75_V: + min_mv = 240; + max_mv = 2600; + break; + case WCD9XXX_LDOH_3P0_V: + min_mv = 260; + max_mv = 2875; + break; + default: + goto done; + } + + if (cfilt_mv < min_mv || cfilt_mv > max_mv) + goto done; + + for (rc = 4; rc <= 44; rc++) { + min_mv = max_mv * (rc) / 44; + if (min_mv >= cfilt_mv) { + rc -= 4; + break; + } + } +done: + return rc; +} + +static void wcd9xxx_resmgr_cond_trigger_cond(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_resmgr_cond cond) +{ + struct list_head *l; + struct wcd9xxx_resmgr_cond_entry *e; + bool set; + + pr_debug("%s: enter\n", __func__); + /* update bit if cond isn't available or cond is set */ + set = !test_bit(cond, &resmgr->cond_avail_flags) || + !!test_bit(cond, &resmgr->cond_flags); + list_for_each(l, &resmgr->update_bit_cond_h) { + e = list_entry(l, struct wcd9xxx_resmgr_cond_entry, list); + if (e->cond == cond) + snd_soc_update_bits(resmgr->codec, e->reg, + 1 << e->shift, + (set ? !e->invert : e->invert) + << e->shift); + } + pr_debug("%s: leave\n", __func__); +} + +/* + * wcd9xxx_regmgr_cond_register : notify resmgr conditions in the condbits are + * available and notified. + * condbits : contains bitmask of enum wcd9xxx_resmgr_cond + */ +void wcd9xxx_regmgr_cond_register(struct wcd9xxx_resmgr *resmgr, + unsigned long condbits) +{ + unsigned int cond; + + for_each_set_bit(cond, &condbits, BITS_PER_BYTE * sizeof(condbits)) { + mutex_lock(&resmgr->update_bit_cond_lock); + WARN(test_bit(cond, &resmgr->cond_avail_flags), + "Condition 0x%0x is already registered\n", cond); + set_bit(cond, &resmgr->cond_avail_flags); + wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond); + mutex_unlock(&resmgr->update_bit_cond_lock); + pr_debug("%s: Condition 0x%x is registered\n", __func__, cond); + } +} + +void wcd9xxx_regmgr_cond_deregister(struct wcd9xxx_resmgr *resmgr, + unsigned long condbits) +{ + unsigned int cond; + + for_each_set_bit(cond, &condbits, BITS_PER_BYTE * sizeof(condbits)) { + mutex_lock(&resmgr->update_bit_cond_lock); + WARN(!test_bit(cond, &resmgr->cond_avail_flags), + "Condition 0x%0x isn't registered\n", cond); + clear_bit(cond, &resmgr->cond_avail_flags); + wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond); + mutex_unlock(&resmgr->update_bit_cond_lock); + pr_debug("%s: Condition 0x%x is deregistered\n", __func__, + cond); + } +} + +void wcd9xxx_resmgr_cond_update_cond(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_resmgr_cond cond, bool set) +{ + mutex_lock(&resmgr->update_bit_cond_lock); + if ((set && !test_and_set_bit(cond, &resmgr->cond_flags)) || + (!set && test_and_clear_bit(cond, &resmgr->cond_flags))) { + pr_debug("%s: Resource %d condition changed to %s\n", __func__, + cond, set ? "set" : "clear"); + wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond); + } + mutex_unlock(&resmgr->update_bit_cond_lock); +} + +int wcd9xxx_resmgr_add_cond_update_bits(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_resmgr_cond cond, + unsigned short reg, int shift, + bool invert) +{ + struct wcd9xxx_resmgr_cond_entry *entry; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->cond = cond; + entry->reg = reg; + entry->shift = shift; + entry->invert = invert; + + mutex_lock(&resmgr->update_bit_cond_lock); + list_add_tail(&entry->list, &resmgr->update_bit_cond_h); + + wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond); + mutex_unlock(&resmgr->update_bit_cond_lock); + + return 0; +} + +/* + * wcd9xxx_resmgr_rm_cond_update_bits : + * Clear bit and remove from the conditional bit update list + */ +int wcd9xxx_resmgr_rm_cond_update_bits(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_resmgr_cond cond, + unsigned short reg, int shift, + bool invert) +{ + struct list_head *l, *next; + struct wcd9xxx_resmgr_cond_entry *e = NULL; + + pr_debug("%s: enter\n", __func__); + mutex_lock(&resmgr->update_bit_cond_lock); + list_for_each_safe(l, next, &resmgr->update_bit_cond_h) { + e = list_entry(l, struct wcd9xxx_resmgr_cond_entry, list); + if (e->reg == reg && e->shift == shift && e->invert == invert) { + snd_soc_update_bits(resmgr->codec, e->reg, + 1 << e->shift, + e->invert << e->shift); + list_del(&e->list); + mutex_unlock(&resmgr->update_bit_cond_lock); + kfree(e); + return 0; + } + } + mutex_unlock(&resmgr->update_bit_cond_lock); + pr_err("%s: Cannot find update bit entry reg 0x%x, shift %d\n", + __func__, e ? e->reg : 0, e ? e->shift : 0); + + return -EINVAL; +} + +int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr, + struct notifier_block *nblock) +{ + return blocking_notifier_chain_register(&resmgr->notifier, nblock); +} + +int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr, + struct notifier_block *nblock) +{ + return blocking_notifier_chain_unregister(&resmgr->notifier, nblock); +} + +int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr, + struct snd_soc_codec *codec, + struct wcd9xxx_core_resource *core_res, + struct wcd9xxx_pdata *pdata, + struct wcd9xxx_micbias_setting *micbias_pdata, + struct wcd9xxx_reg_address *reg_addr, + const struct wcd9xxx_resmgr_cb *resmgr_cb, + enum wcd9xxx_cdc_type cdc_type) +{ + WARN(ARRAY_SIZE(wcd9xxx_event_string) != WCD9XXX_EVENT_LAST + 1, + "Event string table isn't up to date!, %zd != %d\n", + ARRAY_SIZE(wcd9xxx_event_string), WCD9XXX_EVENT_LAST + 1); + + resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF; + resmgr->codec = codec; + resmgr->codec_type = cdc_type; + /* This gives access of core handle to lock/unlock suspend */ + resmgr->core_res = core_res; + resmgr->pdata = pdata; + resmgr->micbias_pdata = micbias_pdata; + resmgr->reg_addr = reg_addr; + resmgr->resmgr_cb = resmgr_cb; + + INIT_LIST_HEAD(&resmgr->update_bit_cond_h); + + BLOCKING_INIT_NOTIFIER_HEAD(&resmgr->notifier); + + mutex_init(&resmgr->codec_resource_lock); + mutex_init(&resmgr->codec_bg_clk_lock); + mutex_init(&resmgr->update_bit_cond_lock); + + return 0; +} + +void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr) +{ + mutex_destroy(&resmgr->update_bit_cond_lock); + mutex_destroy(&resmgr->codec_bg_clk_lock); + mutex_destroy(&resmgr->codec_resource_lock); +} + +void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr) +{ + mutex_lock(&resmgr->codec_resource_lock); +} + +void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr) +{ + mutex_unlock(&resmgr->codec_resource_lock); +} + +MODULE_DESCRIPTION("wcd9xxx resmgr module"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h new file mode 100644 index 0000000000000000000000000000000000000000..e35d6161d488c1e8b1bf175b0ae135b3f1e5d7f7 --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-resmgr.h @@ -0,0 +1,280 @@ +/* Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __WCD9XXX_COMMON_H__ +#define __WCD9XXX_COMMON_H__ + +#include +#include +#include + +enum wcd9xxx_bandgap_type { + WCD9XXX_BANDGAP_OFF, + WCD9XXX_BANDGAP_AUDIO_MODE, + WCD9XXX_BANDGAP_MBHC_MODE, +}; + +enum wcd9xxx_cdc_type { + WCD9XXX_CDC_TYPE_INVALID = 0, + WCD9XXX_CDC_TYPE_TAIKO, + WCD9XXX_CDC_TYPE_TAPAN, + WCD9XXX_CDC_TYPE_HELICON, + WCD9XXX_CDC_TYPE_TOMTOM, +}; + +enum wcd9xxx_clock_type { + WCD9XXX_CLK_OFF, + WCD9XXX_CLK_RCO, + WCD9XXX_CLK_MCLK, +}; + +enum wcd9xxx_clock_config_mode { + WCD9XXX_CFG_MCLK = 0, + WCD9XXX_CFG_RCO, + WCD9XXX_CFG_CAL_RCO, +}; + +enum wcd9xxx_cfilt_sel { + WCD9XXX_CFILT1_SEL, + WCD9XXX_CFILT2_SEL, + WCD9XXX_CFILT3_SEL, + WCD9XXX_NUM_OF_CFILT, +}; + +struct wcd9xxx_reg_address { + u16 micb_4_ctl; + u16 micb_4_int_rbias; + u16 micb_4_mbhc; +}; + +enum wcd9xxx_notify_event { + WCD9XXX_EVENT_INVALID, + + WCD9XXX_EVENT_PRE_RCO_ON, + WCD9XXX_EVENT_POST_RCO_ON, + WCD9XXX_EVENT_PRE_RCO_OFF, + WCD9XXX_EVENT_POST_RCO_OFF, + + WCD9XXX_EVENT_PRE_MCLK_ON, + WCD9XXX_EVENT_POST_MCLK_ON, + WCD9XXX_EVENT_PRE_MCLK_OFF, + WCD9XXX_EVENT_POST_MCLK_OFF, + + WCD9XXX_EVENT_PRE_BG_OFF, + WCD9XXX_EVENT_POST_BG_OFF, + WCD9XXX_EVENT_PRE_BG_AUDIO_ON, + WCD9XXX_EVENT_POST_BG_AUDIO_ON, + WCD9XXX_EVENT_PRE_BG_MBHC_ON, + WCD9XXX_EVENT_POST_BG_MBHC_ON, + + WCD9XXX_EVENT_PRE_MICBIAS_1_OFF, + WCD9XXX_EVENT_POST_MICBIAS_1_OFF, + WCD9XXX_EVENT_PRE_MICBIAS_2_OFF, + WCD9XXX_EVENT_POST_MICBIAS_2_OFF, + WCD9XXX_EVENT_PRE_MICBIAS_3_OFF, + WCD9XXX_EVENT_POST_MICBIAS_3_OFF, + WCD9XXX_EVENT_PRE_MICBIAS_4_OFF, + WCD9XXX_EVENT_POST_MICBIAS_4_OFF, + WCD9XXX_EVENT_PRE_MICBIAS_1_ON, + WCD9XXX_EVENT_POST_MICBIAS_1_ON, + WCD9XXX_EVENT_PRE_MICBIAS_2_ON, + WCD9XXX_EVENT_POST_MICBIAS_2_ON, + WCD9XXX_EVENT_PRE_MICBIAS_3_ON, + WCD9XXX_EVENT_POST_MICBIAS_3_ON, + WCD9XXX_EVENT_PRE_MICBIAS_4_ON, + WCD9XXX_EVENT_POST_MICBIAS_4_ON, + + WCD9XXX_EVENT_PRE_CFILT_1_OFF, + WCD9XXX_EVENT_POST_CFILT_1_OFF, + WCD9XXX_EVENT_PRE_CFILT_2_OFF, + WCD9XXX_EVENT_POST_CFILT_2_OFF, + WCD9XXX_EVENT_PRE_CFILT_3_OFF, + WCD9XXX_EVENT_POST_CFILT_3_OFF, + WCD9XXX_EVENT_PRE_CFILT_1_ON, + WCD9XXX_EVENT_POST_CFILT_1_ON, + WCD9XXX_EVENT_PRE_CFILT_2_ON, + WCD9XXX_EVENT_POST_CFILT_2_ON, + WCD9XXX_EVENT_PRE_CFILT_3_ON, + WCD9XXX_EVENT_POST_CFILT_3_ON, + + WCD9XXX_EVENT_PRE_HPHL_PA_ON, + WCD9XXX_EVENT_POST_HPHL_PA_OFF, + WCD9XXX_EVENT_PRE_HPHR_PA_ON, + WCD9XXX_EVENT_POST_HPHR_PA_OFF, + + WCD9XXX_EVENT_POST_RESUME, + + WCD9XXX_EVENT_PRE_TX_3_ON, + WCD9XXX_EVENT_POST_TX_3_OFF, + + WCD9XXX_EVENT_LAST, +}; + +struct wcd9xxx_resmgr_cb { + int (*cdc_rco_ctrl)(struct snd_soc_codec *, bool); +}; + +struct wcd9xxx_resmgr { + struct snd_soc_codec *codec; + struct wcd9xxx_core_resource *core_res; + + u32 rx_bias_count; + + /* + * bandgap_type, bg_audio_users and bg_mbhc_users have to be + * referred/manipulated after acquiring codec_bg_clk_lock mutex + */ + enum wcd9xxx_bandgap_type bandgap_type; + u16 bg_audio_users; + u16 bg_mbhc_users; + + /* + * clk_type, clk_rco_users and clk_mclk_users have to be + * referred/manipulated after acquiring codec_bg_clk_lock mutex + */ + enum wcd9xxx_clock_type clk_type; + u16 clk_rco_users; + u16 clk_mclk_users; + u16 ext_clk_users; + + /* cfilt users per cfilts */ + u16 cfilt_users[WCD9XXX_NUM_OF_CFILT]; + + struct wcd9xxx_reg_address *reg_addr; + + struct wcd9xxx_pdata *pdata; + + struct wcd9xxx_micbias_setting *micbias_pdata; + + struct blocking_notifier_head notifier; + /* Notifier needs mbhc pointer with resmgr */ + struct wcd9xxx_mbhc *mbhc; + + unsigned long cond_flags; + unsigned long cond_avail_flags; + struct list_head update_bit_cond_h; + struct mutex update_bit_cond_lock; + + /* + * Currently, only used for mbhc purpose, to protect + * concurrent execution of mbhc threaded irq handlers and + * kill race between DAPM and MBHC. But can serve as a + * general lock to protect codec resource + */ + struct mutex codec_resource_lock; + struct mutex codec_bg_clk_lock; + + enum wcd9xxx_cdc_type codec_type; + + const struct wcd9xxx_resmgr_cb *resmgr_cb; +}; + +int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr, + struct snd_soc_codec *codec, + struct wcd9xxx_core_resource *core_res, + struct wcd9xxx_pdata *pdata, + struct wcd9xxx_micbias_setting *micbias_pdata, + struct wcd9xxx_reg_address *reg_addr, + const struct wcd9xxx_resmgr_cb *resmgr_cb, + enum wcd9xxx_cdc_type cdc_type); +void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr); + +int wcd9xxx_resmgr_enable_config_mode(struct wcd9xxx_resmgr *resmgr, + int enable); + +void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable); +void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_clock_type type); +void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_clock_type type); +void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr, + const enum wcd9xxx_bandgap_type choice); +void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_bandgap_type choice); +void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_cfilt_sel cfilt_sel); +void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_cfilt_sel cfilt_sel); +int wcd9xxx_resmgr_get_clk_type(struct wcd9xxx_resmgr *resmgr); + +void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr); +void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr); +#define WCD9XXX_BCL_LOCK(resmgr) \ +{ \ + pr_debug("%s: Acquiring BCL\n", __func__); \ + wcd9xxx_resmgr_bcl_lock(resmgr); \ + pr_debug("%s: Acquiring BCL done\n", __func__); \ +} + +void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr); +#define WCD9XXX_BCL_UNLOCK(resmgr) \ +{ \ + pr_debug("%s: Release BCL\n", __func__); \ + wcd9xxx_resmgr_bcl_unlock(resmgr); \ +} + +#define WCD9XXX_BCL_ASSERT_LOCKED(resmgr) \ +{ \ + WARN_ONCE(!mutex_is_locked(&resmgr->codec_resource_lock), \ + "%s: BCL should have acquired\n", __func__); \ +} + +#define WCD9XXX_BG_CLK_LOCK(resmgr) \ +{ \ + struct wcd9xxx_resmgr *__resmgr = resmgr; \ + pr_debug("%s: Acquiring BG_CLK\n", __func__); \ + mutex_lock(&__resmgr->codec_bg_clk_lock); \ + pr_debug("%s: Acquiring BG_CLK done\n", __func__); \ +} + +#define WCD9XXX_BG_CLK_UNLOCK(resmgr) \ +{ \ + struct wcd9xxx_resmgr *__resmgr = resmgr; \ + pr_debug("%s: Releasing BG_CLK\n", __func__); \ + mutex_unlock(&__resmgr->codec_bg_clk_lock); \ +} + +#define WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr) \ +{ \ + WARN_ONCE(!mutex_is_locked(&resmgr->codec_bg_clk_lock), \ + "%s: BG_CLK lock should have acquired\n", __func__); \ +} + +const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type); +int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr, + unsigned int cfilt_mv); +int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr, + struct notifier_block *nblock); +int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr, + struct notifier_block *nblock); +void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr, + const enum wcd9xxx_notify_event e); + +enum wcd9xxx_resmgr_cond { + WCD9XXX_COND_HPH = 0x01, /* Headphone */ + WCD9XXX_COND_HPH_MIC = 0x02, /* Microphone on the headset */ +}; +void wcd9xxx_regmgr_cond_register(struct wcd9xxx_resmgr *resmgr, + unsigned long condbits); +void wcd9xxx_regmgr_cond_deregister(struct wcd9xxx_resmgr *resmgr, + unsigned long condbits); +int wcd9xxx_resmgr_rm_cond_update_bits(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_resmgr_cond cond, + unsigned short reg, int shift, + bool invert); +int wcd9xxx_resmgr_add_cond_update_bits(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_resmgr_cond cond, + unsigned short reg, int shift, + bool invert); +void wcd9xxx_resmgr_cond_update_cond(struct wcd9xxx_resmgr *resmgr, + enum wcd9xxx_resmgr_cond cond, bool set); + +#endif /* __WCD9XXX_COMMON_H__ */ diff --git a/sound/soc/codecs/wcd_cmi_api.h b/sound/soc/codecs/wcd_cmi_api.h new file mode 100644 index 0000000000000000000000000000000000000000..39be6417e3270b9219bfeb94f54376f4f45790d1 --- /dev/null +++ b/sound/soc/codecs/wcd_cmi_api.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __CMI_API__ +#define __CMI_API__ + +enum cmi_api_result { + CMI_API_FAILED = 1, + CMI_API_BUSY, + CMI_API_NO_MEMORY, + CMI_API_NOT_READY, +}; + +enum cmi_api_event { + CMI_API_MSG = 1, + CMI_API_OFFLINE, + CMI_API_ONLINE, + CMI_API_DEINITIALIZED, +}; + +struct cmi_api_notification { + enum cmi_api_event event; + enum cmi_api_result result; + void *message; +}; + +void *cmi_register( + void notification_callback + (const struct cmi_api_notification *parameter), + u32 service); +enum cmi_api_result cmi_deregister(void *reg_handle); +enum cmi_api_result cmi_send_msg(void *message); + +#endif /*__CMI_API__*/ diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c new file mode 100644 index 0000000000000000000000000000000000000000..7fc6d2e86e8645b0506b60223a4552051cae06c7 --- /dev/null +++ b/sound/soc/codecs/wcd_cpe_core.c @@ -0,0 +1,4600 @@ +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd_cpe_core.h" +#include "wcd_cpe_services.h" +#include "wcd_cmi_api.h" + +#define CMI_CMD_TIMEOUT (10 * HZ) +#define WCD_CPE_LSM_MAX_SESSIONS 2 +#define WCD_CPE_AFE_MAX_PORTS 4 +#define AFE_SVC_EXPLICIT_PORT_START 1 +#define WCD_CPE_EC_PP_BUF_SIZE 480 /* 5 msec buffer */ + +#define ELF_FLAG_EXECUTE (1 << 0) +#define ELF_FLAG_WRITE (1 << 1) +#define ELF_FLAG_READ (1 << 2) + +#define ELF_FLAG_RW (ELF_FLAG_READ | ELF_FLAG_WRITE) + +#define WCD_CPE_GRAB_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock acquire\n", \ + __func__, name); \ + mutex_lock(lock); \ +} + +#define WCD_CPE_REL_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock release\n", \ + __func__, name); \ + mutex_unlock(lock); \ +} + +#define WCD_CPE_STATE_MAX_LEN 11 +#define CPE_OFFLINE_WAIT_TIMEOUT (2 * HZ) +#define CPE_READY_WAIT_TIMEOUT (3 * HZ) +#define WCD_CPE_SYSFS_DIR_MAX_LENGTH 32 + +#define CPE_ERR_IRQ_CB(core) \ + (core->cpe_cdc_cb->cpe_err_irq_control) + +/* + * AFE output buffer size is always + * (sample_rate * number of bytes per sample/2*1000) + */ +#define AFE_OUT_BUF_SIZE(bit_width, sample_rate) \ + (((sample_rate) * (bit_width / BITS_PER_BYTE))/(2*1000)) + +enum afe_port_state { + AFE_PORT_STATE_DEINIT = 0, + AFE_PORT_STATE_INIT, + AFE_PORT_STATE_CONFIG, + AFE_PORT_STATE_STARTED, + AFE_PORT_STATE_SUSPENDED, +}; + +struct wcd_cmi_afe_port_data { + u8 port_id; + struct mutex afe_lock; + struct completion afe_cmd_complete; + enum afe_port_state port_state; + u8 cmd_result; + u32 mem_handle; +}; + +struct cpe_lsm_ids { + u32 module_id; + u32 param_id; +}; + +static struct wcd_cpe_core *core_d; +static struct cpe_lsm_session + *lsm_sessions[WCD_CPE_LSM_MAX_SESSIONS + 1]; +struct wcd_cpe_core * (*wcd_get_cpe_core)(struct snd_soc_codec *); +static struct wcd_cmi_afe_port_data afe_ports[WCD_CPE_AFE_MAX_PORTS + 1]; +static void wcd_cpe_svc_event_cb(const struct cpe_svc_notification *param); +static int wcd_cpe_setup_irqs(struct wcd_cpe_core *core); +static void wcd_cpe_cleanup_irqs(struct wcd_cpe_core *core); +static ssize_t cpe_ftm_test_trigger(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos); +static u32 ramdump_enable; +static u32 cpe_ftm_test_status; +static const struct file_operations cpe_ftm_test_trigger_fops = { + .open = simple_open, + .write = cpe_ftm_test_trigger, +}; + +static int wcd_cpe_afe_svc_cmd_mode(void *core_handle, + u8 mode); +struct wcd_cpe_attribute { + struct attribute attr; + ssize_t (*show)(struct wcd_cpe_core *core, char *buf); + ssize_t (*store)(struct wcd_cpe_core *core, const char *buf, + ssize_t count); +}; + +#define WCD_CPE_ATTR(_name, _mode, _show, _store) \ +static struct wcd_cpe_attribute cpe_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ + .show = _show, \ + .store = _store, \ +} + +#define to_wcd_cpe_attr(a) \ + container_of((a), struct wcd_cpe_attribute, attr) + +#define kobj_to_cpe_core(kobj) \ + container_of((kobj), struct wcd_cpe_core, cpe_kobj) + +/* wcd_cpe_lsm_session_active: check if any session is active + * return true if any session is active. + */ +static bool wcd_cpe_lsm_session_active(void) +{ + int index = 1; + bool lsm_active = false; + + /* session starts from index 1 */ + for (; index <= WCD_CPE_LSM_MAX_SESSIONS; index++) { + if (lsm_sessions[index] != NULL) { + lsm_active = true; + break; + } else { + lsm_active = false; + } + } + return lsm_active; +} + +static int wcd_cpe_get_sfr_dump(struct wcd_cpe_core *core) +{ + struct cpe_svc_mem_segment dump_seg; + int rc; + u8 *sfr_dump; + + sfr_dump = kzalloc(core->sfr_buf_size, GFP_KERNEL); + if (!sfr_dump) + goto done; + + dump_seg.type = CPE_SVC_DATA_MEM; + dump_seg.cpe_addr = core->sfr_buf_addr; + dump_seg.size = core->sfr_buf_size; + dump_seg.data = sfr_dump; + dev_dbg(core->dev, + "%s: reading SFR from CPE, size = %zu\n", + __func__, core->sfr_buf_size); + + rc = cpe_svc_ramdump(core->cpe_handle, &dump_seg); + if (IS_ERR_VALUE(rc)) { + dev_err(core->dev, + "%s: Failed to read cpe sfr_dump, err = %d\n", + __func__, rc); + goto free_sfr_dump; + } + + dev_info(core->dev, + "%s: cpe_sfr = %s\n", __func__, sfr_dump); + +free_sfr_dump: + kfree(sfr_dump); +done: + /* Even if SFR dump failed, do not return error */ + return 0; +} + +static int wcd_cpe_collect_ramdump(struct wcd_cpe_core *core) +{ + struct cpe_svc_mem_segment dump_seg; + int rc; + + if (!core->cpe_ramdump_dev || !core->cpe_dump_v_addr || + core->hw_info.dram_size == 0) { + dev_err(core->dev, + "%s: Ramdump devices not set up, size = %zu\n", + __func__, core->hw_info.dram_size); + return -EINVAL; + } + + dump_seg.type = CPE_SVC_DATA_MEM; + dump_seg.cpe_addr = core->hw_info.dram_offset; + dump_seg.size = core->hw_info.dram_size; + dump_seg.data = core->cpe_dump_v_addr; + + dev_dbg(core->dev, + "%s: Reading ramdump from CPE\n", + __func__); + + rc = cpe_svc_ramdump(core->cpe_handle, &dump_seg); + if (IS_ERR_VALUE(rc)) { + dev_err(core->dev, + "%s: Failed to read CPE ramdump, err = %d\n", + __func__, rc); + return rc; + } + + dev_dbg(core->dev, + "%s: completed reading ramdump from CPE\n", + __func__); + + core->cpe_ramdump_seg.address = (unsigned long) core->cpe_dump_addr; + core->cpe_ramdump_seg.size = core->hw_info.dram_size; + core->cpe_ramdump_seg.v_address = core->cpe_dump_v_addr; + + rc = do_ramdump(core->cpe_ramdump_dev, + &core->cpe_ramdump_seg, 1); + if (rc) + dev_err(core->dev, + "%s: fail to dump cpe ram to device, err = %d\n", + __func__, rc); + return rc; +} + +/* wcd_cpe_is_valid_elf_hdr: check if the ELF header is valid + * @core: handle to wcd_cpe_core + * @fw_size: size of firmware from request_firmware + * @ehdr: the elf header to be checked for + * return true if all checks pass, true if any elf check fails + */ +static bool wcd_cpe_is_valid_elf_hdr(struct wcd_cpe_core *core, size_t fw_size, + const struct elf32_hdr *ehdr) +{ + if (fw_size < sizeof(*ehdr)) { + dev_err(core->dev, "%s:Firmware too small\n", __func__); + goto elf_check_fail; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { + dev_err(core->dev, "%s: Not an ELF file\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { + dev_err(core->dev, "%s: Not a executable image\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_phnum == 0) { + dev_err(core->dev, "%s: no segments to load\n", __func__); + goto elf_check_fail; + } + + if (sizeof(struct elf32_phdr) * ehdr->e_phnum + + sizeof(struct elf32_hdr) > fw_size) { + dev_err(core->dev, "%s: Too small MDT file\n", __func__); + goto elf_check_fail; + } + + return true; + +elf_check_fail: + return false; +} + +/* + * wcd_cpe_load_each_segment: download segment to CPE + * @core: handle to struct wcd_cpe_core + * @file_idx: index of split firmware image file name + * @phdr: program header from metadata + */ +static int wcd_cpe_load_each_segment(struct wcd_cpe_core *core, + int file_idx, const struct elf32_phdr *phdr) +{ + const struct firmware *split_fw; + char split_fname[32]; + int ret = 0; + struct cpe_svc_mem_segment *segment; + + if (!core || !phdr) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + + /* file size can be 0 for bss segments */ + if (phdr->p_filesz == 0 || phdr->p_memsz == 0) + return 0; + + segment = kzalloc(sizeof(struct cpe_svc_mem_segment), GFP_KERNEL); + if (!segment) + return -ENOMEM; + + snprintf(split_fname, sizeof(split_fname), "%s.b%02d", + core->fname, file_idx); + + ret = request_firmware(&split_fw, split_fname, core->dev); + if (ret) { + dev_err(core->dev, "firmware %s not found\n", + split_fname); + ret = -EIO; + goto fw_req_fail; + } + + if (phdr->p_flags & ELF_FLAG_EXECUTE) + segment->type = CPE_SVC_INSTRUCTION_MEM; + else if (phdr->p_flags & ELF_FLAG_RW) + segment->type = CPE_SVC_DATA_MEM; + else { + dev_err(core->dev, "%s invalid flags 0x%x\n", + __func__, phdr->p_flags); + goto done; + } + + segment->cpe_addr = phdr->p_paddr; + segment->size = phdr->p_filesz; + segment->data = (u8 *) split_fw->data; + + dev_dbg(core->dev, + "%s: cpe segment type %s read from firmware\n", __func__, + (segment->type == CPE_SVC_INSTRUCTION_MEM) ? + "INSTRUCTION" : "DATA"); + + ret = cpe_svc_download_segment(core->cpe_handle, segment); + if (ret) { + dev_err(core->dev, + "%s: Failed to download %s, error = %d\n", + __func__, split_fname, ret); + goto done; + } + +done: + release_firmware(split_fw); + +fw_req_fail: + kfree(segment); + return ret; +} + +/* + * wcd_cpe_enable_cpe_clks: enable the clocks for CPE + * @core: handle to wcd_cpe_core + * @enable: flag indicating whether to enable/disable cpe clocks + */ +static int wcd_cpe_enable_cpe_clks(struct wcd_cpe_core *core, bool enable) +{ + int ret, ret1; + + if (!core || !core->cpe_cdc_cb || + !core->cpe_cdc_cb->cpe_clk_en) { + pr_err("%s: invalid handle\n", + __func__); + return -EINVAL; + } + + ret = core->cpe_cdc_cb->cdc_clk_en(core->codec, enable); + if (ret) { + dev_err(core->dev, "%s: Failed to enable RCO\n", + __func__); + return ret; + } + + if (!enable && core->cpe_clk_ref > 0) + core->cpe_clk_ref--; + + /* + * CPE clk will be enabled at the first time + * and be disabled at the last time. + */ + if (core->cpe_clk_ref == 0) { + ret = core->cpe_cdc_cb->cpe_clk_en(core->codec, enable); + if (ret) { + dev_err(core->dev, + "%s: cpe_clk_en() failed, err = %d\n", + __func__, ret); + goto cpe_clk_fail; + } + } + + if (enable) + core->cpe_clk_ref++; + + return 0; + +cpe_clk_fail: + /* Release the codec clk if CPE clk enable failed */ + if (enable) { + ret1 = core->cpe_cdc_cb->cdc_clk_en(core->codec, !enable); + if (ret1) + dev_err(core->dev, + "%s: Fail to release codec clk, err = %d\n", + __func__, ret1); + } + + return ret; +} + +/* + * wcd_cpe_bus_vote_max_bw: Function to vote for max bandwidth on codec bus + * @core: handle to core for cpe + * @vote: flag to indicate enable/disable of vote + * + * This function will try to use the codec provided callback to + * vote/unvote for the max bandwidth of the bus that is used by + * the codec for register reads/writes. + */ +static int wcd_cpe_bus_vote_max_bw(struct wcd_cpe_core *core, + bool vote) +{ + if (!core || !core->cpe_cdc_cb) { + pr_err("%s: Invalid handle to %s\n", + __func__, + (!core) ? "core" : "codec callbacks"); + return -EINVAL; + } + + if (core->cpe_cdc_cb->bus_vote_bw) { + dev_dbg(core->dev, "%s: %s cdc bus max bandwidth\n", + __func__, vote ? "Vote" : "Unvote"); + core->cpe_cdc_cb->bus_vote_bw(core->codec, vote); + } + + return 0; +} + +/* + * wcd_cpe_load_fw: Function to load the fw image + * @core: cpe core pointer + * @load_type: indicates whether to load to data section + * or the instruction section + * + * Parse the mdt file to look for program headers, load each + * split file corresponding to the program headers. + */ +static int wcd_cpe_load_fw(struct wcd_cpe_core *core, + unsigned int load_type) +{ + + int ret, phdr_idx; + struct snd_soc_codec *codec = NULL; + struct wcd9xxx *wcd9xxx = NULL; + const struct elf32_hdr *ehdr; + const struct elf32_phdr *phdr; + const struct firmware *fw; + const u8 *elf_ptr; + char mdt_name[64]; + bool img_dload_fail = false; + bool load_segment; + + if (!core || !core->cpe_handle) { + pr_err("%s: Error CPE core %pK\n", __func__, + core); + return -EINVAL; + } + codec = core->codec; + wcd9xxx = dev_get_drvdata(codec->dev->parent); + snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", core->fname); + ret = request_firmware(&fw, mdt_name, core->dev); + if (IS_ERR_VALUE(ret)) { + dev_err(core->dev, "firmware %s not found\n", mdt_name); + return ret; + } + + ehdr = (struct elf32_hdr *) fw->data; + if (!wcd_cpe_is_valid_elf_hdr(core, fw->size, ehdr)) { + dev_err(core->dev, "%s: fw mdt %s is invalid\n", + __func__, mdt_name); + ret = -EINVAL; + goto done; + } + + elf_ptr = fw->data + sizeof(*ehdr); + + if (load_type == ELF_FLAG_EXECUTE) { + /* Reset CPE first */ + ret = cpe_svc_reset(core->cpe_handle); + if (IS_ERR_VALUE(ret)) { + dev_err(core->dev, + "%s: Failed to reset CPE with error %d\n", + __func__, ret); + goto done; + } + } + + dev_dbg(core->dev, "%s: start image dload, name = %s, load_type = 0x%x\n", + __func__, core->fname, load_type); + + wcd_cpe_bus_vote_max_bw(core, true); + + /* parse every program header and request corresponding firmware */ + for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) { + phdr = (struct elf32_phdr *)elf_ptr; + load_segment = false; + + dev_dbg(core->dev, + "index = %d, vaddr = 0x%x, paddr = 0x%x, filesz = 0x%x, memsz = 0x%x, flags = 0x%x\n" + , phdr_idx, phdr->p_vaddr, phdr->p_paddr, + phdr->p_filesz, phdr->p_memsz, phdr->p_flags); + + switch (load_type) { + case ELF_FLAG_EXECUTE: + if (phdr->p_flags & load_type) + load_segment = true; + break; + case ELF_FLAG_RW: + if (!(phdr->p_flags & ELF_FLAG_EXECUTE) && + (phdr->p_flags & load_type)) + load_segment = true; + break; + default: + pr_err("%s: Invalid load_type 0x%x\n", + __func__, load_type); + ret = -EINVAL; + goto rel_bus_vote; + } + + if (load_segment) { + ret = wcd_cpe_load_each_segment(core, + phdr_idx, phdr); + if (IS_ERR_VALUE(ret)) { + dev_err(core->dev, + "Failed to load segment %d, aborting img dload\n", + phdr_idx); + img_dload_fail = true; + goto rel_bus_vote; + } + } else { + dev_dbg(core->dev, + "%s: skipped segment with index %d\n", + __func__, phdr_idx); + } + + elf_ptr = elf_ptr + sizeof(*phdr); + } + if (load_type == ELF_FLAG_EXECUTE) + core->ssr_type = WCD_CPE_IMEM_DOWNLOADED; + +rel_bus_vote: + wcd_cpe_bus_vote_max_bw(core, false); + +done: + release_firmware(fw); + return ret; +} + +/* + * wcd_cpe_change_online_state - mark cpe online/offline state + * @core: core session to mark + * @online: whether online of offline + * + */ +static void wcd_cpe_change_online_state(struct wcd_cpe_core *core, + int online) +{ + struct wcd_cpe_ssr_entry *ssr_entry = NULL; + unsigned long ret; + + if (!core) { + pr_err("%s: Invalid core handle\n", + __func__); + return; + } + + ssr_entry = &core->ssr_entry; + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + ssr_entry->offline = !online; + + /* Make sure write to offline state is completed. */ + wmb(); + ret = xchg(&ssr_entry->offline_change, 1); + wake_up_interruptible(&ssr_entry->offline_poll_wait); + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); + pr_debug("%s: change state 0x%x offline_change 0x%x\n" + " core->offline 0x%x, ret = %ld\n", + __func__, online, + ssr_entry->offline_change, + core->ssr_entry.offline, ret); +} + +/* + * wcd_cpe_load_fw_image: work function to load the fw image + * @work: work that is scheduled to perform the image loading + * + * Parse the mdt file to look for program headers, load each + * split file corresponding to the program headers. + */ +static void wcd_cpe_load_fw_image(struct work_struct *work) +{ + struct wcd_cpe_core *core; + int ret = 0; + + core = container_of(work, struct wcd_cpe_core, load_fw_work); + ret = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE); + if (!ret) + wcd_cpe_change_online_state(core, 1); + else + pr_err("%s: failed to load instruction section, err = %d\n", + __func__, ret); +} + +/* + * wcd_cpe_get_core_handle: get the handle to wcd_cpe_core + * @codec: codec from which this handle is to be obtained + * Codec driver should provide a callback function to obtain + * handle to wcd_cpe_core during initialization of wcd_cpe_core + */ +void *wcd_cpe_get_core_handle( + struct snd_soc_codec *codec) +{ + struct wcd_cpe_core *core = NULL; + + if (!codec) { + pr_err("%s: Invalid codec handle\n", + __func__); + goto done; + } + + if (!wcd_get_cpe_core) { + dev_err(codec->dev, + "%s: codec callback not available\n", + __func__); + goto done; + } + + core = wcd_get_cpe_core(codec); + + if (!core) + dev_err(codec->dev, + "%s: handle to core not available\n", + __func__); +done: + return core; +} + +/* + * svass_engine_irq: threaded interrupt handler for svass engine irq + * @irq: interrupt number + * @data: data pointer passed during irq registration + */ +static irqreturn_t svass_engine_irq(int irq, void *data) +{ + struct wcd_cpe_core *core = data; + int ret = 0; + + if (!core) { + pr_err("%s: Invalid data for interrupt handler\n", + __func__); + goto done; + } + + ret = cpe_svc_process_irq(core->cpe_handle, CPE_IRQ_OUTBOX_IRQ); + if (IS_ERR_VALUE(ret)) + dev_err(core->dev, + "%s: Error processing irq from cpe_Services\n", + __func__); +done: + return IRQ_HANDLED; +} + +/* + * wcd_cpe_state_read - update read status in procfs + * @entry: snd_info_entry + * @buf: buffer where the read status is updated. + * + */ +static ssize_t wcd_cpe_state_read(struct snd_info_entry *entry, + void *file_private_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + int len = 0; + char buffer[WCD_CPE_STATE_MAX_LEN]; + struct wcd_cpe_core *core = NULL; + struct wcd_cpe_ssr_entry *ssr_entry = NULL; + + core = (struct wcd_cpe_core *) entry->private_data; + if (!core) { + pr_err("%s: CPE core NULL\n", __func__); + return -EINVAL; + } + ssr_entry = &core->ssr_entry; + + /* Make sure read from ssr_entry is completed. */ + rmb(); + dev_dbg(core->dev, + "%s: Offline 0x%x\n", __func__, + ssr_entry->offline); + + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + len = snprintf(buffer, sizeof(buffer), "%s\n", + ssr_entry->offline ? "OFFLINE" : "ONLINE"); + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +/* + * wcd_cpe_state_poll - polls for change state + * @entry: snd_info_entry + * @wait: wait for duration for poll wait + * + */ +static unsigned int wcd_cpe_state_poll(struct snd_info_entry *entry, + void *private_data, struct file *file, + poll_table *wait) +{ + struct wcd_cpe_core *core = NULL; + struct wcd_cpe_ssr_entry *ssr_entry = NULL; + int ret = 0; + + core = (struct wcd_cpe_core *) entry->private_data; + if (!core) { + pr_err("%s: CPE core NULL\n", __func__); + return -EINVAL; + } + + ssr_entry = &core->ssr_entry; + + dev_dbg(core->dev, "%s: CPE Poll wait\n", + __func__); + poll_wait(file, &ssr_entry->offline_poll_wait, wait); + dev_dbg(core->dev, "%s: Wake-up Poll wait\n", + __func__); + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + + if (xchg(&ssr_entry->offline_change, 0)) + ret = POLLIN | POLLPRI | POLLRDNORM; + + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); + + dev_dbg(core->dev, "%s: ret (%d) from poll_wait\n", + __func__, ret); + return ret; +} + +/* + * wcd_cpe_is_online_state - return true if card is online state + * @core: core offline to query + */ +static bool wcd_cpe_is_online_state(void *core_handle) +{ + struct wcd_cpe_core *core = core_handle; + + if (core_handle) { + return !core->ssr_entry.offline; + } else { + pr_err("%s: Core handle NULL\n", __func__); + /* still return 1- offline if core ptr null */ + return false; + } +} + +static struct snd_info_entry_ops wcd_cpe_state_proc_ops = { + .read = wcd_cpe_state_read, + .poll = wcd_cpe_state_poll, +}; + +static int wcd_cpe_check_new_image(struct wcd_cpe_core *core) +{ + int rc = 0; + char temp_img_name[WCD_CPE_IMAGE_FNAME_MAX]; + + if (!strcmp(core->fname, core->dyn_fname) && + core->ssr_type != WCD_CPE_INITIALIZED) { + dev_dbg(core->dev, + "%s: Firmware unchanged, fname = %s, ssr_type 0x%x\n", + __func__, core->fname, core->ssr_type); + goto done; + } + + /* + * Different firmware name requested, + * Re-load the instruction section + */ + strlcpy(temp_img_name, core->fname, + WCD_CPE_IMAGE_FNAME_MAX); + strlcpy(core->fname, core->dyn_fname, + WCD_CPE_IMAGE_FNAME_MAX); + + rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE); + if (rc) { + dev_err(core->dev, + "%s: Failed to dload new image %s, err = %d\n", + __func__, core->fname, rc); + /* If new image download failed, revert back to old image */ + strlcpy(core->fname, temp_img_name, + WCD_CPE_IMAGE_FNAME_MAX); + rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE); + if (rc) + dev_err(core->dev, + "%s: Failed to re-dload image %s, err = %d\n", + __func__, core->fname, rc); + } else { + dev_info(core->dev, "%s: fw changed to %s\n", + __func__, core->fname); + } +done: + return rc; +} + +static int wcd_cpe_enable(struct wcd_cpe_core *core, + bool enable) +{ + int ret = 0; + + if (enable) { + /* Reset CPE first */ + ret = cpe_svc_reset(core->cpe_handle); + if (IS_ERR_VALUE(ret)) { + dev_err(core->dev, + "%s: CPE Reset failed, error = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_cpe_setup_irqs(core); + if (ret) { + dev_err(core->dev, + "%s: CPE IRQs setup failed, error = %d\n", + __func__, ret); + goto done; + } + ret = wcd_cpe_check_new_image(core); + if (ret) + goto fail_boot; + + /* Dload data section */ + ret = wcd_cpe_load_fw(core, ELF_FLAG_RW); + if (ret) { + dev_err(core->dev, + "%s: Failed to dload data section, err = %d\n", + __func__, ret); + goto fail_boot; + } + + ret = wcd_cpe_enable_cpe_clks(core, true); + if (IS_ERR_VALUE(ret)) { + dev_err(core->dev, + "%s: CPE clk enable failed, err = %d\n", + __func__, ret); + goto fail_boot; + } + + ret = cpe_svc_boot(core->cpe_handle, + core->cpe_debug_mode); + if (IS_ERR_VALUE(ret)) { + dev_err(core->dev, + "%s: Failed to boot CPE\n", + __func__); + goto fail_boot; + } + + /* wait for CPE to be online */ + dev_dbg(core->dev, + "%s: waiting for CPE bootup\n", + __func__); + + wait_for_completion(&core->online_compl); + + dev_dbg(core->dev, + "%s: CPE bootup done\n", + __func__); + + core->ssr_type = WCD_CPE_ENABLED; + } else { + if (core->ssr_type == WCD_CPE_BUS_DOWN_EVENT || + core->ssr_type == WCD_CPE_SSR_EVENT) { + /* + * If this disable vote is when + * SSR is in progress, do not disable CPE here, + * instead SSR handler will control CPE. + */ + wcd_cpe_enable_cpe_clks(core, false); + /* + * During BUS_DOWN event, possibly the + * irq driver is under cleanup, do not request + * cleanup of irqs here, rather cleanup irqs + * once BUS_UP event is received. + */ + if (core->ssr_type != WCD_CPE_BUS_DOWN_EVENT) + wcd_cpe_cleanup_irqs(core); + goto done; + } + + ret = cpe_svc_shutdown(core->cpe_handle); + if (IS_ERR_VALUE(ret)) { + dev_err(core->dev, + "%s: CPE shutdown failed, error %d\n", + __func__, ret); + goto done; + } + + wcd_cpe_enable_cpe_clks(core, false); + wcd_cpe_cleanup_irqs(core); + core->ssr_type = WCD_CPE_IMEM_DOWNLOADED; + } + + return ret; + +fail_boot: + wcd_cpe_cleanup_irqs(core); + +done: + return ret; +} + +/* + * wcd_cpe_boot_ssr: Load the images to CPE after ssr and bootup cpe + * @core: handle to the core + */ +static int wcd_cpe_boot_ssr(struct wcd_cpe_core *core) +{ + int rc = 0; + + if (!core || !core->cpe_handle) { + pr_err("%s: Invalid handle\n", __func__); + rc = -EINVAL; + goto fail; + } + /* Load the instruction section and mark CPE as online */ + rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE); + if (rc) { + dev_err(core->dev, + "%s: Failed to load instruction, err = %d\n", + __func__, rc); + goto fail; + } else { + wcd_cpe_change_online_state(core, 1); + } + +fail: + return rc; +} + +/* + * wcd_cpe_clr_ready_status: + * Clear the value from the ready status for CPE + * @core: handle to the core + * @value: flag/bitmask that is to be cleared + * + * This function should not be invoked with ssr_lock acquired + */ +static void wcd_cpe_clr_ready_status(struct wcd_cpe_core *core, + u8 value) +{ + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + core->ready_status &= ~(value); + dev_dbg(core->dev, + "%s: ready_status = 0x%x\n", + __func__, core->ready_status); + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); +} + +/* + * wcd_cpe_set_and_complete: + * Set the ready status with the provided value and + * flag the completion object if ready status moves + * to ready to download + * @core: handle to the core + * @value: flag/bitmask that is to be set + */ +static void wcd_cpe_set_and_complete(struct wcd_cpe_core *core, + u8 value) +{ + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + core->ready_status |= value; + if ((core->ready_status & WCD_CPE_READY_TO_DLOAD) == + WCD_CPE_READY_TO_DLOAD) { + dev_dbg(core->dev, + "%s: marking ready, status = 0x%x\n", + __func__, core->ready_status); + complete(&core->ready_compl); + } + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); +} + + +/* + * wcd_cpe_ssr_work: work function to handle CPE SSR + * @work: work that is scheduled to perform CPE shutdown + * and restart + */ +static void wcd_cpe_ssr_work(struct work_struct *work) +{ + + int rc = 0; + u32 irq = 0; + struct wcd_cpe_core *core = NULL; + u8 status = 0; + + core = container_of(work, struct wcd_cpe_core, ssr_work); + if (!core) { + pr_err("%s: Core handle NULL\n", __func__); + return; + } + + /* Obtain pm request up in case of suspend mode */ + pm_qos_add_request(&core->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + pm_qos_update_request(&core->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + dev_dbg(core->dev, + "%s: CPE SSR with event %d\n", + __func__, core->ssr_type); + + if (core->ssr_type == WCD_CPE_SSR_EVENT) { + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_STATUS, + &status); + if (status & core->irq_info.cpe_fatal_irqs) + irq = CPE_IRQ_WDOG_BITE; + } else { + /* If bus is down, cdc reg cannot be read */ + irq = CPE_IRQ_WDOG_BITE; + } + + if (core->cpe_users > 0) { + rc = cpe_svc_process_irq(core->cpe_handle, irq); + if (IS_ERR_VALUE(rc)) + /* + * Even if process_irq fails, + * wait for cpe to move to offline state + */ + dev_err(core->dev, + "%s: irq processing failed, error = %d\n", + __func__, rc); + + rc = wait_for_completion_timeout(&core->offline_compl, + CPE_OFFLINE_WAIT_TIMEOUT); + if (!rc) { + dev_err(core->dev, + "%s: wait for cpe offline timed out\n", + __func__); + goto err_ret; + } + if (core->ssr_type != WCD_CPE_BUS_DOWN_EVENT) { + wcd_cpe_get_sfr_dump(core); + + /* + * Ramdump has to be explicitly enabled + * through debugfs and cannot be collected + * when bus is down. + */ + if (ramdump_enable) + wcd_cpe_collect_ramdump(core); + } + } else { + pr_err("%s: no cpe users, mark as offline\n", __func__); + wcd_cpe_change_online_state(core, 0); + wcd_cpe_set_and_complete(core, + WCD_CPE_BLK_READY); + } + + rc = wait_for_completion_timeout(&core->ready_compl, + CPE_READY_WAIT_TIMEOUT); + if (!rc) { + dev_err(core->dev, + "%s: ready to online timed out, status = %u\n", + __func__, core->ready_status); + goto err_ret; + } + + rc = wcd_cpe_boot_ssr(core); + + /* Once image are downloaded make sure all + * error interrupts are cleared + */ + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_CLEAR, NULL); + +err_ret: + /* remove after default pm qos */ + pm_qos_update_request(&core->pm_qos_req, + PM_QOS_DEFAULT_VALUE); + pm_qos_remove_request(&core->pm_qos_req); +} + +/* + * wcd_cpe_ssr_handle: handle SSR events here. + * @core_handle: handle to the cpe core + * @event: indicates ADSP or CDSP SSR. + */ +int wcd_cpe_ssr_event(void *core_handle, + enum wcd_cpe_ssr_state_event event) +{ + struct wcd_cpe_core *core = core_handle; + + if (!core) { + pr_err("%s: Invalid handle to core\n", + __func__); + return -EINVAL; + } + + /* + * If CPE is not even enabled, the SSR event for + * CPE needs to be ignored + */ + if (core->ssr_type == WCD_CPE_INITIALIZED) { + dev_info(core->dev, + "%s: CPE initialized but not enabled, skip CPE ssr\n", + __func__); + return 0; + } + + dev_dbg(core->dev, + "%s: Schedule ssr work, event = %d\n", + __func__, core->ssr_type); + + switch (event) { + case WCD_CPE_BUS_DOWN_EVENT: + /* + * If bus down, then CPE block is also + * treated to be down + */ + wcd_cpe_clr_ready_status(core, WCD_CPE_READY_TO_DLOAD); + core->ssr_type = event; + schedule_work(&core->ssr_work); + break; + + case WCD_CPE_SSR_EVENT: + wcd_cpe_clr_ready_status(core, WCD_CPE_BLK_READY); + core->ssr_type = event; + schedule_work(&core->ssr_work); + break; + + case WCD_CPE_BUS_UP_EVENT: + wcd_cpe_cleanup_irqs(core); + wcd_cpe_set_and_complete(core, WCD_CPE_BUS_READY); + /* + * In case of bus up event ssr_type will be changed + * to WCD_CPE_ACTIVE once CPE is online + */ + break; + + default: + dev_err(core->dev, + "%s: unhandled SSR event %d\n", + __func__, event); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(wcd_cpe_ssr_event); + +/* + * svass_exception_irq: threaded irq handler for sva error interrupts + * @irq: interrupt number + * @data: data pointer passed during irq registration + * + * Once a error interrupt is received, it is not cleared, since + * clearing this interrupt will raise spurious interrupts unless + * CPE is reset. + */ +static irqreturn_t svass_exception_irq(int irq, void *data) +{ + struct wcd_cpe_core *core = data; + u8 status = 0; + + if (!core || !CPE_ERR_IRQ_CB(core)) { + pr_err("%s: Invalid %s\n", + __func__, + (!core) ? "core" : "cdc control"); + return IRQ_HANDLED; + } + + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_STATUS, &status); + + while (status != 0) { + if (status & core->irq_info.cpe_fatal_irqs) { + dev_err(core->dev, + "%s: CPE SSR event,err_status = 0x%02x\n", + __func__, status); + wcd_cpe_ssr_event(core, WCD_CPE_SSR_EVENT); + /* + * If fatal interrupt is received, + * trigger SSR and stop processing + * further interrupts + */ + break; + } + /* + * Mask the interrupt that was raised to + * avoid spurious interrupts + */ + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_MASK, &status); + + /* Clear only the interrupt that was raised */ + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_CLEAR, &status); + dev_err(core->dev, + "%s: err_interrupt status = 0x%x\n", + __func__, status); + + /* Read status for pending interrupts */ + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_STATUS, &status); + } + + return IRQ_HANDLED; +} + +/* + * wcd_cpe_cmi_afe_cb: callback called on response to afe commands + * @param: parameter containing the response code, etc + * + * Process the request to the command sent to CPE and wakeup the + * command send wait. + */ +static void wcd_cpe_cmi_afe_cb(const struct cmi_api_notification *param) +{ + struct cmi_hdr *hdr; + struct wcd_cmi_afe_port_data *afe_port_d; + u8 port_id; + + if (!param) { + pr_err("%s: param is null\n", __func__); + return; + } + + if (param->event != CMI_API_MSG) { + pr_err("%s: unhandled event 0x%x\n", + __func__, param->event); + return; + } + + pr_debug("%s: param->result = %d\n", + __func__, param->result); + + hdr = (struct cmi_hdr *) param->message; + + /* + * for AFE cmd response, port id is + * stored at session id field of header + */ + port_id = CMI_HDR_GET_SESSION_ID(hdr); + if (port_id > WCD_CPE_AFE_MAX_PORTS) { + pr_err("%s: invalid port_id %d\n", + __func__, port_id); + return; + } + + afe_port_d = &(afe_ports[port_id]); + + if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) { + + u8 *payload = ((u8 *)param->message) + (sizeof(struct cmi_hdr)); + u8 result = payload[0]; + + afe_port_d->cmd_result = result; + complete(&afe_port_d->afe_cmd_complete); + + } else if (hdr->opcode == CPE_AFE_PORT_CMDRSP_SHARED_MEM_ALLOC) { + + struct cpe_cmdrsp_shmem_alloc *cmdrsp_shmem_alloc = + (struct cpe_cmdrsp_shmem_alloc *) param->message; + + if (cmdrsp_shmem_alloc->addr == 0) { + pr_err("%s: Failed AFE shared mem alloc\n", __func__); + afe_port_d->cmd_result = CMI_SHMEM_ALLOC_FAILED; + } else { + pr_debug("%s AFE shared mem addr = 0x%x\n", + __func__, cmdrsp_shmem_alloc->addr); + afe_port_d->mem_handle = cmdrsp_shmem_alloc->addr; + afe_port_d->cmd_result = 0; + } + complete(&afe_port_d->afe_cmd_complete); + } +} + +/* + * wcd_cpe_initialize_afe_port_data: Initialize all AFE ports + * + * Initialize the data for all the afe ports. Assign the + * afe port state to INIT state. + */ +static void wcd_cpe_initialize_afe_port_data(void) +{ + struct wcd_cmi_afe_port_data *afe_port_d; + int i; + + for (i = 0; i <= WCD_CPE_AFE_MAX_PORTS; i++) { + afe_port_d = &afe_ports[i]; + afe_port_d->port_id = i; + init_completion(&afe_port_d->afe_cmd_complete); + afe_port_d->port_state = AFE_PORT_STATE_INIT; + mutex_init(&afe_port_d->afe_lock); + } +} + +/* + * wcd_cpe_deinitialize_afe_port_data: De-initialize all AFE ports + * + * De-Initialize the data for all the afe ports. Assign the + * afe port state to DEINIT state. + */ +static void wcd_cpe_deinitialize_afe_port_data(void) +{ + struct wcd_cmi_afe_port_data *afe_port_d; + int i; + + for (i = 0; i <= WCD_CPE_AFE_MAX_PORTS; i++) { + afe_port_d = &afe_ports[i]; + afe_port_d->port_state = AFE_PORT_STATE_DEINIT; + mutex_destroy(&afe_port_d->afe_lock); + } +} + +/* + * wcd_cpe_svc_event_cb: callback from cpe services, indicating + * CPE is online or offline. + * @param: parameter / payload for event to be notified + */ +static void wcd_cpe_svc_event_cb(const struct cpe_svc_notification *param) +{ + struct snd_soc_codec *codec; + struct wcd_cpe_core *core; + struct cpe_svc_boot_event *boot_data; + bool active_sessions; + + if (!param) { + pr_err("%s: Invalid event\n", __func__); + return; + } + + codec = param->private_data; + if (!codec) { + pr_err("%s: Invalid handle to codec\n", + __func__); + return; + } + + core = wcd_cpe_get_core_handle(codec); + if (!core) { + pr_err("%s: Invalid handle to core\n", + __func__); + return; + } + + dev_dbg(core->dev, + "%s: event = 0x%x, ssr_type = 0x%x\n", + __func__, param->event, core->ssr_type); + + switch (param->event) { + case CPE_SVC_BOOT: + boot_data = (struct cpe_svc_boot_event *) + param->payload; + core->sfr_buf_addr = boot_data->debug_address; + core->sfr_buf_size = boot_data->debug_buffer_size; + dev_dbg(core->dev, + "%s: CPE booted, sfr_addr = %d, sfr_size = %zu\n", + __func__, core->sfr_buf_addr, + core->sfr_buf_size); + break; + case CPE_SVC_ONLINE: + core->ssr_type = WCD_CPE_ACTIVE; + dev_dbg(core->dev, "%s CPE is now online\n", + __func__); + complete(&core->online_compl); + break; + case CPE_SVC_OFFLINE: + /* + * offline can happen during normal shutdown, + * but we are interested in offline only during + * SSR. + */ + if (core->ssr_type != WCD_CPE_SSR_EVENT && + core->ssr_type != WCD_CPE_BUS_DOWN_EVENT) + break; + + active_sessions = wcd_cpe_lsm_session_active(); + wcd_cpe_change_online_state(core, 0); + complete(&core->offline_compl); + dev_err(core->dev, "%s: CPE is now offline\n", + __func__); + break; + case CPE_SVC_CMI_CLIENTS_DEREG: + + /* + * Only when either CPE SSR is in progress, + * or the bus is down, we need to mark the CPE + * as ready. In all other cases, this event is + * ignored + */ + if (core->ssr_type == WCD_CPE_SSR_EVENT || + core->ssr_type == WCD_CPE_BUS_DOWN_EVENT) + wcd_cpe_set_and_complete(core, + WCD_CPE_BLK_READY); + break; + default: + dev_err(core->dev, + "%s: unhandled notification\n", + __func__); + break; + } +} + +/* + * wcd_cpe_cleanup_irqs: free the irq resources required by cpe + * @core: handle the cpe core + * + * This API will free the IRQs for CPE but does not mask the + * CPE interrupts. If masking is needed, it has to be done + * explicity by caller. + */ +static void wcd_cpe_cleanup_irqs(struct wcd_cpe_core *core) +{ + + struct snd_soc_codec *codec = core->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, + core->irq_info.cpe_engine_irq, + core); + wcd9xxx_free_irq(core_res, + core->irq_info.cpe_err_irq, + core); + +} + +/* + * wcd_cpe_setup_sva_err_intr: setup the irqs for CPE + * @core: handle to wcd_cpe_core + * All interrupts needed for CPE are acquired. If any + * request_irq fails, then all irqs are free'd + */ +static int wcd_cpe_setup_irqs(struct wcd_cpe_core *core) +{ + int ret; + struct snd_soc_codec *codec = core->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + + ret = wcd9xxx_request_irq(core_res, + core->irq_info.cpe_engine_irq, + svass_engine_irq, "SVASS_Engine", core); + if (ret) { + dev_err(core->dev, + "%s: Failed to request svass engine irq\n", + __func__); + goto fail_engine_irq; + } + + /* Make sure all error interrupts are cleared */ + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_CLEAR, + NULL); + + /* Enable required error interrupts */ + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_UNMASK, + NULL); + + ret = wcd9xxx_request_irq(core_res, + core->irq_info.cpe_err_irq, + svass_exception_irq, "SVASS_Exception", core); + if (ret) { + dev_err(core->dev, + "%s: Failed to request svass err irq\n", + __func__); + goto fail_exception_irq; + } + + return 0; + +fail_exception_irq: + wcd9xxx_free_irq(core_res, + core->irq_info.cpe_engine_irq, core); + +fail_engine_irq: + return ret; +} + +static int wcd_cpe_get_cal_index(int32_t cal_type) +{ + int cal_index = -EINVAL; + + if (cal_type == ULP_AFE_CAL_TYPE) + cal_index = WCD_CPE_LSM_CAL_AFE; + else if (cal_type == ULP_LSM_CAL_TYPE) + cal_index = WCD_CPE_LSM_CAL_LSM; + else if (cal_type == ULP_LSM_TOPOLOGY_ID_CAL_TYPE) + cal_index = WCD_CPE_LSM_CAL_TOPOLOGY_ID; + else + pr_err("%s: invalid cal_type %d\n", + __func__, cal_type); + + return cal_index; +} + +static int wcd_cpe_alloc_cal(int32_t cal_type, size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + cal_index = wcd_cpe_get_cal_index(cal_type); + if (cal_index < 0) { + pr_err("%s: invalid caltype %d\n", + __func__, cal_type); + return -EINVAL; + } + + ret = cal_utils_alloc_cal(data_size, data, + core_d->cal_data[cal_index], + 0, NULL); + if (ret < 0) + pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + return ret; +} + +static int wcd_cpe_dealloc_cal(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + int cal_index; + + cal_index = wcd_cpe_get_cal_index(cal_type); + if (cal_index < 0) { + pr_err("%s: invalid caltype %d\n", + __func__, cal_type); + return -EINVAL; + } + + ret = cal_utils_dealloc_cal(data_size, data, + core_d->cal_data[cal_index]); + if (ret < 0) + pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + return ret; +} + +static int wcd_cpe_set_cal(int32_t cal_type, size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + cal_index = wcd_cpe_get_cal_index(cal_type); + if (cal_index < 0) { + pr_err("%s: invalid caltype %d\n", + __func__, cal_type); + return -EINVAL; + } + + ret = cal_utils_set_cal(data_size, data, + core_d->cal_data[cal_index], + 0, NULL); + if (ret < 0) + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + return ret; +} + +static int wcd_cpe_cal_init(struct wcd_cpe_core *core) +{ + int ret = 0; + + struct cal_type_info cal_type_info[] = { + {{ULP_AFE_CAL_TYPE, + {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL, + wcd_cpe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ULP_LSM_CAL_TYPE, + {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL, + wcd_cpe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ULP_LSM_TOPOLOGY_ID_CAL_TYPE, + {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL, + wcd_cpe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + }; + + ret = cal_utils_create_cal_types(WCD_CPE_LSM_CAL_MAX, + core->cal_data, + cal_type_info); + if (ret < 0) + pr_err("%s: could not create cal type!\n", + __func__); + return ret; +} + +/* + * wcd_cpe_enable: setup the cpe interrupts and schedule + * the work to download image and bootup the CPE. + * core: handle to cpe core structure + */ +static int wcd_cpe_vote(struct wcd_cpe_core *core, + bool enable) +{ + int ret = 0; + + if (!core) { + pr_err("%s: Invalid handle to core\n", + __func__); + ret = -EINVAL; + goto done; + } + + dev_dbg(core->dev, + "%s: enter, enable = %s, cpe_users = %u\n", + __func__, (enable ? "true" : "false"), + core->cpe_users); + + if (enable) { + core->cpe_users++; + if (core->cpe_users == 1) { + ret = wcd_cpe_enable(core, enable); + if (ret) { + dev_err(core->dev, + "%s: CPE enable failed, err = %d\n", + __func__, ret); + goto done; + } + } else { + dev_dbg(core->dev, + "%s: cpe already enabled, users = %u\n", + __func__, core->cpe_users); + goto done; + } + } else { + core->cpe_users--; + if (core->cpe_users == 0) { + ret = wcd_cpe_enable(core, enable); + if (ret) { + dev_err(core->dev, + "%s: CPE disable failed, err = %d\n", + __func__, ret); + goto done; + } + } else { + dev_dbg(core->dev, + "%s: %u valid users on cpe\n", + __func__, core->cpe_users); + goto done; + } + } + + dev_dbg(core->dev, + "%s: leave, enable = %s, cpe_users = %u\n", + __func__, (enable ? "true" : "false"), + core->cpe_users); + +done: + return ret; +} + +static int wcd_cpe_debugfs_init(struct wcd_cpe_core *core) +{ + int rc = 0; + + struct dentry *dir = debugfs_create_dir("wcd_cpe", NULL); + + if (IS_ERR_OR_NULL(dir)) { + dir = NULL; + rc = -ENODEV; + goto err_create_dir; + } + + if (!debugfs_create_u32("ramdump_enable", 0644, + dir, &ramdump_enable)) { + dev_err(core->dev, "%s: Failed to create debugfs node %s\n", + __func__, "ramdump_enable"); + rc = -ENODEV; + goto err_create_entry; + } + + if (!debugfs_create_file("cpe_ftm_test_trigger", 0200, + dir, core, &cpe_ftm_test_trigger_fops)) { + dev_err(core->dev, "%s: Failed to create debugfs node %s\n", + __func__, "cpe_ftm_test_trigger"); + rc = -ENODEV; + goto err_create_entry; + } + + if (!debugfs_create_u32("cpe_ftm_test_status", 0444, + dir, &cpe_ftm_test_status)) { + dev_err(core->dev, "%s: Failed to create debugfs node %s\n", + __func__, "cpe_ftm_test_status"); + rc = -ENODEV; + goto err_create_entry; + } + +err_create_entry: + debugfs_remove(dir); + +err_create_dir: + return rc; +} + +static ssize_t fw_name_show(struct wcd_cpe_core *core, char *buf) +{ + return snprintf(buf, WCD_CPE_IMAGE_FNAME_MAX, "%s", + core->dyn_fname); +} + +static ssize_t fw_name_store(struct wcd_cpe_core *core, + const char *buf, ssize_t count) +{ + int copy_count = count; + const char *pos; + + pos = memchr(buf, '\n', count); + if (pos) + copy_count = pos - buf; + + if (copy_count > WCD_CPE_IMAGE_FNAME_MAX) { + dev_err(core->dev, + "%s: Invalid length %d, max allowed %d\n", + __func__, copy_count, WCD_CPE_IMAGE_FNAME_MAX); + return -EINVAL; + } + + strlcpy(core->dyn_fname, buf, copy_count + 1); + + return count; +} + +WCD_CPE_ATTR(fw_name, 0660, fw_name_show, fw_name_store); + +static ssize_t wcd_cpe_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct wcd_cpe_attribute *cpe_attr = to_wcd_cpe_attr(attr); + struct wcd_cpe_core *core = kobj_to_cpe_core(kobj); + ssize_t ret = -EINVAL; + + if (core && cpe_attr->show) + ret = cpe_attr->show(core, buf); + + return ret; +} + +static ssize_t wcd_cpe_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + struct wcd_cpe_attribute *cpe_attr = to_wcd_cpe_attr(attr); + struct wcd_cpe_core *core = kobj_to_cpe_core(kobj); + ssize_t ret = -EINVAL; + + if (core && cpe_attr->store) + ret = cpe_attr->store(core, buf, count); + + return ret; +} + +static const struct sysfs_ops wcd_cpe_sysfs_ops = { + .show = wcd_cpe_sysfs_show, + .store = wcd_cpe_sysfs_store, +}; + +static struct kobj_type wcd_cpe_ktype = { + .sysfs_ops = &wcd_cpe_sysfs_ops, +}; + +static int wcd_cpe_sysfs_init(struct wcd_cpe_core *core, int id) +{ + char sysfs_dir_name[WCD_CPE_SYSFS_DIR_MAX_LENGTH]; + int rc = 0; + + snprintf(sysfs_dir_name, WCD_CPE_SYSFS_DIR_MAX_LENGTH, + "%s%d", "wcd_cpe", id); + + rc = kobject_init_and_add(&core->cpe_kobj, &wcd_cpe_ktype, + kernel_kobj, + sysfs_dir_name); + if (unlikely(rc)) { + dev_err(core->dev, + "%s: Failed to add kobject %s, err = %d\n", + __func__, sysfs_dir_name, rc); + goto done; + } + + rc = sysfs_create_file(&core->cpe_kobj, &cpe_attr_fw_name.attr); + if (rc) { + dev_err(core->dev, + "%s: Failed to fw_name sysfs entry to %s\n", + __func__, sysfs_dir_name); + goto fail_create_file; + } + + return 0; + +fail_create_file: + kobject_put(&core->cpe_kobj); +done: + return rc; +} + +static ssize_t cpe_ftm_test_trigger(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wcd_cpe_core *core = file->private_data; + int ret = 0; + + /* Enable the clks for cpe */ + ret = wcd_cpe_enable_cpe_clks(core, true); + if (IS_ERR_VALUE(ret)) { + dev_err(core->dev, + "%s: CPE clk enable failed, err = %d\n", + __func__, ret); + goto done; + } + + /* Get the CPE_STATUS */ + ret = cpe_svc_ftm_test(core->cpe_handle, &cpe_ftm_test_status); + if (IS_ERR_VALUE(ret)) { + dev_err(core->dev, + "%s: CPE FTM test failed, err = %d\n", + __func__, ret); + if (ret == CPE_SVC_BUSY) { + cpe_ftm_test_status = 1; + ret = 0; + } + } + + /* Disable the clks for cpe */ + ret = wcd_cpe_enable_cpe_clks(core, false); + if (IS_ERR_VALUE(ret)) { + dev_err(core->dev, + "%s: CPE clk disable failed, err = %d\n", + __func__, ret); + } + +done: + if (ret < 0) + return ret; + else + return count; +} + +static int wcd_cpe_validate_params( + struct snd_soc_codec *codec, + struct wcd_cpe_params *params) +{ + + if (!codec) { + pr_err("%s: Invalid codec\n", __func__); + return -EINVAL; + } + + if (!params) { + dev_err(codec->dev, + "%s: No params supplied for codec %s\n", + __func__, codec->component.name); + return -EINVAL; + } + + if (!params->codec || !params->get_cpe_core || + !params->cdc_cb) { + dev_err(codec->dev, + "%s: Invalid params for codec %s\n", + __func__, codec->component.name); + return -EINVAL; + } + + return 0; +} + +/* + * wcd_cpe_init: Initialize CPE related structures + * @img_fname: filename for firmware image + * @codec: handle to codec requesting for image download + * @params: parameter structure passed from caller + * + * This API will initialize the cpe core but will not + * download the image or boot the cpe core. + */ +struct wcd_cpe_core *wcd_cpe_init(const char *img_fname, + struct snd_soc_codec *codec, + struct wcd_cpe_params *params) +{ + struct wcd_cpe_core *core; + int ret = 0; + struct snd_card *card = NULL; + struct snd_info_entry *entry = NULL; + char proc_name[WCD_CPE_STATE_MAX_LEN]; + const char *cpe_name = "cpe"; + const char *state_name = "_state"; + const struct cpe_svc_hw_cfg *hw_info; + int id = 0; + + if (wcd_cpe_validate_params(codec, params)) + return NULL; + + core = kzalloc(sizeof(struct wcd_cpe_core), GFP_KERNEL); + if (!core) + return NULL; + + snprintf(core->fname, sizeof(core->fname), "%s", img_fname); + strlcpy(core->dyn_fname, core->fname, WCD_CPE_IMAGE_FNAME_MAX); + + wcd_get_cpe_core = params->get_cpe_core; + + core->codec = params->codec; + core->dev = params->codec->dev; + core->cpe_debug_mode = params->dbg_mode; + + core->cdc_info.major_version = params->cdc_major_ver; + core->cdc_info.minor_version = params->cdc_minor_ver; + core->cdc_info.id = params->cdc_id; + + core->cpe_cdc_cb = params->cdc_cb; + + memcpy(&core->irq_info, ¶ms->cdc_irq_info, + sizeof(core->irq_info)); + + INIT_WORK(&core->load_fw_work, wcd_cpe_load_fw_image); + INIT_WORK(&core->ssr_work, wcd_cpe_ssr_work); + init_completion(&core->offline_compl); + init_completion(&core->ready_compl); + init_completion(&core->online_compl); + init_waitqueue_head(&core->ssr_entry.offline_poll_wait); + mutex_init(&core->ssr_lock); + core->cpe_users = 0; + core->cpe_clk_ref = 0; + + /* + * By default, during probe, it is assumed that + * both CPE hardware block and underlying bus to codec + * are ready + */ + core->ready_status = WCD_CPE_READY_TO_DLOAD; + + core->cpe_handle = cpe_svc_initialize(NULL, &core->cdc_info, + params->cpe_svc_params); + if (!core->cpe_handle) { + dev_err(core->dev, + "%s: failed to initialize cpe services\n", + __func__); + goto fail_cpe_initialize; + } + + core->cpe_reg_handle = cpe_svc_register(core->cpe_handle, + wcd_cpe_svc_event_cb, + CPE_SVC_ONLINE | CPE_SVC_OFFLINE | + CPE_SVC_BOOT | + CPE_SVC_CMI_CLIENTS_DEREG, + "codec cpe handler"); + if (!core->cpe_reg_handle) { + dev_err(core->dev, + "%s: failed to register cpe service\n", + __func__); + goto fail_cpe_register; + } + + card = codec->component.card->snd_card; + snprintf(proc_name, (sizeof("cpe") + sizeof("_state") + + sizeof(id) - 2), "%s%d%s", cpe_name, id, state_name); + entry = snd_info_create_card_entry(card, proc_name, + card->proc_root); + if (entry) { + core->ssr_entry.entry = entry; + core->ssr_entry.offline = 1; + entry->size = WCD_CPE_STATE_MAX_LEN; + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->c.ops = &wcd_cpe_state_proc_ops; + entry->private_data = core; + ret = snd_info_register(entry); + if (ret < 0) { + dev_err(core->dev, + "%s: snd_info_register failed (%d)\n", + __func__, ret); + snd_info_free_entry(entry); + entry = NULL; + } + } else { + dev_err(core->dev, + "%s: Failed to create CPE SSR status entry\n", + __func__); + /* + * Even if SSR entry creation fails, continue + * with image download + */ + } + + core_d = core; + ret = wcd_cpe_cal_init(core); + if (IS_ERR_VALUE(ret)) { + dev_err(core->dev, + "%s: CPE calibration init failed, err = %d\n", + __func__, ret); + goto fail_cpe_reset; + } + + wcd_cpe_debugfs_init(core); + + wcd_cpe_sysfs_init(core, id); + + hw_info = cpe_svc_get_hw_cfg(core->cpe_handle); + if (!hw_info) { + dev_err(core->dev, + "%s: hw info not available\n", + __func__); + goto schedule_dload_work; + } else { + core->hw_info.dram_offset = hw_info->DRAM_offset; + core->hw_info.dram_size = hw_info->DRAM_size; + core->hw_info.iram_offset = hw_info->IRAM_offset; + core->hw_info.iram_size = hw_info->IRAM_size; + } + + /* Setup the ramdump device and buffer */ + core->cpe_ramdump_dev = create_ramdump_device("cpe", + core->dev); + if (!core->cpe_ramdump_dev) { + dev_err(core->dev, + "%s: Failed to create ramdump device\n", + __func__); + goto schedule_dload_work; + } + + arch_setup_dma_ops(core->dev, 0, 0, NULL, 0); + core->cpe_dump_v_addr = dma_alloc_coherent(core->dev, + core->hw_info.dram_size, + &core->cpe_dump_addr, + GFP_KERNEL); + if (!core->cpe_dump_v_addr) { + dev_err(core->dev, + "%s: Failed to alloc memory for cpe dump, size = %zd\n", + __func__, core->hw_info.dram_size); + goto schedule_dload_work; + } else { + memset(core->cpe_dump_v_addr, 0, core->hw_info.dram_size); + } + +schedule_dload_work: + core->ssr_type = WCD_CPE_INITIALIZED; + schedule_work(&core->load_fw_work); + return core; + +fail_cpe_reset: + cpe_svc_deregister(core->cpe_handle, core->cpe_reg_handle); + +fail_cpe_register: + cpe_svc_deinitialize(core->cpe_handle); + +fail_cpe_initialize: + kfree(core); + return NULL; +} +EXPORT_SYMBOL(wcd_cpe_init); + +/* + * wcd_cpe_cmi_lsm_callback: callback called from cpe services + * to notify command response for lsm + * service + * @param: param containing the response code and status + * + * This callback is registered with cpe services while registering + * the LSM service + */ +static void wcd_cpe_cmi_lsm_callback(const struct cmi_api_notification *param) +{ + struct cmi_hdr *hdr; + struct cpe_lsm_session *lsm_session; + u8 session_id; + + if (!param) { + pr_err("%s: param is null\n", __func__); + return; + } + + if (param->event != CMI_API_MSG) { + pr_err("%s: unhandled event 0x%x\n", __func__, param->event); + return; + } + + hdr = (struct cmi_hdr *) param->message; + session_id = CMI_HDR_GET_SESSION_ID(hdr); + + if (session_id > WCD_CPE_LSM_MAX_SESSIONS) { + pr_err("%s: invalid lsm session id = %d\n", + __func__, session_id); + return; + } + + lsm_session = lsm_sessions[session_id]; + + if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) { + + u8 *payload = ((u8 *)param->message) + (sizeof(struct cmi_hdr)); + u8 result = payload[0]; + + lsm_session->cmd_err_code = result; + complete(&lsm_session->cmd_comp); + + } else if (hdr->opcode == CPE_LSM_SESSION_CMDRSP_SHARED_MEM_ALLOC) { + + struct cpe_cmdrsp_shmem_alloc *cmdrsp_shmem_alloc = + (struct cpe_cmdrsp_shmem_alloc *) param->message; + + if (cmdrsp_shmem_alloc->addr == 0) { + pr_err("%s: Failed LSM shared mem alloc\n", __func__); + lsm_session->cmd_err_code = CMI_SHMEM_ALLOC_FAILED; + + } else { + + pr_debug("%s LSM shared mem addr = 0x%x\n", + __func__, cmdrsp_shmem_alloc->addr); + lsm_session->lsm_mem_handle = cmdrsp_shmem_alloc->addr; + lsm_session->cmd_err_code = 0; + } + + complete(&lsm_session->cmd_comp); + + } else if (hdr->opcode == CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2) { + + struct cpe_lsm_event_detect_v2 *event_detect_v2 = + (struct cpe_lsm_event_detect_v2 *) param->message; + + if (!lsm_session->priv_d) { + pr_err("%s: private data is not present\n", + __func__); + return; + } + + pr_debug("%s: event payload, status = %u, size = %u\n", + __func__, event_detect_v2->detection_status, + event_detect_v2->size); + + if (lsm_session->event_cb) + lsm_session->event_cb( + lsm_session->priv_d, + event_detect_v2->detection_status, + event_detect_v2->size, + event_detect_v2->payload); + } +} + +/* + * wcd_cpe_cmi_send_lsm_msg: send a message to lsm service + * @core: handle to cpe core + * @session: session on which to send the message + * @message: actual message containing header and payload + * + * Sends message to lsm service for specified session and wait + * for response back on the message. + * should be called after acquiring session specific mutex + */ +static int wcd_cpe_cmi_send_lsm_msg( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *message) +{ + int ret = 0; + struct cmi_hdr *hdr = message; + + pr_debug("%s: sending message with opcode 0x%x\n", + __func__, hdr->opcode); + + if (unlikely(!wcd_cpe_is_online_state(core))) { + dev_err(core->dev, + "%s: MSG not sent, CPE offline\n", + __func__); + goto done; + } + + if (CMI_HDR_GET_OBM_FLAG(hdr)) + wcd_cpe_bus_vote_max_bw(core, true); + + reinit_completion(&session->cmd_comp); + ret = cmi_send_msg(message); + if (ret) { + pr_err("%s: msg opcode (0x%x) send failed (%d)\n", + __func__, hdr->opcode, ret); + goto rel_bus_vote; + } + + ret = wait_for_completion_timeout(&session->cmd_comp, + CMI_CMD_TIMEOUT); + if (ret > 0) { + pr_debug("%s: command 0x%x, received response 0x%x\n", + __func__, hdr->opcode, session->cmd_err_code); + if (session->cmd_err_code == CMI_SHMEM_ALLOC_FAILED) + session->cmd_err_code = CPE_ENOMEMORY; + if (session->cmd_err_code > 0) + pr_err("%s: CPE returned error[%s]\n", + __func__, cpe_err_get_err_str( + session->cmd_err_code)); + ret = cpe_err_get_lnx_err_code(session->cmd_err_code); + goto rel_bus_vote; + } else { + pr_err("%s: command (0x%x) send timed out\n", + __func__, hdr->opcode); + ret = -ETIMEDOUT; + goto rel_bus_vote; + } + + +rel_bus_vote: + + if (CMI_HDR_GET_OBM_FLAG(hdr)) + wcd_cpe_bus_vote_max_bw(core, false); + +done: + return ret; +} + + +/* + * fill_cmi_header: fill the cmi header with specified values + * + * @hdr: header to be updated with values + * @session_id: session id of the header, + * in case of AFE service it is port_id + * @service_id: afe/lsm, etc + * @version: update the version field in header + * @payload_size: size of the payload following after header + * @opcode: opcode of the message + * @obm_flag: indicates if this header is for obm message + * + */ +static int fill_cmi_header(struct cmi_hdr *hdr, + u8 session_id, u8 service_id, + bool version, u8 payload_size, + u16 opcode, bool obm_flag) +{ + /* sanitize the data */ + if (!IS_VALID_SESSION_ID(session_id) || + !IS_VALID_SERVICE_ID(service_id) || + !IS_VALID_PLD_SIZE(payload_size)) { + pr_err("Invalid header creation request\n"); + return -EINVAL; + } + + CMI_HDR_SET_SESSION(hdr, session_id); + CMI_HDR_SET_SERVICE(hdr, service_id); + if (version) + CMI_HDR_SET_VERSION(hdr, 1); + else + CMI_HDR_SET_VERSION(hdr, 0); + + CMI_HDR_SET_PAYLOAD_SIZE(hdr, payload_size); + + hdr->opcode = opcode; + + if (obm_flag) + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_OUT_BAND); + else + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND); + + return 0; +} + +/* + * fill_lsm_cmd_header_v0_inband: + * Given the header, fill the header with information + * for lsm service, version 0 and inband message + * @hdr: the cmi header to be filled. + * @session_id: ID for the lsm session + * @payload_size: size for cmi message payload + * @opcode: opcode for cmi message + */ +static int fill_lsm_cmd_header_v0_inband(struct cmi_hdr *hdr, + u8 session_id, u8 payload_size, u16 opcode) +{ + return fill_cmi_header(hdr, session_id, + CMI_CPE_LSM_SERVICE_ID, false, + payload_size, opcode, false); +} + +/* + * wcd_cpe_is_valid_lsm_session: + * Check session parameters to identify validity for the sesion + * @core: handle to cpe core + * @session: handle to the lsm session + * @func: invoking function to be printed in error logs + */ +static int wcd_cpe_is_valid_lsm_session(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + const char *func) +{ + if (unlikely(IS_ERR_OR_NULL(core))) { + pr_err("%s: invalid handle to core\n", + func); + return -EINVAL; + } + + if (unlikely(IS_ERR_OR_NULL(session))) { + dev_err(core->dev, "%s: invalid session\n", + func); + return -EINVAL; + } + + if (session->id > WCD_CPE_LSM_MAX_SESSIONS) { + dev_err(core->dev, "%s: invalid session id (%u)\n", + func, session->id); + return -EINVAL; + } + + dev_dbg(core->dev, "%s: session_id = %u\n", + func, session->id); + return 0; +} + +static int wcd_cpe_cmd_lsm_open_tx_v2( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session) +{ + struct cpe_lsm_cmd_open_tx_v2 cmd_open_tx_v2; + struct cal_block_data *top_cal = NULL; + struct audio_cal_info_lsm_top *lsm_top; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + if (core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID] == NULL) { + dev_err(core->dev, + "%s: LSM_TOPOLOGY cal not allocated!\n", + __func__); + return -EINVAL; + } + + mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]->lock); + top_cal = cal_utils_get_only_cal_block( + core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]); + if (!top_cal) { + dev_err(core->dev, + "%s: Failed to get LSM TOPOLOGY cal block\n", + __func__); + ret = -EINVAL; + goto unlock_cal_mutex; + } + + lsm_top = (struct audio_cal_info_lsm_top *) + top_cal->cal_info; + + if (!lsm_top) { + dev_err(core->dev, + "%s: cal_info for LSM_TOPOLOGY not found\n", + __func__); + ret = -EINVAL; + goto unlock_cal_mutex; + } + + dev_dbg(core->dev, + "%s: topology_id = 0x%x, acdb_id = 0x%x, app_type = 0x%x\n", + __func__, lsm_top->topology, lsm_top->acdb_id, + lsm_top->app_type); + + if (lsm_top->topology == 0) { + dev_err(core->dev, + "%s: topology id not sent for app_type 0x%x\n", + __func__, lsm_top->app_type); + ret = -EINVAL; + goto unlock_cal_mutex; + } + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_open_tx_v2, 0, sizeof(struct cpe_lsm_cmd_open_tx_v2)); + if (fill_lsm_cmd_header_v0_inband(&cmd_open_tx_v2.hdr, + session->id, OPEN_V2_CMD_PAYLOAD_SIZE, + CPE_LSM_SESSION_CMD_OPEN_TX_V2)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_open_tx_v2.topology_id = lsm_top->topology; + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_open_tx_v2); + if (ret) + dev_err(core->dev, + "%s: failed to send open_tx_v2 cmd, err = %d\n", + __func__, ret); + else + session->is_topology_used = true; +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + +unlock_cal_mutex: + mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]->lock); + return ret; +} + +/* + * wcd_cpe_cmd_lsm_open_tx: compose and send lsm open command + * @core_handle: handle to cpe core + * @session: session for which the command needs to be sent + * @app_id: application id part of the command + * @sample_rate: sample rate for this session + */ +static int wcd_cpe_cmd_lsm_open_tx(void *core_handle, + struct cpe_lsm_session *session, + u16 app_id, u16 sample_rate) +{ + struct cpe_lsm_cmd_open_tx cmd_open_tx; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + /* Try to open with topology first */ + ret = wcd_cpe_cmd_lsm_open_tx_v2(core, session); + if (!ret) + goto done; + + dev_dbg(core->dev, "%s: Try open_tx without topology\n", + __func__); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_open_tx, 0, sizeof(struct cpe_lsm_cmd_open_tx)); + if (fill_lsm_cmd_header_v0_inband(&cmd_open_tx.hdr, + session->id, OPEN_CMD_PAYLOAD_SIZE, + CPE_LSM_SESSION_CMD_OPEN_TX)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_open_tx.app_id = app_id; + cmd_open_tx.sampling_rate = sample_rate; + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_open_tx); + if (ret) + dev_err(core->dev, + "%s: failed to send open_tx cmd, err = %d\n", + __func__, ret); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +done: + return ret; +} + +/* + * wcd_cpe_cmd_close_tx: compose and send lsm close command + * @core_handle: handle to cpe core + * @session: session for which the command needs to be sent + */ +static int wcd_cpe_cmd_lsm_close_tx(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cmi_hdr cmd_close_tx; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_close_tx, 0, sizeof(cmd_close_tx)); + if (fill_lsm_cmd_header_v0_inband(&cmd_close_tx, session->id, + 0, CPE_LSM_SESSION_CMD_CLOSE_TX)) { + ret = -EINVAL; + goto end_ret; + } + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_close_tx); + if (ret) + dev_err(core->dev, + "%s: lsm close_tx cmd failed, err = %d\n", + __func__, ret); + else + session->is_topology_used = false; +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_cmd_shmem_alloc: compose and send lsm shared + * memory allocation command + * @core_handle: handle to cpe core + * @session: session for which the command needs to be sent + * @size: size of memory to be allocated + */ +static int wcd_cpe_cmd_lsm_shmem_alloc(void *core_handle, + struct cpe_lsm_session *session, + u32 size) +{ + struct cpe_cmd_shmem_alloc cmd_shmem_alloc; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_shmem_alloc, 0, sizeof(cmd_shmem_alloc)); + if (fill_lsm_cmd_header_v0_inband(&cmd_shmem_alloc.hdr, session->id, + SHMEM_ALLOC_CMD_PLD_SIZE, + CPE_LSM_SESSION_CMD_SHARED_MEM_ALLOC)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_shmem_alloc.size = size; + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_shmem_alloc); + if (ret) + dev_err(core->dev, + "%s: lsm_shmem_alloc cmd send fail, %d\n", + __func__, ret); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_cmd_lsm_shmem_dealloc: deallocate the shared memory + * for the specified session + * @core_handle: handle to cpe core + * @session: session for which memory needs to be deallocated. + */ +static int wcd_cpe_cmd_lsm_shmem_dealloc(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cpe_cmd_shmem_dealloc cmd_dealloc; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_dealloc, 0, sizeof(cmd_dealloc)); + if (fill_lsm_cmd_header_v0_inband(&cmd_dealloc.hdr, session->id, + SHMEM_DEALLOC_CMD_PLD_SIZE, + CPE_LSM_SESSION_CMD_SHARED_MEM_DEALLOC)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_dealloc.addr = session->lsm_mem_handle; + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_dealloc); + if (ret) { + dev_err(core->dev, + "%s: lsm_shmem_dealloc cmd failed, rc %d\n", + __func__, ret); + goto end_ret; + } + + memset(&session->lsm_mem_handle, 0, + sizeof(session->lsm_mem_handle)); + +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_send_lsm_cal: send the calibration for lsm service + * from acdb to the cpe + * @core: handle to cpe core + * @session: session for which the calibration needs to be set. + */ +static int wcd_cpe_send_lsm_cal( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session) +{ + + u8 *msg_pld; + struct cmi_hdr *hdr; + struct cal_block_data *lsm_cal = NULL; + void *inb_msg; + int rc = 0; + + if (core->cal_data[WCD_CPE_LSM_CAL_LSM] == NULL) { + pr_err("%s: LSM cal not allocated!\n", __func__); + return -EINVAL; + } + + mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_LSM]->lock); + lsm_cal = cal_utils_get_only_cal_block( + core->cal_data[WCD_CPE_LSM_CAL_LSM]); + if (!lsm_cal) { + pr_err("%s: failed to get lsm cal block\n", __func__); + rc = -EINVAL; + goto unlock_cal_mutex; + } + + if (lsm_cal->cal_data.size == 0) { + dev_dbg(core->dev, "%s: No LSM cal to send\n", + __func__); + rc = 0; + goto unlock_cal_mutex; + } + + inb_msg = kzalloc(sizeof(struct cmi_hdr) + lsm_cal->cal_data.size, + GFP_KERNEL); + if (!inb_msg) { + rc = -ENOMEM; + goto unlock_cal_mutex; + } + + hdr = (struct cmi_hdr *) inb_msg; + + rc = fill_lsm_cmd_header_v0_inband(hdr, session->id, + lsm_cal->cal_data.size, + CPE_LSM_SESSION_CMD_SET_PARAMS); + if (rc) { + pr_err("%s: invalid params for header, err = %d\n", + __func__, rc); + goto free_msg; + } + + msg_pld = ((u8 *) inb_msg) + sizeof(struct cmi_hdr); + memcpy(msg_pld, lsm_cal->cal_data.kvaddr, + lsm_cal->cal_data.size); + + rc = wcd_cpe_cmi_send_lsm_msg(core, session, inb_msg); + if (rc) + pr_err("%s: acdb lsm_params send failed, err = %d\n", + __func__, rc); + +free_msg: + kfree(inb_msg); + +unlock_cal_mutex: + mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_LSM]->lock); + return rc; + +} + +static void wcd_cpe_set_param_data(struct cpe_param_data *param_d, + struct cpe_lsm_ids *ids, u32 p_size, + u32 set_param_cmd) +{ + param_d->module_id = ids->module_id; + param_d->param_id = ids->param_id; + + switch (set_param_cmd) { + case CPE_LSM_SESSION_CMD_SET_PARAMS_V2: + param_d->p_size.param_size = p_size; + break; + case CPE_LSM_SESSION_CMD_SET_PARAMS: + default: + param_d->p_size.sr.param_size = + (u16) p_size; + param_d->p_size.sr.reserved = 0; + break; + } +} + +static int wcd_cpe_send_param_epd_thres(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, struct cpe_lsm_ids *ids) +{ + struct snd_lsm_ep_det_thres *ep_det_data; + struct cpe_lsm_param_epd_thres epd_cmd; + struct cmi_hdr *msg_hdr = &epd_cmd.hdr; + struct cpe_param_data *param_d = + &epd_cmd.param; + int rc; + + memset(&epd_cmd, 0, sizeof(epd_cmd)); + ep_det_data = (struct snd_lsm_ep_det_thres *) data; + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_CMD_EPD_THRES_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + wcd_cpe_set_param_data(param_d, ids, + CPE_EPD_THRES_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + epd_cmd.minor_version = 1; + epd_cmd.epd_begin = ep_det_data->epd_begin; + epd_cmd.epd_end = ep_det_data->epd_end; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, &epd_cmd); + if (unlikely(rc)) + dev_err(core->dev, + "%s: set_param(EPD Threshold) failed, rc %dn", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + return rc; +} + +static int wcd_cpe_send_param_opmode(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, struct cpe_lsm_ids *ids) +{ + struct snd_lsm_detect_mode *opmode_d; + struct cpe_lsm_param_opmode opmode_cmd; + struct cmi_hdr *msg_hdr = &opmode_cmd.hdr; + struct cpe_param_data *param_d = + &opmode_cmd.param; + int rc; + + memset(&opmode_cmd, 0, sizeof(opmode_cmd)); + opmode_d = (struct snd_lsm_detect_mode *) data; + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_CMD_OPMODE_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + wcd_cpe_set_param_data(param_d, ids, + CPE_OPMODE_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + opmode_cmd.minor_version = 1; + if (opmode_d->mode == LSM_MODE_KEYWORD_ONLY_DETECTION) + opmode_cmd.mode = 1; + else + opmode_cmd.mode = 3; + + if (opmode_d->detect_failure) + opmode_cmd.mode |= 0x04; + + opmode_cmd.reserved = 0; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, &opmode_cmd); + if (unlikely(rc)) + dev_err(core->dev, + "%s: set_param(operation_mode) failed, rc %dn", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + return rc; +} + +static int wcd_cpe_send_param_gain(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, struct cpe_lsm_ids *ids) +{ + struct snd_lsm_gain *gain_d; + struct cpe_lsm_param_gain gain_cmd; + struct cmi_hdr *msg_hdr = &gain_cmd.hdr; + struct cpe_param_data *param_d = + &gain_cmd.param; + int rc; + + memset(&gain_cmd, 0, sizeof(gain_cmd)); + gain_d = (struct snd_lsm_gain *) data; + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_CMD_GAIN_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + wcd_cpe_set_param_data(param_d, ids, + CPE_GAIN_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + gain_cmd.minor_version = 1; + gain_cmd.gain = gain_d->gain; + gain_cmd.reserved = 0; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, &gain_cmd); + if (unlikely(rc)) + dev_err(core->dev, + "%s: set_param(lsm_gain) failed, rc %dn", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + return rc; +} + +static int wcd_cpe_send_param_connectport(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, struct cpe_lsm_ids *ids, u16 port_id) +{ + struct cpe_lsm_param_connectport con_port_cmd; + struct cmi_hdr *msg_hdr = &con_port_cmd.hdr; + struct cpe_param_data *param_d = + &con_port_cmd.param; + int rc; + + memset(&con_port_cmd, 0, sizeof(con_port_cmd)); + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_CMD_CONNECTPORT_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + wcd_cpe_set_param_data(param_d, ids, + CPE_CONNECTPORT_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + con_port_cmd.minor_version = 1; + con_port_cmd.afe_port_id = port_id; + con_port_cmd.reserved = 0; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, &con_port_cmd); + if (unlikely(rc)) + dev_err(core->dev, + "%s: set_param(connect_port) failed, rc %dn", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + return rc; +} + +static int wcd_cpe_send_param_conf_levels( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + struct cpe_lsm_ids *ids) +{ + struct cpe_lsm_conf_level conf_level_data; + struct cmi_hdr *hdr = &(conf_level_data.hdr); + struct cpe_param_data *param_d = &(conf_level_data.param); + u8 pld_size = 0; + u8 pad_bytes = 0; + void *message; + int ret = 0; + + memset(&conf_level_data, 0, sizeof(conf_level_data)); + + pld_size = (sizeof(struct cpe_lsm_conf_level) - sizeof(struct cmi_hdr)); + pld_size += session->num_confidence_levels; + pad_bytes = ((4 - (pld_size % 4)) % 4); + pld_size += pad_bytes; + + fill_cmi_header(hdr, session->id, CMI_CPE_LSM_SERVICE_ID, + false, pld_size, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2, false); + + wcd_cpe_set_param_data(param_d, ids, + pld_size - sizeof(struct cpe_param_data), + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + conf_level_data.num_active_models = session->num_confidence_levels; + + message = kzalloc(sizeof(struct cpe_lsm_conf_level) + + conf_level_data.num_active_models + pad_bytes, + GFP_KERNEL); + if (!message) { + pr_err("%s: no memory for conf_level\n", __func__); + return -ENOMEM; + } + + memcpy(message, &conf_level_data, + sizeof(struct cpe_lsm_conf_level)); + memcpy(((u8 *) message) + sizeof(struct cpe_lsm_conf_level), + session->conf_levels, conf_level_data.num_active_models); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, message); + if (ret) + pr_err("%s: lsm_set_conf_levels failed, err = %d\n", + __func__, ret); + kfree(message); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +static int wcd_cpe_send_param_snd_model(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, struct cpe_lsm_ids *ids) +{ + int ret = 0; + struct cmi_obm_msg obm_msg; + struct cpe_param_data *param_d; + + + ret = fill_cmi_header(&obm_msg.hdr, session->id, + CMI_CPE_LSM_SERVICE_ID, 0, 20, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2, true); + if (ret) { + dev_err(core->dev, + "%s: Invalid parameters, rc = %d\n", + __func__, ret); + goto err_ret; + } + + obm_msg.pld.version = 0; + obm_msg.pld.size = session->snd_model_size; + obm_msg.pld.data_ptr.kvaddr = session->snd_model_data; + obm_msg.pld.mem_handle = session->lsm_mem_handle; + + param_d = (struct cpe_param_data *) session->snd_model_data; + wcd_cpe_set_param_data(param_d, ids, + (session->snd_model_size - sizeof(*param_d)), + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &obm_msg); + if (ret) + dev_err(core->dev, + "%s: snd_model_register failed, %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + +err_ret: + return ret; +} + +static int wcd_cpe_send_param_dereg_model( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + struct cpe_lsm_ids *ids) +{ + struct cmi_hdr *hdr; + struct cpe_param_data *param_d; + u8 *message; + u32 pld_size; + int rc = 0; + + pld_size = sizeof(*hdr) + sizeof(*param_d); + + message = kzalloc(pld_size, GFP_KERNEL); + if (!message) + return -ENOMEM; + + hdr = (struct cmi_hdr *) message; + param_d = (struct cpe_param_data *) + (((u8 *) message) + sizeof(*hdr)); + + if (fill_lsm_cmd_header_v0_inband(hdr, + session->id, + sizeof(*param_d), + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + wcd_cpe_set_param_data(param_d, ids, 0, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, message); + if (rc) + dev_err(core->dev, + "%s: snd_model_deregister failed, %d\n", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + kfree(message); + return rc; +} + +static int wcd_cpe_send_custom_param( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, u32 msg_size) +{ + u8 *msg; + struct cmi_hdr *hdr; + u8 *msg_pld; + int rc; + + if (msg_size > CMI_INBAND_MESSAGE_SIZE) { + dev_err(core->dev, + "%s: out of band custom params not supported\n", + __func__); + return -EINVAL; + } + + msg = kzalloc(sizeof(*hdr) + msg_size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = (struct cmi_hdr *) msg; + msg_pld = msg + sizeof(struct cmi_hdr); + + if (fill_lsm_cmd_header_v0_inband(hdr, + session->id, + msg_size, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + memcpy(msg_pld, data, msg_size); + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, msg); + if (rc) + dev_err(core->dev, + "%s: custom params send failed, err = %d\n", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + kfree(msg); + return rc; +} + +static int wcd_cpe_set_one_param(void *core_handle, + struct cpe_lsm_session *session, struct lsm_params_info *p_info, + void *data, enum LSM_PARAM_TYPE param_type) +{ + struct wcd_cpe_core *core = core_handle; + int rc = 0; + struct cpe_lsm_ids ids; + + memset(&ids, 0, sizeof(ids)); + ids.module_id = p_info->module_id; + ids.param_id = p_info->param_id; + + switch (param_type) { + case LSM_ENDPOINT_DETECT_THRESHOLD: + rc = wcd_cpe_send_param_epd_thres(core, session, + data, &ids); + break; + case LSM_OPERATION_MODE: { + struct cpe_lsm_ids connectport_ids; + + rc = wcd_cpe_send_param_opmode(core, session, + data, &ids); + if (rc) + break; + + connectport_ids.module_id = LSM_MODULE_ID_FRAMEWORK; + connectport_ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT; + + rc = wcd_cpe_send_param_connectport(core, session, NULL, + &connectport_ids, CPE_AFE_PORT_1_TX); + if (rc) + dev_err(core->dev, + "%s: send_param_connectport failed, err %d\n", + __func__, rc); + break; + } + case LSM_GAIN: + rc = wcd_cpe_send_param_gain(core, session, data, &ids); + break; + case LSM_MIN_CONFIDENCE_LEVELS: + rc = wcd_cpe_send_param_conf_levels(core, session, &ids); + break; + case LSM_REG_SND_MODEL: + rc = wcd_cpe_send_param_snd_model(core, session, &ids); + break; + case LSM_DEREG_SND_MODEL: + rc = wcd_cpe_send_param_dereg_model(core, session, &ids); + break; + case LSM_CUSTOM_PARAMS: + rc = wcd_cpe_send_custom_param(core, session, + data, p_info->param_size); + break; + default: + pr_err("%s: wrong param_type 0x%x\n", + __func__, p_info->param_type); + } + + if (rc) + dev_err(core->dev, + "%s: send_param(%d) failed, err %d\n", + __func__, p_info->param_type, rc); + return rc; +} + +/* + * wcd_cpe_lsm_set_params: set the parameters for lsm service + * @core: handle to cpe core + * @session: session for which the parameters are to be set + * @detect_mode: mode for detection + * @detect_failure: flag indicating failure detection enabled/disabled + * + */ +static int wcd_cpe_lsm_set_params(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + enum lsm_detection_mode detect_mode, bool detect_failure) +{ + struct cpe_lsm_ids ids; + struct snd_lsm_detect_mode det_mode; + + int ret = 0; + + /* Send lsm calibration */ + ret = wcd_cpe_send_lsm_cal(core, session); + if (ret) { + pr_err("%s: fail to sent acdb cal, err = %d", + __func__, ret); + goto err_ret; + } + + /* Send operation mode */ + ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP; + ids.param_id = CPE_LSM_PARAM_ID_OPERATION_MODE; + det_mode.mode = detect_mode; + det_mode.detect_failure = detect_failure; + ret = wcd_cpe_send_param_opmode(core, session, + &det_mode, &ids); + if (ret) + dev_err(core->dev, + "%s: Failed to set opmode, err=%d\n", + __func__, ret); + +err_ret: + return ret; +} + +static int wcd_cpe_lsm_set_data(void *core_handle, + struct cpe_lsm_session *session, + enum lsm_detection_mode detect_mode, + bool detect_failure) +{ + struct wcd_cpe_core *core = core_handle; + struct cpe_lsm_ids ids; + int ret = 0; + + if (session->num_confidence_levels > 0) { + ret = wcd_cpe_lsm_set_params(core, session, detect_mode, + detect_failure); + if (ret) { + dev_err(core->dev, + "%s: lsm set params failed, rc = %d\n", + __func__, ret); + goto err_ret; + } + + ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP; + ids.param_id = CPE_LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS; + ret = wcd_cpe_send_param_conf_levels(core, session, &ids); + if (ret) { + dev_err(core->dev, + "%s: lsm confidence levels failed, rc = %d\n", + __func__, ret); + goto err_ret; + } + } else { + dev_dbg(core->dev, + "%s: no conf levels to set\n", + __func__); + } + +err_ret: + return ret; +} + +/* + * wcd_cpe_lsm_reg_snd_model: register the sound model for listen + * @session: session for which to register the sound model + * @detect_mode: detection mode, user dependent/independent + * @detect_failure: flag to indicate if failure detection is enabled + * + * The memory required for sound model should be pre-allocated on CPE + * before this function is invoked. + */ +static int wcd_cpe_lsm_reg_snd_model(void *core_handle, + struct cpe_lsm_session *session, + enum lsm_detection_mode detect_mode, + bool detect_failure) +{ + int ret = 0; + struct cmi_obm_msg obm_msg; + struct wcd_cpe_core *core = core_handle; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + ret = wcd_cpe_lsm_set_data(core_handle, session, + detect_mode, detect_failure); + if (ret) { + dev_err(core->dev, + "%s: fail to set lsm data, err = %d\n", + __func__, ret); + return ret; + } + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + ret = fill_cmi_header(&obm_msg.hdr, session->id, + CMI_CPE_LSM_SERVICE_ID, 0, 20, + CPE_LSM_SESSION_CMD_REGISTER_SOUND_MODEL, true); + if (ret) { + dev_err(core->dev, + "%s: Invalid parameters, rc = %d\n", + __func__, ret); + goto err_ret; + } + + obm_msg.pld.version = 0; + obm_msg.pld.size = session->snd_model_size; + obm_msg.pld.data_ptr.kvaddr = session->snd_model_data; + obm_msg.pld.mem_handle = session->lsm_mem_handle; + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &obm_msg); + if (ret) + dev_err(core->dev, + "%s: snd_model_register failed, %d\n", + __func__, ret); +err_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_lsm_dereg_snd_model: deregister the sound model for listen + * @core_handle: handle to cpe core + * @session: session for which to deregister the sound model + * + */ +static int wcd_cpe_lsm_dereg_snd_model(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cmi_hdr cmd_dereg_snd_model; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_dereg_snd_model, 0, sizeof(cmd_dereg_snd_model)); + if (fill_lsm_cmd_header_v0_inband(&cmd_dereg_snd_model, session->id, + 0, CPE_LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL)) { + ret = -EINVAL; + goto end_ret; + } + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_dereg_snd_model); + if (ret) + dev_err(core->dev, + "%s: failed to send dereg_snd_model cmd\n", + __func__); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_lsm_get_afe_out_port_id: get afe output port id + * @core_handle: handle to the CPE core + * @session: session for which port id needs to get + */ +static int wcd_cpe_lsm_get_afe_out_port_id(void *core_handle, + struct cpe_lsm_session *session) +{ + struct wcd_cpe_core *core = core_handle; + struct snd_soc_codec *codec; + int rc = 0; + + if (!core || !core->codec) { + pr_err("%s: Invalid handle to %s\n", + __func__, + (!core) ? "core" : "codec"); + rc = -EINVAL; + goto done; + } + + if (!session) { + dev_err(core->dev, "%s: Invalid session\n", + __func__); + rc = -EINVAL; + goto done; + } + + if (!core->cpe_cdc_cb || + !core->cpe_cdc_cb->get_afe_out_port_id) { + session->afe_out_port_id = WCD_CPE_AFE_OUT_PORT_2; + dev_dbg(core->dev, + "%s: callback not defined, default port_id = %d\n", + __func__, session->afe_out_port_id); + goto done; + } + + codec = core->codec; + rc = core->cpe_cdc_cb->get_afe_out_port_id(codec, + &session->afe_out_port_id); + if (rc) { + dev_err(core->dev, + "%s: failed to get port id, err = %d\n", + __func__, rc); + goto done; + } + dev_dbg(core->dev, "%s: port_id: %d\n", __func__, + session->afe_out_port_id); + +done: + return rc; +} + +/* + * wcd_cpe_cmd_lsm_start: send the start command to lsm + * @core_handle: handle to the CPE core + * @session: session for which start command to be sent + * + */ +static int wcd_cpe_cmd_lsm_start(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cmi_hdr cmd_lsm_start; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_lsm_start, 0, sizeof(struct cmi_hdr)); + if (fill_lsm_cmd_header_v0_inband(&cmd_lsm_start, session->id, 0, + CPE_LSM_SESSION_CMD_START)) { + ret = -EINVAL; + goto end_ret; + } + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_lsm_start); + if (ret) + dev_err(core->dev, "failed to send lsm_start cmd\n"); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_cmd_lsm_stop: send the stop command for LSM service + * @core_handle: handle to the cpe core + * @session: session for which stop command to be sent + * + */ +static int wcd_cpe_cmd_lsm_stop(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cmi_hdr cmd_lsm_stop; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_lsm_stop, 0, sizeof(struct cmi_hdr)); + if (fill_lsm_cmd_header_v0_inband(&cmd_lsm_stop, session->id, 0, + CPE_LSM_SESSION_CMD_STOP)) { + ret = -EINVAL; + goto end_ret; + } + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_lsm_stop); + if (ret) + dev_err(core->dev, + "%s: failed to send lsm_stop cmd\n", + __func__); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; + +} + +/* + * wcd_cpe_alloc_lsm_session: allocate a lsm session + * @core: handle to wcd_cpe_core + * @lsm_priv_d: lsm private data + */ +static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( + void *core_handle, void *client_data, + void (*event_cb)(void *, u8, u8, u8 *)) +{ + struct cpe_lsm_session *session; + int i, session_id = -1; + struct wcd_cpe_core *core = core_handle; + bool afe_register_service = false; + int ret = 0; + + /* + * Even if multiple listen sessions can be + * allocated, the AFE service registration + * should be done only once as CPE can only + * have one instance of AFE service. + * + * If this is the first session to be allocated, + * only then register the afe service. + */ + if (!wcd_cpe_lsm_session_active()) + afe_register_service = true; + + for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++) { + if (!lsm_sessions[i]) { + session_id = i; + break; + } + } + + if (session_id < 0) { + dev_err(core->dev, + "%s: max allowed sessions already allocated\n", + __func__); + return NULL; + } + + ret = wcd_cpe_vote(core, true); + if (ret) { + dev_err(core->dev, + "%s: Failed to enable cpe, err = %d\n", + __func__, ret); + return NULL; + } + + session = kzalloc(sizeof(struct cpe_lsm_session), GFP_KERNEL); + if (!session) + goto err_session_alloc; + + session->id = session_id; + session->event_cb = event_cb; + session->cmi_reg_handle = cmi_register(wcd_cpe_cmi_lsm_callback, + CMI_CPE_LSM_SERVICE_ID); + if (!session->cmi_reg_handle) { + dev_err(core->dev, + "%s: Failed to register LSM service with CMI\n", + __func__); + goto err_ret; + } + session->priv_d = client_data; + mutex_init(&session->lsm_lock); + if (afe_register_service) { + /* Register for AFE Service */ + core->cmi_afe_handle = cmi_register(wcd_cpe_cmi_afe_cb, + CMI_CPE_AFE_SERVICE_ID); + wcd_cpe_initialize_afe_port_data(); + if (!core->cmi_afe_handle) { + dev_err(core->dev, + "%s: Failed to register AFE service with CMI\n", + __func__); + goto err_afe_svc_reg; + } + + /* Once AFE service is registered, send the mode command */ + ret = wcd_cpe_afe_svc_cmd_mode(core, + AFE_SVC_EXPLICIT_PORT_START); + if (ret) + goto err_afe_mode_cmd; + } + + session->lsm_mem_handle = 0; + init_completion(&session->cmd_comp); + + lsm_sessions[session_id] = session; + return session; + +err_afe_mode_cmd: + cmi_deregister(core->cmi_afe_handle); + +err_afe_svc_reg: + cmi_deregister(session->cmi_reg_handle); + mutex_destroy(&session->lsm_lock); + +err_ret: + kfree(session); + +err_session_alloc: + wcd_cpe_vote(core, false); + return NULL; +} + +/* + * wcd_cpe_lsm_config_lab_latency: send lab latency value + * @core: handle to wcd_cpe_core + * @session: lsm session + * @latency: the value of latency for lab setup in msec + */ +static int wcd_cpe_lsm_config_lab_latency( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + u32 latency) +{ + int ret = 0, pld_size = CPE_PARAM_LSM_LAB_LATENCY_SIZE; + struct cpe_lsm_lab_latency_config cpe_lab_latency; + struct cpe_lsm_lab_config *lab_lat = &cpe_lab_latency.latency_cfg; + struct cpe_param_data *param_d = &lab_lat->param; + struct cpe_lsm_ids ids; + + if (fill_lsm_cmd_header_v0_inband(&cpe_lab_latency.hdr, session->id, + (u8) pld_size, CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + pr_err("%s: Failed to create header\n", __func__); + return -EINVAL; + } + if (latency == 0x00 || latency > WCD_CPE_LAB_MAX_LATENCY) { + pr_err("%s: Invalid latency %u\n", + __func__, latency); + return -EINVAL; + } + + lab_lat->latency = latency; + lab_lat->minor_ver = 1; + ids.module_id = CPE_LSM_MODULE_ID_LAB; + ids.param_id = CPE_LSM_PARAM_ID_LAB_CONFIG; + wcd_cpe_set_param_data(param_d, &ids, + PARAM_SIZE_LSM_LATENCY_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + pr_debug("%s: Module 0x%x Param 0x%x size %zu pld_size 0x%x\n", + __func__, lab_lat->param.module_id, + lab_lat->param.param_id, PARAM_SIZE_LSM_LATENCY_SIZE, + pld_size); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cpe_lab_latency); + if (ret != 0) + pr_err("%s: lsm_set_params failed, error = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_lsm_lab_control: enable/disable lab + * @core: handle to wcd_cpe_core + * @session: lsm session + * @enable: Indicates whether to enable / disable lab + */ +static int wcd_cpe_lsm_lab_control( + void *core_handle, + struct cpe_lsm_session *session, + bool enable) +{ + struct wcd_cpe_core *core = core_handle; + int ret = 0, pld_size = CPE_PARAM_SIZE_LSM_LAB_CONTROL; + struct cpe_lsm_control_lab cpe_lab_enable; + struct cpe_lsm_lab_enable *lab_enable = &cpe_lab_enable.lab_enable; + struct cpe_param_data *param_d = &lab_enable->param; + struct cpe_lsm_ids ids; + + pr_debug("%s: enter payload_size = %d Enable %d\n", + __func__, pld_size, enable); + + if (fill_lsm_cmd_header_v0_inband(&cpe_lab_enable.hdr, session->id, + (u8) pld_size, CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + return -EINVAL; + } + if (enable == true) + lab_enable->enable = 1; + else + lab_enable->enable = 0; + + ids.module_id = CPE_LSM_MODULE_ID_LAB; + ids.param_id = CPE_LSM_PARAM_ID_LAB_ENABLE; + wcd_cpe_set_param_data(param_d, &ids, + PARAM_SIZE_LSM_CONTROL_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + pr_debug("%s: Module 0x%x, Param 0x%x size %zu pld_size 0x%x\n", + __func__, lab_enable->param.module_id, + lab_enable->param.param_id, PARAM_SIZE_LSM_CONTROL_SIZE, + pld_size); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cpe_lab_enable); + if (ret != 0) { + pr_err("%s: lsm_set_params failed, error = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + goto done; + } + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + + if (lab_enable->enable) + ret = wcd_cpe_lsm_config_lab_latency(core, session, + WCD_CPE_LAB_MAX_LATENCY); +done: + return ret; +} + +/* + * wcd_cpe_lsm_eob: stop lab + * @core: handle to wcd_cpe_core + * @session: lsm session to be deallocated + */ +static int wcd_cpe_lsm_eob( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session) +{ + int ret = 0; + struct cmi_hdr lab_eob; + + if (fill_lsm_cmd_header_v0_inband(&lab_eob, session->id, + 0, CPE_LSM_SESSION_CMD_EOB)) { + return -EINVAL; + } + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &lab_eob); + if (ret != 0) + pr_err("%s: lsm_set_params failed\n", __func__); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + + return ret; +} + +/* + * wcd_cpe_dealloc_lsm_session: deallocate lsm session + * @core: handle to wcd_cpe_core + * @session: lsm session to be deallocated + */ +static int wcd_cpe_dealloc_lsm_session(void *core_handle, + struct cpe_lsm_session *session) +{ + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + if (!session) { + dev_err(core->dev, + "%s: Invalid lsm session\n", __func__); + return -EINVAL; + } + + dev_dbg(core->dev, "%s: session %d being deallocated\n", + __func__, session->id); + if (session->id > WCD_CPE_LSM_MAX_SESSIONS) { + dev_err(core->dev, + "%s: Wrong session id %d max allowed = %d\n", + __func__, session->id, + WCD_CPE_LSM_MAX_SESSIONS); + return -EINVAL; + } + + cmi_deregister(session->cmi_reg_handle); + mutex_destroy(&session->lsm_lock); + lsm_sessions[session->id] = NULL; + kfree(session); + + if (!wcd_cpe_lsm_session_active()) { + cmi_deregister(core->cmi_afe_handle); + core->cmi_afe_handle = NULL; + wcd_cpe_deinitialize_afe_port_data(); + } + + ret = wcd_cpe_vote(core, false); + if (ret) + dev_dbg(core->dev, + "%s: Failed to un-vote cpe, err = %d\n", + __func__, ret); + + return ret; +} + +static int wcd_cpe_lab_ch_setup(void *core_handle, + struct cpe_lsm_session *session, + enum wcd_cpe_event event) +{ + struct wcd_cpe_core *core = core_handle; + struct snd_soc_codec *codec; + int rc = 0; + u8 cpe_intr_bits; + + if (!core || !core->codec) { + pr_err("%s: Invalid handle to %s\n", + __func__, + (!core) ? "core" : "codec"); + rc = EINVAL; + goto done; + } + + if (!core->cpe_cdc_cb || + !core->cpe_cdc_cb->cdc_ext_clk || + !core->cpe_cdc_cb->lab_cdc_ch_ctl) { + dev_err(core->dev, + "%s: Invalid codec callbacks\n", + __func__); + rc = -EINVAL; + goto done; + } + + codec = core->codec; + dev_dbg(core->dev, + "%s: event = 0x%x\n", + __func__, event); + + switch (event) { + case WCD_CPE_PRE_ENABLE: + rc = core->cpe_cdc_cb->cdc_ext_clk(codec, true, false); + if (rc) { + dev_err(core->dev, + "%s: failed to enable cdc clk, err = %d\n", + __func__, rc); + goto done; + } + + rc = core->cpe_cdc_cb->lab_cdc_ch_ctl(codec, + true); + if (rc) { + dev_err(core->dev, + "%s: failed to enable cdc port, err = %d\n", + __func__, rc); + rc = core->cpe_cdc_cb->cdc_ext_clk(codec, false, false); + goto done; + } + + break; + + case WCD_CPE_POST_ENABLE: + rc = cpe_svc_toggle_lab(core->cpe_handle, true); + if (rc) + dev_err(core->dev, + "%s: Failed to enable lab\n", __func__); + break; + + case WCD_CPE_PRE_DISABLE: + /* + * Mask the non-fatal interrupts in CPE as they will + * be generated during lab teardown and may flood. + */ + cpe_intr_bits = ~(core->irq_info.cpe_fatal_irqs & 0xFF); + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_MASK, + &cpe_intr_bits); + + rc = core->cpe_cdc_cb->lab_cdc_ch_ctl(codec, + false); + if (rc) + dev_err(core->dev, + "%s: failed to disable cdc port, err = %d\n", + __func__, rc); + break; + + case WCD_CPE_POST_DISABLE: + rc = wcd_cpe_lsm_eob(core, session); + if (rc) + dev_err(core->dev, + "%s: eob send failed, err = %d\n", + __func__, rc); + + /* Continue teardown even if eob failed */ + rc = cpe_svc_toggle_lab(core->cpe_handle, false); + if (rc) + dev_err(core->dev, + "%s: Failed to disable lab\n", __func__); + + /* Continue with disabling even if toggle lab fails */ + rc = core->cpe_cdc_cb->cdc_ext_clk(codec, false, false); + if (rc) + dev_err(core->dev, + "%s: failed to disable cdc clk, err = %d\n", + __func__, rc); + + /* Unmask non-fatal CPE interrupts */ + cpe_intr_bits = ~(core->irq_info.cpe_fatal_irqs & 0xFF); + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_UNMASK, + &cpe_intr_bits); + break; + + default: + dev_err(core->dev, + "%s: Invalid event 0x%x\n", + __func__, event); + rc = -EINVAL; + break; + } + +done: + return rc; +} + +static int wcd_cpe_lsm_set_fmt_cfg(void *core_handle, + struct cpe_lsm_session *session) +{ + int ret; + struct cpe_lsm_output_format_cfg out_fmt_cfg; + struct wcd_cpe_core *core = core_handle; + + ret = wcd_cpe_is_valid_lsm_session(core, session, __func__); + if (ret) + goto done; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&out_fmt_cfg, 0, sizeof(out_fmt_cfg)); + if (fill_lsm_cmd_header_v0_inband(&out_fmt_cfg.hdr, + session->id, OUT_FMT_CFG_CMD_PAYLOAD_SIZE, + CPE_LSM_SESSION_CMD_TX_BUFF_OUTPUT_CONFIG)) { + ret = -EINVAL; + goto err_ret; + } + + out_fmt_cfg.format = session->out_fmt_cfg.format; + out_fmt_cfg.packing = session->out_fmt_cfg.pack_mode; + out_fmt_cfg.data_path_events = session->out_fmt_cfg.data_path_events; + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &out_fmt_cfg); + if (ret) + dev_err(core->dev, + "%s: lsm_set_output_format_cfg failed, err = %d\n", + __func__, ret); + +err_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +done: + return ret; +} + +static void wcd_cpe_snd_model_offset(void *core_handle, + struct cpe_lsm_session *session, size_t *offset) +{ + *offset = sizeof(struct cpe_param_data); +} + +static int wcd_cpe_lsm_set_media_fmt_params(void *core_handle, + struct cpe_lsm_session *session, + struct lsm_hw_params *param) +{ + struct cpe_lsm_media_fmt_param media_fmt; + struct cmi_hdr *msg_hdr = &media_fmt.hdr; + struct wcd_cpe_core *core = core_handle; + struct cpe_param_data *param_d = &media_fmt.param; + struct cpe_lsm_ids ids; + int ret; + + memset(&media_fmt, 0, sizeof(media_fmt)); + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_MEDIA_FMT_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + ret = -EINVAL; + goto done; + } + + memset(&ids, 0, sizeof(ids)); + ids.module_id = CPE_LSM_MODULE_FRAMEWORK; + ids.param_id = CPE_LSM_PARAM_ID_MEDIA_FMT; + + wcd_cpe_set_param_data(param_d, &ids, CPE_MEDIA_FMT_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + media_fmt.minor_version = 1; + media_fmt.sample_rate = param->sample_rate; + media_fmt.num_channels = param->num_chs; + media_fmt.bit_width = param->bit_width; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &media_fmt); + if (ret) + dev_err(core->dev, + "%s: Set_param(media_format) failed, err=%d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +done: + return ret; +} + +static int wcd_cpe_lsm_set_port(void *core_handle, + struct cpe_lsm_session *session, void *data) +{ + u32 port_id; + int ret; + struct cpe_lsm_ids ids; + struct wcd_cpe_core *core = core_handle; + + ret = wcd_cpe_is_valid_lsm_session(core, session, __func__); + if (ret) + goto done; + + if (!data) { + dev_err(core->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + port_id = *(u32 *)data; + dev_dbg(core->dev, "%s: port_id: %d\n", __func__, port_id); + + memset(&ids, 0, sizeof(ids)); + ids.module_id = LSM_MODULE_ID_FRAMEWORK; + ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT; + + ret = wcd_cpe_send_param_connectport(core, session, NULL, + &ids, port_id); + if (ret) + dev_err(core->dev, + "%s: send_param_connectport failed, err %d\n", + __func__, ret); +done: + return ret; +} + +/* + * wcd_cpe_get_lsm_ops: register lsm driver to codec + * @lsm_ops: structure with lsm callbacks + * @codec: codec to which this lsm driver is registered to + */ +int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops) +{ + lsm_ops->lsm_alloc_session = wcd_cpe_alloc_lsm_session; + lsm_ops->lsm_dealloc_session = wcd_cpe_dealloc_lsm_session; + lsm_ops->lsm_open_tx = wcd_cpe_cmd_lsm_open_tx; + lsm_ops->lsm_close_tx = wcd_cpe_cmd_lsm_close_tx; + lsm_ops->lsm_shmem_alloc = wcd_cpe_cmd_lsm_shmem_alloc; + lsm_ops->lsm_shmem_dealloc = wcd_cpe_cmd_lsm_shmem_dealloc; + lsm_ops->lsm_register_snd_model = wcd_cpe_lsm_reg_snd_model; + lsm_ops->lsm_deregister_snd_model = wcd_cpe_lsm_dereg_snd_model; + lsm_ops->lsm_get_afe_out_port_id = wcd_cpe_lsm_get_afe_out_port_id; + lsm_ops->lsm_start = wcd_cpe_cmd_lsm_start; + lsm_ops->lsm_stop = wcd_cpe_cmd_lsm_stop; + lsm_ops->lsm_lab_control = wcd_cpe_lsm_lab_control; + lsm_ops->lab_ch_setup = wcd_cpe_lab_ch_setup; + lsm_ops->lsm_set_data = wcd_cpe_lsm_set_data; + lsm_ops->lsm_set_fmt_cfg = wcd_cpe_lsm_set_fmt_cfg; + lsm_ops->lsm_set_one_param = wcd_cpe_set_one_param; + lsm_ops->lsm_get_snd_model_offset = wcd_cpe_snd_model_offset; + lsm_ops->lsm_set_media_fmt_params = wcd_cpe_lsm_set_media_fmt_params; + lsm_ops->lsm_set_port = wcd_cpe_lsm_set_port; + + return 0; +} +EXPORT_SYMBOL(wcd_cpe_get_lsm_ops); + +static int fill_afe_cmd_header(struct cmi_hdr *hdr, u8 port_id, + u16 opcode, u8 pld_size, + bool obm_flag) +{ + CMI_HDR_SET_SESSION(hdr, port_id); + CMI_HDR_SET_SERVICE(hdr, CMI_CPE_AFE_SERVICE_ID); + + CMI_HDR_SET_PAYLOAD_SIZE(hdr, pld_size); + + hdr->opcode = opcode; + + if (obm_flag) + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_OUT_BAND); + else + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND); + + return 0; +} + +/* + * wcd_cpe_cmi_send_afe_msg: send message to AFE service + * @core: wcd cpe core handle + * @port_cfg: configuration data for the afe port + * for which this message is to be sent + * @message: actual message with header and payload + * + * Port specific lock needs to be acquired before this + * function can be invoked + */ +static int wcd_cpe_cmi_send_afe_msg( + struct wcd_cpe_core *core, + struct wcd_cmi_afe_port_data *port_d, + void *message) +{ + int ret = 0; + struct cmi_hdr *hdr = message; + + pr_debug("%s: sending message with opcode 0x%x\n", + __func__, hdr->opcode); + + if (unlikely(!wcd_cpe_is_online_state(core))) { + dev_err(core->dev, "%s: CPE offline\n", __func__); + return 0; + } + + if (CMI_HDR_GET_OBM_FLAG(hdr)) + wcd_cpe_bus_vote_max_bw(core, true); + + ret = cmi_send_msg(message); + if (ret) { + pr_err("%s: cmd 0x%x send failed, err = %d\n", + __func__, hdr->opcode, ret); + goto rel_bus_vote; + } + + ret = wait_for_completion_timeout(&port_d->afe_cmd_complete, + CMI_CMD_TIMEOUT); + if (ret > 0) { + pr_debug("%s: command 0x%x, received response 0x%x\n", + __func__, hdr->opcode, port_d->cmd_result); + if (port_d->cmd_result == CMI_SHMEM_ALLOC_FAILED) + port_d->cmd_result = CPE_ENOMEMORY; + if (port_d->cmd_result > 0) + pr_err("%s: CPE returned error[%s]\n", + __func__, cpe_err_get_err_str( + port_d->cmd_result)); + ret = cpe_err_get_lnx_err_code(port_d->cmd_result); + goto rel_bus_vote; + } else { + pr_err("%s: command 0x%x send timed out\n", + __func__, hdr->opcode); + ret = -ETIMEDOUT; + goto rel_bus_vote; + } + +rel_bus_vote: + reinit_completion(&port_d->afe_cmd_complete); + + if (CMI_HDR_GET_OBM_FLAG(hdr)) + wcd_cpe_bus_vote_max_bw(core, false); + + return ret; +} + + + +/* + * wcd_cpe_afe_shmem_alloc: allocate the cpe memory for afe service + * @core: handle to cpe core + * @port_cfg: configuration data for the port which needs + * memory to be allocated on CPE + * @size: size of the memory to be allocated + */ +static int wcd_cpe_afe_shmem_alloc( + struct wcd_cpe_core *core, + struct wcd_cmi_afe_port_data *port_d, + u32 size) +{ + struct cpe_cmd_shmem_alloc cmd_shmem_alloc; + int ret = 0; + + pr_debug("%s: enter: size = %d\n", __func__, size); + + memset(&cmd_shmem_alloc, 0, sizeof(cmd_shmem_alloc)); + if (fill_afe_cmd_header(&cmd_shmem_alloc.hdr, port_d->port_id, + CPE_AFE_PORT_CMD_SHARED_MEM_ALLOC, + SHMEM_ALLOC_CMD_PLD_SIZE, false)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_shmem_alloc.size = size; + + ret = wcd_cpe_cmi_send_afe_msg(core, port_d, &cmd_shmem_alloc); + if (ret) { + pr_err("%s: afe_shmem_alloc fail,ret = %d\n", + __func__, ret); + goto end_ret; + } + + pr_debug("%s: completed %s, mem_handle = 0x%x\n", + __func__, "CPE_AFE_CMD_SHARED_MEM_ALLOC", + port_d->mem_handle); + +end_ret: + return ret; +} + +/* + * wcd_cpe_afe_shmem_dealloc: deallocate the cpe memory for + * afe service + * @core: handle to cpe core + * @port_d: configuration data for the port which needs + * memory to be deallocated on CPE + * The memory handle to be de-allocated is saved in the + * port configuration data + */ +static int wcd_cpe_afe_shmem_dealloc( + struct wcd_cpe_core *core, + struct wcd_cmi_afe_port_data *port_d) +{ + struct cpe_cmd_shmem_dealloc cmd_dealloc; + int ret = 0; + + pr_debug("%s: enter, port_id = %d\n", + __func__, port_d->port_id); + + memset(&cmd_dealloc, 0, sizeof(cmd_dealloc)); + if (fill_afe_cmd_header(&cmd_dealloc.hdr, port_d->port_id, + CPE_AFE_PORT_CMD_SHARED_MEM_DEALLOC, + SHMEM_DEALLOC_CMD_PLD_SIZE, false)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_dealloc.addr = port_d->mem_handle; + ret = wcd_cpe_cmi_send_afe_msg(core, port_d, &cmd_dealloc); + if (ret) { + pr_err("failed to send shmem_dealloc cmd\n"); + goto end_ret; + } + memset(&port_d->mem_handle, 0, + sizeof(port_d->mem_handle)); + +end_ret: + return ret; +} + +/* + * wcd_cpe_send_afe_cal: send the acdb calibration to AFE port + * @core: handle to cpe core + * @port_d: configuration data for the port for which the + * calibration needs to be appplied + */ +static int wcd_cpe_send_afe_cal(void *core_handle, + struct wcd_cmi_afe_port_data *port_d) +{ + + struct cal_block_data *afe_cal = NULL; + struct wcd_cpe_core *core = core_handle; + struct cmi_obm_msg obm_msg; + void *inb_msg = NULL; + void *msg; + int rc = 0; + bool is_obm_msg; + + if (core->cal_data[WCD_CPE_LSM_CAL_AFE] == NULL) { + pr_err("%s: LSM cal not allocated!\n", + __func__); + rc = -EINVAL; + goto rel_cal_mutex; + } + + mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_AFE]->lock); + afe_cal = cal_utils_get_only_cal_block( + core->cal_data[WCD_CPE_LSM_CAL_AFE]); + if (!afe_cal) { + pr_err("%s: failed to get afe cal block\n", + __func__); + rc = -EINVAL; + goto rel_cal_mutex; + } + + if (afe_cal->cal_data.size == 0) { + dev_dbg(core->dev, "%s: No AFE cal to send\n", + __func__); + rc = 0; + goto rel_cal_mutex; + } + + is_obm_msg = (afe_cal->cal_data.size > + CMI_INBAND_MESSAGE_SIZE) ? true : false; + + if (is_obm_msg) { + struct cmi_hdr *hdr = &(obm_msg.hdr); + struct cmi_obm *pld = &(obm_msg.pld); + + rc = wcd_cpe_afe_shmem_alloc(core, port_d, + afe_cal->cal_data.size); + if (rc) { + dev_err(core->dev, + "%s: AFE shmem alloc fail %d\n", + __func__, rc); + goto rel_cal_mutex; + } + + rc = fill_afe_cmd_header(hdr, port_d->port_id, + CPE_AFE_CMD_SET_PARAM, + CPE_AFE_PARAM_PAYLOAD_SIZE, + true); + if (rc) { + dev_err(core->dev, + "%s: invalid params for header, err = %d\n", + __func__, rc); + wcd_cpe_afe_shmem_dealloc(core, port_d); + goto rel_cal_mutex; + } + + pld->version = 0; + pld->size = afe_cal->cal_data.size; + pld->data_ptr.kvaddr = afe_cal->cal_data.kvaddr; + pld->mem_handle = port_d->mem_handle; + msg = &obm_msg; + + } else { + u8 *msg_pld; + struct cmi_hdr *hdr; + + inb_msg = kzalloc(sizeof(struct cmi_hdr) + + afe_cal->cal_data.size, + GFP_KERNEL); + if (!inb_msg) { + dev_err(core->dev, + "%s: no memory for afe cal inband\n", + __func__); + rc = -ENOMEM; + goto rel_cal_mutex; + } + + hdr = (struct cmi_hdr *) inb_msg; + + rc = fill_afe_cmd_header(hdr, port_d->port_id, + CPE_AFE_CMD_SET_PARAM, + CPE_AFE_PARAM_PAYLOAD_SIZE, + false); + if (rc) { + dev_err(core->dev, + "%s: invalid params for header, err = %d\n", + __func__, rc); + kfree(inb_msg); + inb_msg = NULL; + goto rel_cal_mutex; + } + + msg_pld = ((u8 *) inb_msg) + sizeof(struct cmi_hdr); + memcpy(msg_pld, afe_cal->cal_data.kvaddr, + afe_cal->cal_data.size); + + msg = inb_msg; + } + + rc = wcd_cpe_cmi_send_afe_msg(core, port_d, msg); + if (rc) + pr_err("%s: afe cal for listen failed, rc = %d\n", + __func__, rc); + + if (is_obm_msg) { + wcd_cpe_afe_shmem_dealloc(core, port_d); + port_d->mem_handle = 0; + } else { + kfree(inb_msg); + inb_msg = NULL; + } + +rel_cal_mutex: + mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_AFE]->lock); + return rc; +} + +/* + * wcd_cpe_is_valid_port: check validity of afe port id + * @core: handle to core to check for validity + * @afe_cfg: client provided afe configuration + * @func: function name invoking this validity check, + * used for logging purpose only. + */ +static int wcd_cpe_is_valid_port(struct wcd_cpe_core *core, + struct wcd_cpe_afe_port_cfg *afe_cfg, + const char *func) +{ + if (unlikely(IS_ERR_OR_NULL(core))) { + pr_err("%s: Invalid core handle\n", func); + return -EINVAL; + } + + if (afe_cfg->port_id > WCD_CPE_AFE_MAX_PORTS) { + dev_err(core->dev, + "%s: invalid afe port (%u)\n", + func, afe_cfg->port_id); + return -EINVAL; + } + + dev_dbg(core->dev, + "%s: port_id = %u\n", + func, afe_cfg->port_id); + + return 0; +} + +static int wcd_cpe_afe_svc_cmd_mode(void *core_handle, + u8 mode) +{ + struct cpe_afe_svc_cmd_mode afe_mode; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret; + + afe_port_d = &afe_ports[0]; + /* + * AFE SVC mode command is for the service and not port + * specific, hence use AFE port as 0 so the command will + * be applied to all AFE ports on CPE. + */ + afe_port_d->port_id = 0; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + memset(&afe_mode, 0, sizeof(afe_mode)); + if (fill_afe_cmd_header(&afe_mode.hdr, afe_port_d->port_id, + CPE_AFE_SVC_CMD_LAB_MODE, + CPE_AFE_CMD_MODE_PAYLOAD_SIZE, + false)) { + ret = -EINVAL; + goto err_ret; + } + + afe_mode.mode = mode; + + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &afe_mode); + if (ret) + dev_err(core->dev, + "%s: afe_svc_mode cmd failed, err = %d\n", + __func__, ret); + +err_ret: + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +static int wcd_cpe_afe_cmd_port_cfg(void *core_handle, + struct wcd_cpe_afe_port_cfg *afe_cfg) +{ + struct cpe_afe_cmd_port_cfg port_cfg_cmd; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret; + + ret = wcd_cpe_is_valid_port(core, afe_cfg, __func__); + if (ret) + goto done; + + afe_port_d = &afe_ports[afe_cfg->port_id]; + afe_port_d->port_id = afe_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + memset(&port_cfg_cmd, 0, sizeof(port_cfg_cmd)); + if (fill_afe_cmd_header(&port_cfg_cmd.hdr, + afe_cfg->port_id, + CPE_AFE_PORT_CMD_GENERIC_CONFIG, + CPE_AFE_CMD_PORT_CFG_PAYLOAD_SIZE, + false)) { + ret = -EINVAL; + goto err_ret; + } + + port_cfg_cmd.bit_width = afe_cfg->bit_width; + port_cfg_cmd.num_channels = afe_cfg->num_channels; + port_cfg_cmd.sample_rate = afe_cfg->sample_rate; + + if (afe_port_d->port_id == CPE_AFE_PORT_3_TX) + port_cfg_cmd.buffer_size = WCD_CPE_EC_PP_BUF_SIZE; + else + port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width, + afe_cfg->sample_rate); + + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &port_cfg_cmd); + if (ret) + dev_err(core->dev, + "%s: afe_port_config failed, err = %d\n", + __func__, ret); + +err_ret: + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); +done: + return ret; +} + +/* + * wcd_cpe_afe_set_params: set the parameters for afe port + * @afe_cfg: configuration data for the port for which the + * parameters are to be set + */ +static int wcd_cpe_afe_set_params(void *core_handle, + struct wcd_cpe_afe_port_cfg *afe_cfg, bool afe_mad_ctl) +{ + struct cpe_afe_params afe_params; + struct cpe_afe_hw_mad_ctrl *hw_mad_ctrl = &afe_params.hw_mad_ctrl; + struct cpe_afe_port_cfg *port_cfg = &afe_params.port_cfg; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0, pld_size = 0; + + ret = wcd_cpe_is_valid_port(core, afe_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[afe_cfg->port_id]; + afe_port_d->port_id = afe_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + ret = wcd_cpe_send_afe_cal(core, afe_port_d); + if (ret) { + dev_err(core->dev, + "%s: afe acdb cal send failed, err = %d\n", + __func__, ret); + goto err_ret; + } + + pld_size = CPE_AFE_PARAM_PAYLOAD_SIZE; + memset(&afe_params, 0, sizeof(afe_params)); + + if (fill_afe_cmd_header(&afe_params.hdr, + afe_cfg->port_id, + CPE_AFE_CMD_SET_PARAM, + (u8) pld_size, false)) { + ret = -EINVAL; + goto err_ret; + } + + hw_mad_ctrl->param.module_id = CPE_AFE_MODULE_HW_MAD; + hw_mad_ctrl->param.param_id = CPE_AFE_PARAM_ID_HW_MAD_CTL; + hw_mad_ctrl->param.p_size.sr.param_size = PARAM_SIZE_AFE_HW_MAD_CTRL; + hw_mad_ctrl->param.p_size.sr.reserved = 0; + hw_mad_ctrl->minor_version = 1; + hw_mad_ctrl->mad_type = MAD_TYPE_AUDIO; + hw_mad_ctrl->mad_enable = afe_mad_ctl; + + port_cfg->param.module_id = CPE_AFE_MODULE_AUDIO_DEV_INTERFACE; + port_cfg->param.param_id = CPE_AFE_PARAM_ID_GENERIC_PORT_CONFIG; + port_cfg->param.p_size.sr.param_size = PARAM_SIZE_AFE_PORT_CFG; + port_cfg->param.p_size.sr.reserved = 0; + port_cfg->minor_version = 1; + port_cfg->bit_width = afe_cfg->bit_width; + port_cfg->num_channels = afe_cfg->num_channels; + port_cfg->sample_rate = afe_cfg->sample_rate; + + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &afe_params); + if (ret) + dev_err(core->dev, + "%s: afe_port_config failed, err = %d\n", + __func__, ret); +err_ret: + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +/* + * wcd_cpe_afe_port_start: send the start command to afe service + * @core_handle: handle to the cpe core + * @port_cfg: configuration data for the afe port which needs + * to be started. + */ +static int wcd_cpe_afe_port_start(void *core_handle, + struct wcd_cpe_afe_port_cfg *port_cfg) +{ + + struct cmi_hdr hdr; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0; + + ret = wcd_cpe_is_valid_port(core, port_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[port_cfg->port_id]; + afe_port_d->port_id = port_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + memset(&hdr, 0, sizeof(struct cmi_hdr)); + fill_afe_cmd_header(&hdr, port_cfg->port_id, + CPE_AFE_PORT_CMD_START, + 0, false); + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr); + if (ret) + dev_err(core->dev, + "%s: afe_port_start cmd failed, err = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +/* + * wcd_cpe_afe_port_stop: send stop command to afe service + * @core_handle: handle to the cpe core + * @port_cfg: configuration data for the afe port which needs + * to be stopped. + */ +static int wcd_cpe_afe_port_stop(void *core_handle, + struct wcd_cpe_afe_port_cfg *port_cfg) +{ + struct cmi_hdr hdr; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0; + + ret = wcd_cpe_is_valid_port(core, port_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[port_cfg->port_id]; + afe_port_d->port_id = port_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + memset(&hdr, 0, sizeof(hdr)); + fill_afe_cmd_header(&hdr, port_cfg->port_id, + CPE_AFE_PORT_CMD_STOP, + 0, false); + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr); + if (ret) + dev_err(core->dev, + "%s: afe_stop cmd failed, err = %d\n", + __func__, ret); + + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +/* + * wcd_cpe_afe_port_suspend: send suspend command to afe service + * @core_handle: handle to the cpe core + * @port_cfg: configuration data for the afe port which needs + * to be suspended. + */ +static int wcd_cpe_afe_port_suspend(void *core_handle, + struct wcd_cpe_afe_port_cfg *port_cfg) +{ + struct cmi_hdr hdr; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0; + + ret = wcd_cpe_is_valid_port(core, port_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[port_cfg->port_id]; + afe_port_d->port_id = port_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + memset(&hdr, 0, sizeof(struct cmi_hdr)); + fill_afe_cmd_header(&hdr, port_cfg->port_id, + CPE_AFE_PORT_CMD_SUSPEND, + 0, false); + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr); + if (ret) + dev_err(core->dev, + "%s: afe_suspend cmd failed, err = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +/* + * wcd_cpe_afe_port_resume: send the resume command to afe service + * @core_handle: handle to the cpe core + * @port_cfg: configuration data for the afe port which needs + * to be resumed. + */ +static int wcd_cpe_afe_port_resume(void *core_handle, + struct wcd_cpe_afe_port_cfg *port_cfg) +{ + struct cmi_hdr hdr; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0; + + ret = wcd_cpe_is_valid_port(core, port_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[port_cfg->port_id]; + afe_port_d->port_id = port_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + memset(&hdr, 0, sizeof(hdr)); + fill_afe_cmd_header(&hdr, port_cfg->port_id, + CPE_AFE_PORT_CMD_RESUME, + 0, false); + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr); + if (ret) + dev_err(core->dev, + "%s: afe_resume cmd failed, err = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; + +} + +/* + * wcd_cpe_register_afe_driver: register lsm driver to codec + * @cpe_ops: structure with lsm callbacks + * @codec: codec to which this lsm driver is registered to + */ +int wcd_cpe_get_afe_ops(struct wcd_cpe_afe_ops *afe_ops) +{ + afe_ops->afe_set_params = wcd_cpe_afe_set_params; + afe_ops->afe_port_start = wcd_cpe_afe_port_start; + afe_ops->afe_port_stop = wcd_cpe_afe_port_stop; + afe_ops->afe_port_suspend = wcd_cpe_afe_port_suspend; + afe_ops->afe_port_resume = wcd_cpe_afe_port_resume; + afe_ops->afe_port_cmd_cfg = wcd_cpe_afe_cmd_port_cfg; + + return 0; +} +EXPORT_SYMBOL(wcd_cpe_get_afe_ops); + +MODULE_DESCRIPTION("WCD CPE Core"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd_cpe_core.h b/sound/soc/codecs/wcd_cpe_core.h new file mode 100644 index 0000000000000000000000000000000000000000..3d672b860ff46388f45a3b9172d897e75c32d1a1 --- /dev/null +++ b/sound/soc/codecs/wcd_cpe_core.h @@ -0,0 +1,226 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef WCD_CPE_CORE_H +#define WCD_CPE_CORE_H + +#include +#include +#include "wcd_cpe_services.h" + +#define WCD_CPE_LAB_MAX_LATENCY 250 +#define WCD_CPE_MAD_SLIM_CHANNEL 140 + +/* Indicates CPE block is ready for image re-download */ +#define WCD_CPE_BLK_READY (1 << 0) +/* Indicates the underlying bus is ready */ +#define WCD_CPE_BUS_READY (1 << 1) + +/* + * only when the underlying bus and CPE block both are ready, + * the state will be ready to download + */ +#define WCD_CPE_READY_TO_DLOAD \ + (WCD_CPE_BLK_READY | WCD_CPE_BUS_READY) + +#define WCD_CPE_LOAD_IMEM (1 << 0) +#define WCD_CPE_LOAD_DATA (1 << 1) +#define WCD_CPE_LOAD_ALL \ + (WCD_CPE_LOAD_IMEM | WCD_CPE_LOAD_DATA) + +#define WCD_CPE_IMAGE_FNAME_MAX 64 + +#define WCD_CPE_AFE_OUT_PORT_2 2 +#define WCD_CPE_AFE_OUT_PORT_4 4 + +enum { + WCD_CPE_LSM_CAL_AFE = 0, + WCD_CPE_LSM_CAL_LSM, + WCD_CPE_LSM_CAL_TOPOLOGY_ID, + WCD_CPE_LSM_CAL_MAX, +}; + +enum cpe_err_irq_cntl_type { + CPE_ERR_IRQ_MASK = 0, + CPE_ERR_IRQ_UNMASK, + CPE_ERR_IRQ_CLEAR, + CPE_ERR_IRQ_STATUS, +}; + +struct wcd_cpe_cdc_cb { + /* codec provided callback to enable RCO */ + int (*cdc_clk_en)(struct snd_soc_codec *, bool); + + /* callback for FLL setup for codec */ + int (*cpe_clk_en)(struct snd_soc_codec *, bool); + int (*cdc_ext_clk)(struct snd_soc_codec *codec, int enable, bool dapm); + int (*lab_cdc_ch_ctl)(struct snd_soc_codec *codec, u8 event); + int (*get_afe_out_port_id)(struct snd_soc_codec *codec, u16 *port_id); + int (*bus_vote_bw)(struct snd_soc_codec *codec, + bool vote); + + /* Callback to control the cpe error interrupt mask/status/clear */ + int (*cpe_err_irq_control)(struct snd_soc_codec *codec, + enum cpe_err_irq_cntl_type cntl_type, + u8 *status); +}; + +enum wcd_cpe_ssr_state_event { + /* Indicates CPE is initialized */ + WCD_CPE_INITIALIZED = 0, + /* Indicates that IMEM is downloaded to CPE */ + WCD_CPE_IMEM_DOWNLOADED, + /* Indicates CPE is enabled */ + WCD_CPE_ENABLED, + /* Indicates that CPE is currently active */ + WCD_CPE_ACTIVE, + /* Event from underlying bus notifying bus is down */ + WCD_CPE_BUS_DOWN_EVENT, + /* Event from CPE block, notifying CPE is down */ + WCD_CPE_SSR_EVENT, + /* Event from underlying bus notifying bus is up */ + WCD_CPE_BUS_UP_EVENT, +}; + +struct wcd_cpe_ssr_entry { + int offline; + u32 offline_change; + wait_queue_head_t offline_poll_wait; + struct snd_info_entry *entry; +}; + +struct wcd_cpe_irq_info { + int cpe_engine_irq; + int cpe_err_irq; + u8 cpe_fatal_irqs; +}; + +struct wcd_cpe_hw_info { + u32 dram_offset; + size_t dram_size; + u32 iram_offset; + size_t iram_size; +}; + +struct wcd_cpe_core { + /* handle to cpe services */ + void *cpe_handle; + + /* registration handle to cpe services */ + void *cpe_reg_handle; + + /* cmi registration handle for afe service */ + void *cmi_afe_handle; + + /* handle to codec */ + struct snd_soc_codec *codec; + + /* codec device */ + struct device *dev; + + /* firmware image file name */ + char fname[WCD_CPE_IMAGE_FNAME_MAX]; + + /* firmware image file name from sysfs */ + char dyn_fname[WCD_CPE_IMAGE_FNAME_MAX]; + + /* codec information needed by cpe services */ + struct cpe_svc_codec_info_v1 cdc_info; + + /* work to perform image download */ + struct work_struct load_fw_work; + + /* flag to indicate mode in which cpe needs to be booted */ + int cpe_debug_mode; + + /* callbacks for codec specific implementation */ + const struct wcd_cpe_cdc_cb *cpe_cdc_cb; + + /* work to handle CPE SSR*/ + struct work_struct ssr_work; + + /* PM handle for suspend mode during SSR */ + struct pm_qos_request pm_qos_req; + + /* completion event indicating CPE OFFLINE */ + struct completion offline_compl; + + /* entry into snd card procfs indicating cpe status */ + struct wcd_cpe_ssr_entry ssr_entry; + + /* + * completion event to signal CPE is + * ready for image re-download + */ + struct completion ready_compl; + + /* maintains the status for cpe ssr */ + u8 ready_status; + + /* Indicate SSR type */ + enum wcd_cpe_ssr_state_event ssr_type; + + /* mutex to protect cpe ssr status variables */ + struct mutex ssr_lock; + + /* Store the calibration data needed for cpe */ + struct cal_type_data *cal_data[WCD_CPE_LSM_CAL_MAX]; + + /* completion event to signal CPE is online */ + struct completion online_compl; + + /* reference counter for cpe usage */ + u8 cpe_users; + + /* Ramdump support */ + void *cpe_ramdump_dev; + struct ramdump_segment cpe_ramdump_seg; + dma_addr_t cpe_dump_addr; + void *cpe_dump_v_addr; + + /* SFR support */ + u32 sfr_buf_addr; + size_t sfr_buf_size; + + /* IRQ information for CPE interrupts */ + struct wcd_cpe_irq_info irq_info; + + /* Kobject for sysfs entry */ + struct kobject cpe_kobj; + + /* Reference count for cpe clk*/ + int cpe_clk_ref; + + /* codec based hardware info */ + struct wcd_cpe_hw_info hw_info; +}; + +struct wcd_cpe_params { + struct snd_soc_codec *codec; + struct wcd_cpe_core * (*get_cpe_core)( + struct snd_soc_codec *); + const struct wcd_cpe_cdc_cb *cdc_cb; + int dbg_mode; + u16 cdc_major_ver; + u16 cdc_minor_ver; + u32 cdc_id; + + struct wcd_cpe_irq_info cdc_irq_info; + + struct cpe_svc_init_param *cpe_svc_params; +}; + +int wcd_cpe_ssr_event(void *core_handle, + enum wcd_cpe_ssr_state_event event); +struct wcd_cpe_core *wcd_cpe_init(const char *img_fname, +struct snd_soc_codec *codec, struct wcd_cpe_params *params); +#endif diff --git a/sound/soc/codecs/wcd_cpe_services.c b/sound/soc/codecs/wcd_cpe_services.c new file mode 100644 index 0000000000000000000000000000000000000000..0028ebc08d5fb5c8d0cd5179d68185208b5fcbba --- /dev/null +++ b/sound/soc/codecs/wcd_cpe_services.c @@ -0,0 +1,2990 @@ +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd_cpe_services.h" +#include "wcd_cmi_api.h" + +#define CPE_MSG_BUFFER_SIZE 132 +#define CPE_NO_SERVICE 0 + +#define CMI_DRIVER_SUPPORTED_VERSION 0 +#define CMI_API_SUCCESS 0 +#define CMI_MSG_TRANSPORT (0x0002) +#define CPE_SVC_INACTIVE_STATE_RETRIES_MAX 10 + +#define TOMTOM_A_SVASS_SPE_DRAM_OFFSET 0x50000 +#define TOMTOM_A_SVASS_SPE_DRAM_SIZE 0x30000 +#define TOMTOM_A_SVASS_SPE_IRAM_OFFSET 0x80000 +#define TOMTOM_A_SVASS_SPE_IRAM_SIZE 0xC000 +#define TOMTOM_A_SVASS_SPE_INBOX_SIZE 12 +#define TOMTOM_A_SVASS_SPE_OUTBOX_SIZE 12 + +#define MEM_ACCESS_NONE_VAL 0x0 +#define MEM_ACCESS_IRAM_VAL 0x1 +#define MEM_ACCESS_DRAM_VAL 0x2 +#define LISTEN_CTL_SPE_VAL 0x0 +#define LISTEN_CTL_MSM_VAL 0x1 + +#define TOMTOM_A_SVASS_SPE_INBOX(N) (TOMTOM_A_SVASS_SPE_INBOX_0 + (N)) +#define TOMTOM_A_SVASS_SPE_OUTBOX(N) (TOMTOM_A_SVASS_SPE_OUTBOX_0 + (N)) + +#define WCD9335_CPE_SS_SPE_DRAM_OFFSET 0x48000 +#define WCD9335_CPE_SS_SPE_DRAM_SIZE 0x34000 +#define WCD9335_CPE_SS_SPE_IRAM_OFFSET 0x80000 +#define WCD9335_CPE_SS_SPE_IRAM_SIZE 0x20000 + +#define WCD9335_CPE_SS_SPE_INBOX_SIZE 16 +#define WCD9335_CPE_SS_SPE_OUTBOX_SIZE 16 +#define WCD9335_CPE_SS_SPE_MEM_BANK_SIZ 16 + +#define WCD9335_CPE_SS_SPE_INBOX1(N) (WCD9335_CPE_SS_INBOX1_0 + (N)) +#define WCD9335_CPE_SS_SPE_OUTBOX1(N) (WCD9335_CPE_SS_OUTBOX1_0 + (N)) +#define WCD9335_CPE_SS_MEM_BANK(N) (WCD9335_CPE_SS_MEM_BANK_0 + (N)) + +#define CHUNK_SIZE 16 + +#define CPE_SVC_GRAB_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock acquire\n", \ + __func__, name); \ + mutex_lock(lock); \ +} + +#define CPE_SVC_REL_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock release\n", \ + __func__, name); \ + mutex_unlock(lock); \ +} + +static const struct cpe_svc_hw_cfg cpe_svc_tomtom_info = { + TOMTOM_A_SVASS_SPE_DRAM_SIZE, + TOMTOM_A_SVASS_SPE_DRAM_OFFSET, + TOMTOM_A_SVASS_SPE_IRAM_SIZE, + TOMTOM_A_SVASS_SPE_IRAM_OFFSET, + TOMTOM_A_SVASS_SPE_INBOX_SIZE, + TOMTOM_A_SVASS_SPE_OUTBOX_SIZE +}; + +static const struct cpe_svc_hw_cfg cpe_svc_wcd9335_info = { + WCD9335_CPE_SS_SPE_DRAM_SIZE, + WCD9335_CPE_SS_SPE_DRAM_OFFSET, + WCD9335_CPE_SS_SPE_IRAM_SIZE, + WCD9335_CPE_SS_SPE_IRAM_OFFSET, + WCD9335_CPE_SS_SPE_INBOX_SIZE, + WCD9335_CPE_SS_SPE_OUTBOX_SIZE +}; + +enum cpe_state { + CPE_STATE_UNINITIALIZED = 0, + CPE_STATE_INITIALIZED, + CPE_STATE_IDLE, + CPE_STATE_DOWNLOADING, + CPE_STATE_BOOTING, + CPE_STATE_SENDING_MSG, + CPE_STATE_OFFLINE, + CPE_STATE_BUFFERING, + CPE_STATE_BUFFERING_CANCELLED +}; + +enum cpe_substate { + CPE_SS_IDLE = 0, + CPE_SS_MSG_REQUEST_ACCESS, + CPE_SS_MSG_SEND_INBOX, + CPE_SS_MSG_SENT, + CPE_SS_DL_DOWNLOADING, + CPE_SS_DL_COMPLETED, + CPE_SS_BOOT, + CPE_SS_BOOT_INIT, + CPE_SS_ONLINE +}; + +enum cpe_command { + CPE_CMD_KILL_THREAD = 0, + CPE_CMD_BOOT, + CPE_CMD_BOOT_INITIALIZE, + CPE_CMD_BOOT_COMPLETE, + CPE_CMD_SEND_MSG, + CPE_CMD_SEND_TRANS_MSG, + CPE_CMD_SEND_MSG_COMPLETE, + CPE_CMD_PROCESS_IRQ, + CPE_CMD_RAMDUMP, + CPE_CMD_DL_SEGMENT, + CPE_CMD_SHUTDOWN, + CPE_CMD_RESET, + CPE_CMD_DEINITIALIZE, + CPE_CMD_READ, + CPE_CMD_ENABLE_LAB, + CPE_CMD_DISABLE_LAB, + CPE_CMD_SWAP_BUFFER, + CPE_LAB_CFG_SB, + CPE_CMD_CANCEL_MEMACCESS, + CPE_CMD_PROC_INCOMING_MSG, + CPE_CMD_FTM_TEST, +}; + +enum cpe_process_result { + CPE_PROC_SUCCESS = 0, + CPE_PROC_FAILED, + CPE_PROC_KILLED, + CPE_PROC_QUEUED, +}; + +struct cpe_command_node { + enum cpe_command command; + enum cpe_svc_result result; + void *data; + struct list_head list; +}; + +struct cpe_info { + struct list_head main_queue; + struct completion cmd_complete; + struct completion thread_comp; + void *thread_handler; + bool stop_thread; + struct mutex msg_lock; + enum cpe_state state; + enum cpe_substate substate; + struct list_head client_list; + enum cpe_process_result (*cpe_process_command) + (struct cpe_command_node *command_node); + enum cpe_svc_result (*cpe_cmd_validate) + (const struct cpe_info *i, + enum cpe_command command); + enum cpe_svc_result (*cpe_start_notification) + (struct cpe_info *i); + u32 initialized; + struct cpe_svc_tgt_abstraction *tgt; + void *pending; + void *data; + void *client_context; + u32 codec_id; + struct work_struct clk_plan_work; + struct completion core_svc_cmd_compl; +}; + +struct cpe_tgt_waiti_info { + u8 tgt_waiti_size; + u8 *tgt_waiti_data; +}; + +struct cpe_svc_tgt_abstraction { + enum cpe_svc_result (*tgt_boot)(int debug_mode); + + u32 (*tgt_cpar_init_done)(void); + + u32 (*tgt_is_active)(void); + + enum cpe_svc_result (*tgt_reset)(void); + + enum cpe_svc_result (*tgt_stop)(void); + + enum cpe_svc_result (*tgt_read_mailbox) + (u8 *buffer, size_t size); + + enum cpe_svc_result (*tgt_write_mailbox) + (u8 *buffer, size_t size); + + enum cpe_svc_result (*tgt_read_ram) + (struct cpe_info *c, + struct cpe_svc_mem_segment *data); + + enum cpe_svc_result (*tgt_write_ram) + (struct cpe_info *c, + const struct cpe_svc_mem_segment *data); + + enum cpe_svc_result (*tgt_route_notification) + (enum cpe_svc_module module, + enum cpe_svc_route_dest dest); + + enum cpe_svc_result (*tgt_set_debug_mode)(u32 enable); + const struct cpe_svc_hw_cfg *(*tgt_get_cpe_info)(void); + enum cpe_svc_result (*tgt_deinit) + (struct cpe_svc_tgt_abstraction *param); + enum cpe_svc_result (*tgt_voice_tx_lab) + (bool); + u8 *inbox; + u8 *outbox; + struct cpe_tgt_waiti_info *tgt_waiti_info; +}; + +static enum cpe_svc_result cpe_tgt_tomtom_init( + struct cpe_svc_codec_info_v1 *codec_info, + struct cpe_svc_tgt_abstraction *param); + +static enum cpe_svc_result cpe_tgt_wcd9335_init( + struct cpe_svc_codec_info_v1 *codec_info, + struct cpe_svc_tgt_abstraction *param); + +struct cpe_send_msg { + u8 *payload; + u32 isobm; + u32 address; + size_t size; +}; + +struct cpe_read_handle { + void *registration; + struct cpe_info t_info; + struct list_head buffers; + void *config; +}; + +struct generic_notification { + void (*notification) + (const struct cpe_svc_notification *parameter); + void (*cmi_notification) + (const struct cmi_api_notification *parameter); +}; + +struct cpe_notif_node { + struct generic_notification notif; + u32 mask; + u32 service; + const struct cpe_info *context; + const char *name; + u32 disabled; + struct list_head list; +}; + +struct cpe_priv { + struct cpe_info *cpe_default_handle; + void (*cpe_irq_control_callback)(u32 enable); + void (*cpe_query_freq_plans_cb) + (void *cdc_priv, + struct cpe_svc_cfg_clk_plan *clk_freq); + void (*cpe_change_freq_plan_cb)(void *cdc_priv, + u32 clk_freq); + u32 cpe_msg_buffer; + void *cpe_cmi_handle; + struct mutex cpe_api_mutex; + struct mutex cpe_svc_lock; + struct cpe_svc_boot_event cpe_debug_vector; + void *cdc_priv; +}; + +static struct cpe_priv cpe_d; + +static enum cpe_svc_result __cpe_svc_shutdown(void *cpe_handle); + +static enum cpe_svc_result cpe_is_command_valid( + const struct cpe_info *t_info, + enum cpe_command command); + +static int cpe_register_read(u32 reg, u8 *val) +{ + *(val) = snd_soc_read(cpe_d.cdc_priv, reg); + return 0; +} + +static enum cpe_svc_result cpe_update_bits(u32 reg, + u32 mask, u32 value) +{ + int ret = 0; + + ret = snd_soc_update_bits(cpe_d.cdc_priv, reg, + mask, value); + if (ret < 0) + return CPE_SVC_FAILED; + + return CPE_SVC_SUCCESS; +} + +static int cpe_register_write(u32 reg, u32 val) +{ + int ret = 0; + + if (reg != TOMTOM_A_SVASS_MEM_BANK && + reg != WCD9335_CPE_SS_MEM_BANK_0) + pr_debug("%s: reg = 0x%x, value = 0x%x\n", + __func__, reg, val); + + ret = snd_soc_write(cpe_d.cdc_priv, reg, val); + if (ret < 0) + return CPE_SVC_FAILED; + + return CPE_SVC_SUCCESS; +} + +static int cpe_register_write_repeat(u32 reg, u8 *ptr, u32 to_write) +{ + struct snd_soc_codec *codec = cpe_d.cdc_priv; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int ret = 0; + + ret = wcd9xxx_slim_write_repeat(wcd9xxx, reg, to_write, ptr); + if (ret != 0) + pr_err("%s: slim_write_repeat failed\n", __func__); + + if (ret < 0) + return CPE_SVC_FAILED; + + return CPE_SVC_SUCCESS; +} + +static bool cpe_register_read_autoinc_supported(void) +{ + return true; +} + + +/* Called under msgq locked context */ +static void cpe_cmd_received(struct cpe_info *t_info) +{ + struct cpe_command_node *node = NULL; + enum cpe_process_result proc_rc = CPE_PROC_SUCCESS; + + if (!t_info) { + pr_err("%s: Invalid thread info\n", + __func__); + return; + } + + while (!list_empty(&t_info->main_queue)) { + if (proc_rc != CPE_PROC_SUCCESS) + break; + node = list_first_entry(&t_info->main_queue, + struct cpe_command_node, list); + if (!node) + break; + list_del(&node->list); + proc_rc = t_info->cpe_process_command(node); + pr_debug("%s: process command return %d\n", + __func__, proc_rc); + + switch (proc_rc) { + case CPE_PROC_SUCCESS: + kfree(node); + break; + case CPE_PROC_FAILED: + kfree(node); + pr_err("%s: cmd failed\n", __func__); + break; + case CPE_PROC_KILLED: + break; + default: + list_add(&node->list, &(t_info->main_queue)); + + } + } +} + +static int cpe_worker_thread(void *context) +{ + struct cpe_info *t_info = (struct cpe_info *)context; + + /* + * Thread will run until requested to stop explicitly + * by setting the t_info->stop_thread flag + */ + while (1) { + /* Wait for command to be processed */ + wait_for_completion(&t_info->cmd_complete); + + CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock"); + cpe_cmd_received(t_info); + reinit_completion(&t_info->cmd_complete); + /* Check if thread needs to be stopped */ + if (t_info->stop_thread) + goto unlock_and_exit; + CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock"); + }; + +unlock_and_exit: + pr_debug("%s: thread stopped\n", __func__); + CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock"); + complete_and_exit(&t_info->thread_comp, 0); +} + +static void cpe_create_worker_thread(struct cpe_info *t_info) +{ + INIT_LIST_HEAD(&t_info->main_queue); + init_completion(&t_info->cmd_complete); + init_completion(&t_info->thread_comp); + t_info->stop_thread = false; + t_info->thread_handler = kthread_run(cpe_worker_thread, + (void *)t_info, "cpe-worker-thread"); + pr_debug("%s: Created new worker thread\n", + __func__); +} + +static void cpe_cleanup_worker_thread(struct cpe_info *t_info) +{ + if (!t_info->thread_handler) { + pr_err("%s: thread not created\n", __func__); + return; + } + + /* + * Wake up the command handler in case + * it is waiting for an command to be processed. + */ + CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock"); + t_info->stop_thread = true; + complete(&t_info->cmd_complete); + CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock"); + + /* Wait for the thread to exit */ + wait_for_completion(&t_info->thread_comp); + t_info->thread_handler = NULL; + + pr_debug("%s: Thread cleaned up successfully\n", + __func__); +} + +static enum cpe_svc_result +cpe_send_cmd_to_thread(struct cpe_info *t_info, + enum cpe_command command, void *data, + bool high_prio) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_command_node *cmd = NULL; + + rc = cpe_is_command_valid(t_info, command); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Invalid command %d\n", + __func__, command); + return rc; + } + + cmd = kzalloc(sizeof(struct cpe_command_node), + GFP_ATOMIC); + if (!cmd) + return CPE_SVC_NO_MEMORY; + + cmd->command = command; + cmd->data = data; + + CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock"); + if (high_prio) + list_add(&(cmd->list), + &(t_info->main_queue)); + else + list_add_tail(&(cmd->list), + &(t_info->main_queue)); + complete(&t_info->cmd_complete); + CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock"); + + return rc; +} + +static enum cpe_svc_result cpe_change_state( + struct cpe_info *t_info, + enum cpe_state state, enum cpe_substate ss) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + t_info->state = state; + t_info->substate = ss; + + pr_debug("%s: current state: %d,%d, new_state: %d,%d\n", + __func__, t_info->state, t_info->substate, + state, ss); + + return rc; +} + +static enum cpe_svc_result +cpe_is_command_valid(const struct cpe_info *t_info, + enum cpe_command command) +{ + enum cpe_svc_result rc = CPE_SVC_INVALID_HANDLE; + + if (t_info && t_info->cpe_cmd_validate) + rc = t_info->cpe_cmd_validate(t_info, command); + else + pr_err("%s: invalid handle or callback\n", + __func__); + return rc; +} + +static void cpe_notify_client(struct cpe_notif_node *client, + struct cpe_svc_notification *payload) +{ + if (!client || !payload) { + pr_err("%s: invalid client or payload\n", + __func__); + return; + } + + if (!(client->mask & payload->event)) { + pr_debug("%s: client mask 0x%x not registered for event 0x%x\n", + __func__, client->mask, payload->event); + return; + } + + if (client->notif.notification && !client->disabled) + client->notif.notification(payload); + + if ((client->mask & CPE_SVC_CMI_MSG) && + client->notif.cmi_notification) + client->notif.cmi_notification( + (const struct cmi_api_notification *)payload); +} + +static void cpe_broadcast_notification(const struct cpe_info *t_info, + struct cpe_svc_notification *payload) +{ + struct cpe_notif_node *n = NULL; + + if (!t_info || !payload) { + pr_err("%s: invalid handle\n", __func__); + return; + } + + pr_debug("%s: notify clients, event = %d\n", + __func__, payload->event); + payload->private_data = cpe_d.cdc_priv; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + list_for_each_entry(n, &t_info->client_list, list) { + if (!(n->mask & CPE_SVC_CMI_MSG)) + cpe_notify_client(n, payload); + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); +} + +static void *cpe_register_generic(struct cpe_info *t_info, + void notification_callback( + const struct cpe_svc_notification *parameter), + void cmi_callback( + const struct cmi_api_notification *parameter), + u32 mask, u32 service, const char *name) +{ + struct cpe_notif_node *n = NULL; + + n = kzalloc(sizeof(struct cpe_notif_node), + GFP_KERNEL); + if (!n) + return NULL; + n->mask = mask; + n->service = service; + n->notif.notification = notification_callback; + n->notif.cmi_notification = cmi_callback; + n->context = t_info; + n->disabled = false; + n->name = name; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + /* Make sure CPE core service is first */ + if (service == CMI_CPE_CORE_SERVICE_ID) + list_add(&n->list, &t_info->client_list); + else + list_add_tail(&n->list, &t_info->client_list); + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + + return n; +} + +static enum cpe_svc_result cpe_deregister_generic(struct cpe_info *t_info, + void *reg_handle) +{ + struct cpe_notif_node *n = (struct cpe_notif_node *)reg_handle; + + if (!t_info || !reg_handle) { + pr_err("%s: invalid handle\n", __func__); + return CPE_SVC_INVALID_HANDLE; + } + + list_del(&(n->list)); + kfree(reg_handle); + + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result cpe_svc_tgt_init(struct cpe_svc_codec_info_v1 *i, + struct cpe_svc_tgt_abstraction *abs) +{ + if (!i || !abs) { + pr_err("%s: Incorrect information provided\n", + __func__); + return CPE_SVC_FAILED; + } + + switch (i->id) { + case CPE_SVC_CODEC_TOMTOM: + return cpe_tgt_tomtom_init(i, abs); + case CPE_SVC_CODEC_WCD9335: + return cpe_tgt_wcd9335_init(i, abs); + default: + pr_err("%s: Codec type %d not supported\n", + __func__, i->id); + return CPE_SVC_FAILED; + } + + return CPE_SVC_SUCCESS; +} + +static void cpe_notify_cmi_client(struct cpe_info *t_info, u8 *payload, + enum cpe_svc_result result) +{ + struct cpe_notif_node *n = NULL; + struct cmi_api_notification notif; + struct cmi_hdr *hdr; + u8 service = 0; + + if (!t_info || !payload) { + pr_err("%s: invalid payload/handle\n", + __func__); + return; + } + + hdr = CMI_GET_HEADER(payload); + service = CMI_HDR_GET_SERVICE(hdr); + + notif.event = CPE_SVC_CMI_MSG; + notif.result = result; + notif.message = payload; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + list_for_each_entry(n, &t_info->client_list, list) { + + if ((n->mask & CPE_SVC_CMI_MSG) && + n->service == service && + n->notif.cmi_notification) { + n->notif.cmi_notification(¬if); + break; + } + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); +} + +static void cpe_toggle_irq_notification(struct cpe_info *t_info, u32 value) +{ + if (cpe_d.cpe_irq_control_callback) + cpe_d.cpe_irq_control_callback(value); +} + +static void cpe_command_cleanup(struct cpe_command_node *command_node) +{ + switch (command_node->command) { + case CPE_CMD_SEND_MSG: + case CPE_CMD_SEND_TRANS_MSG: + case CPE_CMD_SEND_MSG_COMPLETE: + case CPE_CMD_SHUTDOWN: + case CPE_CMD_READ: + kfree(command_node->data); + command_node->data = NULL; + break; + default: + pr_err("%s: unhandled command\n", + __func__); + break; + } +} + +static enum cpe_svc_result cpe_send_msg_to_inbox( + struct cpe_info *t_info, u32 opcode, + struct cpe_send_msg *msg) +{ + size_t bytes = 0; + size_t inbox_size = + t_info->tgt->tgt_get_cpe_info()->inbox_size; + struct cmi_hdr *hdr; + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + memset(t_info->tgt->inbox, 0, inbox_size); + hdr = CMI_GET_HEADER(t_info->tgt->inbox); + CMI_HDR_SET_SESSION(hdr, 1); + CMI_HDR_SET_SERVICE(hdr, CMI_CPE_CORE_SERVICE_ID); + CMI_HDR_SET_VERSION(hdr, CMI_DRIVER_SUPPORTED_VERSION); + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND); + + switch (opcode) { + case CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC: { + struct cmi_core_svc_cmd_shared_mem_alloc *m; + + CMI_HDR_SET_OPCODE(hdr, + CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC); + CMI_HDR_SET_PAYLOAD_SIZE(hdr, + sizeof(struct cmi_core_svc_cmd_shared_mem_alloc)); + m = (struct cmi_core_svc_cmd_shared_mem_alloc *) + CMI_GET_PAYLOAD(t_info->tgt->inbox); + m->size = CPE_MSG_BUFFER_SIZE; + pr_debug("send shared mem alloc msg to cpe inbox\n"); + } + break; + case CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ: + CMI_HDR_SET_OPCODE(hdr, + CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ); + CMI_HDR_SET_PAYLOAD_SIZE(hdr, 0); + pr_debug("%s: Creating DRAM acces request msg\n", + __func__); + break; + + case CPE_CMI_BASIC_RSP_OPCODE: { + struct cmi_basic_rsp_result *rsp; + + CMI_HDR_SET_OPCODE(hdr, + CPE_CMI_BASIC_RSP_OPCODE); + CMI_HDR_SET_PAYLOAD_SIZE(hdr, + sizeof(struct cmi_basic_rsp_result)); + rsp = (struct cmi_basic_rsp_result *) + CMI_GET_PAYLOAD(t_info->tgt->inbox); + rsp->status = 0; + pr_debug("%s: send basic response\n", __func__); + } + break; + + default: + if (msg->address != 0) { + struct cmi_msg_transport *m = NULL; + struct cpe_svc_mem_segment mem_seg; + + mem_seg.type = CPE_SVC_DATA_MEM; + if (msg->isobm) { + struct cmi_obm *obm = (struct cmi_obm *) + + CMI_GET_PAYLOAD(msg->payload); + mem_seg.cpe_addr = obm->mem_handle; + mem_seg.data = (u8 *)obm->data_ptr.kvaddr; + mem_seg.size = obm->size; + t_info->tgt->tgt_write_ram(t_info, &mem_seg); + } + + mem_seg.cpe_addr = msg->address; + mem_seg.data = msg->payload; + mem_seg.size = msg->size; + t_info->tgt->tgt_write_ram(t_info, &mem_seg); + + hdr = CMI_GET_HEADER(t_info->tgt->inbox); + CMI_HDR_SET_OPCODE(hdr, CMI_MSG_TRANSPORT); + m = (struct cmi_msg_transport *) + CMI_GET_PAYLOAD(t_info->tgt->inbox); + m->addr = msg->address; + m->size = msg->size; + CMI_HDR_SET_PAYLOAD_SIZE(hdr, + sizeof(struct cmi_msg_transport)); + } else { + memcpy(t_info->tgt->inbox, msg->payload, + msg->size); + } + + break; + } + + pr_debug("%s: sending message to cpe inbox\n", + __func__); + bytes = sizeof(struct cmi_hdr); + hdr = CMI_GET_HEADER(t_info->tgt->inbox); + bytes += CMI_HDR_GET_PAYLOAD_SIZE(hdr); + rc = t_info->tgt->tgt_write_mailbox(t_info->tgt->inbox, bytes); + + return rc; +} + +static bool cpe_is_cmd_clk_req(void *cmd) +{ + struct cmi_hdr *hdr; + + hdr = CMI_GET_HEADER(cmd); + + if ((CMI_HDR_GET_SERVICE(hdr) == + CMI_CPE_CORE_SERVICE_ID)) { + if (CMI_GET_OPCODE(cmd) == + CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST) + return true; + } + + return false; +} + +static enum cpe_svc_result cpe_process_clk_change_req( + struct cpe_info *t_info) +{ + struct cmi_core_svc_cmd_clk_freq_request *req; + + req = (struct cmi_core_svc_cmd_clk_freq_request *) + CMI_GET_PAYLOAD(t_info->tgt->outbox); + + if (!cpe_d.cpe_change_freq_plan_cb) { + pr_err("%s: No support for clk freq change\n", + __func__); + return CPE_SVC_FAILED; + } + + cpe_d.cpe_change_freq_plan_cb(cpe_d.cdc_priv, + req->clk_freq); + + /*send a basic response*/ + cpe_send_msg_to_inbox(t_info, + CPE_CMI_BASIC_RSP_OPCODE, NULL); + + return CPE_SVC_SUCCESS; +} + +static void cpe_process_irq_int(u32 irq, + struct cpe_info *t_info) +{ + struct cpe_command_node temp_node; + struct cpe_send_msg *m; + u8 size = 0; + bool err_irq = false; + struct cmi_hdr *hdr; + + pr_debug("%s: irq = %u\n", __func__, irq); + + if (!t_info) { + pr_err("%s: Invalid handle\n", + __func__); + return; + } + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + switch (irq) { + case CPE_IRQ_OUTBOX_IRQ: + size = t_info->tgt->tgt_get_cpe_info()->outbox_size; + t_info->tgt->tgt_read_mailbox(t_info->tgt->outbox, size); + break; + + case CPE_IRQ_MEM_ACCESS_ERROR: + err_irq = true; + cpe_change_state(t_info, CPE_STATE_OFFLINE, CPE_SS_IDLE); + break; + + case CPE_IRQ_WDOG_BITE: + case CPE_IRQ_RCO_WDOG_INT: + err_irq = true; + __cpe_svc_shutdown(t_info); + break; + + case CPE_IRQ_FLL_LOCK_LOST: + default: + err_irq = true; + break; + } + + if (err_irq) { + pr_err("%s: CPE error IRQ %u occurred\n", + __func__, irq); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return; + } + + switch (t_info->state) { + case CPE_STATE_BOOTING: + + switch (t_info->substate) { + case CPE_SS_BOOT: + temp_node.command = CPE_CMD_BOOT_INITIALIZE; + temp_node.result = CPE_SVC_SUCCESS; + t_info->substate = CPE_SS_BOOT_INIT; + t_info->cpe_process_command(&temp_node); + break; + + case CPE_SS_BOOT_INIT: + temp_node.command = CPE_CMD_BOOT_COMPLETE; + temp_node.result = CPE_SVC_SUCCESS; + t_info->substate = CPE_SS_ONLINE; + t_info->cpe_process_command(&temp_node); + break; + + default: + pr_debug("%s: unhandled substate %d for state %d\n", + __func__, t_info->state, t_info->substate); + break; + } + break; + + case CPE_STATE_SENDING_MSG: + hdr = CMI_GET_HEADER(t_info->tgt->outbox); + if (CMI_GET_OPCODE(t_info->tgt->outbox) == + CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2) { + pr_debug("%s: session_id: %u, state: %d,%d, event received\n", + __func__, CMI_HDR_GET_SESSION_ID(hdr), + t_info->state, t_info->substate); + temp_node.command = CPE_CMD_PROC_INCOMING_MSG; + temp_node.data = NULL; + t_info->cpe_process_command(&temp_node); + break; + } + + m = (struct cpe_send_msg *)t_info->pending; + + switch (t_info->substate) { + case CPE_SS_MSG_REQUEST_ACCESS: + cpe_send_cmd_to_thread(t_info, + CPE_CMD_SEND_TRANS_MSG, m, true); + break; + + case CPE_SS_MSG_SEND_INBOX: + if (cpe_is_cmd_clk_req(t_info->tgt->outbox)) + cpe_process_clk_change_req(t_info); + else + cpe_send_cmd_to_thread(t_info, + CPE_CMD_SEND_MSG_COMPLETE, m, true); + break; + + default: + pr_debug("%s: unhandled substate %d for state %d\n", + __func__, t_info->state, t_info->substate); + break; + } + break; + + case CPE_STATE_IDLE: + pr_debug("%s: Message received, notifying client\n", + __func__); + temp_node.command = CPE_CMD_PROC_INCOMING_MSG; + temp_node.data = NULL; + t_info->cpe_process_command(&temp_node); + break; + + default: + pr_debug("%s: unhandled state %d\n", + __func__, t_info->state); + break; + } + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); +} + + +static void broacast_boot_failed(void) +{ + struct cpe_info *t_info = cpe_d.cpe_default_handle; + struct cpe_svc_notification payload; + + payload.event = CPE_SVC_BOOT_FAILED; + payload.result = CPE_SVC_FAILED; + payload.payload = NULL; + if (t_info) + payload.private_data = + t_info->client_context; + cpe_broadcast_notification(t_info, &payload); +} + +static enum cpe_svc_result broadcast_boot_event( + struct cpe_info *t_info) +{ + struct cpe_svc_notification payload; + + payload.event = CPE_SVC_ONLINE; + payload.result = CPE_SVC_SUCCESS; + payload.payload = NULL; + if (t_info) + payload.private_data = + t_info->client_context; + cpe_broadcast_notification(t_info, &payload); + + return CPE_SVC_SUCCESS; +} + +static enum cpe_process_result cpe_boot_initialize(struct cpe_info *t_info, + enum cpe_svc_result *cpe_rc) +{ + enum cpe_process_result rc = CPE_SVC_FAILED; + struct cpe_svc_notification payload; + struct cmi_core_svc_event_system_boot *p = NULL; + + if (CMI_GET_OPCODE(t_info->tgt->outbox) != + CPE_CORE_SVC_EVENT_SYSTEM_BOOT) { + broacast_boot_failed(); + return rc; + } + + p = (struct cmi_core_svc_event_system_boot *) + CMI_GET_PAYLOAD(t_info->tgt->outbox); + if (p->status != CPE_BOOT_SUCCESS) { + pr_err("%s: cpe boot failed, status = %d\n", + __func__, p->status); + broacast_boot_failed(); + return rc; + } + + /* boot was successful */ + if (p->version == + CPE_CORE_VERSION_SYSTEM_BOOT_EVENT) { + cpe_d.cpe_debug_vector.debug_address = + p->sfr_buff_address; + cpe_d.cpe_debug_vector.debug_buffer_size = + p->sfr_buff_size; + cpe_d.cpe_debug_vector.status = p->status; + payload.event = CPE_SVC_BOOT; + payload.result = CPE_SVC_SUCCESS; + payload.payload = (void *)&cpe_d.cpe_debug_vector; + payload.private_data = t_info->client_context; + cpe_broadcast_notification(t_info, &payload); + } + cpe_change_state(t_info, CPE_STATE_BOOTING, + CPE_SS_BOOT_INIT); + (*cpe_rc) = cpe_send_msg_to_inbox(t_info, + CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC, NULL); + rc = CPE_PROC_SUCCESS; + return rc; +} + +static void cpe_svc_core_cmi_handler( + const struct cmi_api_notification *parameter) +{ + struct cmi_hdr *hdr; + + if (!parameter) + return; + + pr_debug("%s: event = %d\n", + __func__, parameter->event); + + if (parameter->event != CMI_API_MSG) + return; + + hdr = (struct cmi_hdr *) parameter->message; + + if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) { + struct cmi_basic_rsp_result *result; + + result = (struct cmi_basic_rsp_result *) + ((u8 *)parameter->message) + (sizeof(*hdr)); + if (result->status) + pr_err("%s: error response, error code = %u\n", + __func__, result->status); + complete(&cpe_d.cpe_default_handle->core_svc_cmd_compl); + } +} + +static void cpe_clk_plan_work(struct work_struct *work) +{ + struct cpe_info *t_info = NULL; + size_t size = 0; + struct cpe_svc_cfg_clk_plan plan; + u8 *cmi_msg; + struct cmi_hdr *hdr; + int rc; + + t_info = container_of(work, struct cpe_info, clk_plan_work); + if (!t_info) { + pr_err("%s: Invalid handle for cpe_info\n", + __func__); + return; + } + + /* Register the core service */ + cpe_d.cpe_cmi_handle = cmi_register( + cpe_svc_core_cmi_handler, + CMI_CPE_CORE_SERVICE_ID); + + /* send the clk plan command */ + if (!cpe_d.cpe_query_freq_plans_cb) { + pr_err("%s: No support for querying clk plans\n", + __func__); + return; + } + + cpe_d.cpe_query_freq_plans_cb(cpe_d.cdc_priv, &plan); + size = sizeof(plan.current_clk_feq) + + sizeof(plan.num_clk_freqs); + size += plan.num_clk_freqs * + sizeof(plan.clk_freqs[0]); + cmi_msg = kzalloc(size + sizeof(struct cmi_hdr), + GFP_KERNEL); + if (!cmi_msg) + return; + + hdr = (struct cmi_hdr *) cmi_msg; + CMI_HDR_SET_OPCODE(hdr, + CPE_CORE_SVC_CMD_CFG_CLK_PLAN); + CMI_HDR_SET_SERVICE(hdr, CMI_CPE_CORE_SERVICE_ID); + CMI_HDR_SET_SESSION(hdr, 1); + CMI_HDR_SET_VERSION(hdr, CMI_DRIVER_SUPPORTED_VERSION); + CMI_HDR_SET_PAYLOAD_SIZE(hdr, size); + memcpy(CMI_GET_PAYLOAD(cmi_msg), &plan, + size); + cmi_send_msg(cmi_msg); + + /* Wait for clk plan command to complete */ + rc = wait_for_completion_timeout(&t_info->core_svc_cmd_compl, + (10 * HZ)); + if (!rc) { + pr_err("%s: clk plan cmd timed out\n", + __func__); + goto cmd_fail; + } + + /* clk plan cmd is successful, send start notification */ + if (t_info->cpe_start_notification) + t_info->cpe_start_notification(t_info); + else + pr_err("%s: no start notification\n", + __func__); + +cmd_fail: + kfree(cmi_msg); + cmi_deregister(cpe_d.cpe_cmi_handle); +} + +static enum cpe_process_result cpe_boot_complete( + struct cpe_info *t_info) +{ + struct cmi_core_svc_cmdrsp_shared_mem_alloc *p = NULL; + + if (CMI_GET_OPCODE(t_info->tgt->outbox) != + CPE_CORE_SVC_CMDRSP_SHARED_MEM_ALLOC) { + broacast_boot_failed(); + return CPE_PROC_FAILED; + } + + p = (struct cmi_core_svc_cmdrsp_shared_mem_alloc *) + CMI_GET_PAYLOAD(t_info->tgt->outbox); + cpe_d.cpe_msg_buffer = p->addr; + + if (cpe_d.cpe_msg_buffer == 0) { + pr_err("%s: Invalid cpe buffer for message\n", + __func__); + broacast_boot_failed(); + return CPE_PROC_FAILED; + } + + cpe_change_state(t_info, CPE_STATE_IDLE, CPE_SS_IDLE); + cpe_create_worker_thread(t_info); + + if (t_info->codec_id != CPE_SVC_CODEC_TOMTOM) { + schedule_work(&t_info->clk_plan_work); + } else { + if (t_info->cpe_start_notification) + t_info->cpe_start_notification(t_info); + else + pr_err("%s: no start notification\n", + __func__); + } + + pr_debug("%s: boot complete\n", __func__); + return CPE_SVC_SUCCESS; +} + +static enum cpe_process_result cpe_process_send_msg( + struct cpe_info *t_info, + enum cpe_svc_result *cpe_rc, + struct cpe_command_node *command_node) +{ + enum cpe_process_result rc = CPE_PROC_SUCCESS; + struct cpe_send_msg *m = + (struct cpe_send_msg *)command_node->data; + u32 size = m->size; + + if (t_info->pending) { + pr_debug("%s: message queued\n", __func__); + *cpe_rc = CPE_SVC_SUCCESS; + return CPE_PROC_QUEUED; + } + + pr_debug("%s: Send CMI message, size = %u\n", + __func__, size); + + if (size <= t_info->tgt->tgt_get_cpe_info()->inbox_size) { + pr_debug("%s: Msg fits mailbox, size %u\n", + __func__, size); + cpe_change_state(t_info, CPE_STATE_SENDING_MSG, + CPE_SS_MSG_SEND_INBOX); + t_info->pending = m; + *cpe_rc = cpe_send_msg_to_inbox(t_info, 0, m); + } else if (size < CPE_MSG_BUFFER_SIZE) { + m->address = cpe_d.cpe_msg_buffer; + pr_debug("%s: Message req CMI mem access\n", + __func__); + t_info->pending = m; + cpe_change_state(t_info, CPE_STATE_SENDING_MSG, + CPE_SS_MSG_REQUEST_ACCESS); + *cpe_rc = cpe_send_msg_to_inbox(t_info, + CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ, m); + } else { + pr_debug("%s: Invalid msg size %u\n", + __func__, size); + cpe_command_cleanup(command_node); + rc = CPE_PROC_FAILED; + cpe_change_state(t_info, CPE_STATE_IDLE, + CPE_SS_IDLE); + } + + return rc; +} + +static enum cpe_process_result cpe_process_incoming( + struct cpe_info *t_info) +{ + enum cpe_process_result rc = CPE_PROC_FAILED; + struct cmi_hdr *hdr; + + hdr = CMI_GET_HEADER(t_info->tgt->outbox); + + if (CMI_HDR_GET_SERVICE(hdr) == + CMI_CPE_CORE_SERVICE_ID) { + pr_debug("%s: core service message received\n", + __func__); + + switch (CMI_GET_OPCODE(t_info->tgt->outbox)) { + case CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST: + cpe_process_clk_change_req(t_info); + rc = CPE_PROC_SUCCESS; + break; + case CMI_MSG_TRANSPORT: + pr_debug("%s: transport msg received\n", + __func__); + rc = CPE_PROC_SUCCESS; + break; + case CPE_CMI_BASIC_RSP_OPCODE: + pr_debug("%s: received basic rsp\n", + __func__); + rc = CPE_PROC_SUCCESS; + break; + default: + pr_debug("%s: unknown message received\n", + __func__); + break; + } + } else { + /* if service id if for a CMI client, notify client */ + pr_debug("%s: Message received, notifying client\n", + __func__); + cpe_notify_cmi_client(t_info, + t_info->tgt->outbox, CPE_SVC_SUCCESS); + rc = CPE_PROC_SUCCESS; + } + + return rc; +} + +static enum cpe_process_result cpe_process_kill_thread( + struct cpe_info *t_info, + struct cpe_command_node *command_node) +{ + struct cpe_svc_notification payload; + + cpe_d.cpe_msg_buffer = 0; + payload.result = CPE_SVC_SHUTTING_DOWN; + payload.event = CPE_SVC_OFFLINE; + payload.payload = NULL; + payload.private_data = t_info->client_context; + /* + * Make state as offline before broadcasting + * the message to clients. + */ + cpe_change_state(t_info, CPE_STATE_OFFLINE, + CPE_SS_IDLE); + cpe_broadcast_notification(t_info, &payload); + + return CPE_PROC_KILLED; +} + +static enum cpe_process_result cpe_mt_process_cmd( + struct cpe_command_node *command_node) +{ + struct cpe_info *t_info = cpe_d.cpe_default_handle; + enum cpe_svc_result cpe_rc = CPE_SVC_SUCCESS; + enum cpe_process_result rc = CPE_PROC_SUCCESS; + struct cpe_send_msg *m; + struct cmi_hdr *hdr; + u8 service = 0; + u8 retries = 0; + + if (!t_info || !command_node) { + pr_err("%s: Invalid handle/command node\n", + __func__); + return CPE_PROC_FAILED; + } + + pr_debug("%s: cmd = %u\n", __func__, command_node->command); + + cpe_rc = cpe_is_command_valid(t_info, command_node->command); + + if (cpe_rc != CPE_SVC_SUCCESS) { + pr_err("%s: Invalid command %d, err = %d\n", + __func__, command_node->command, cpe_rc); + return CPE_PROC_FAILED; + } + + switch (command_node->command) { + + case CPE_CMD_BOOT_INITIALIZE: + rc = cpe_boot_initialize(t_info, &cpe_rc); + break; + + case CPE_CMD_BOOT_COMPLETE: + rc = cpe_boot_complete(t_info); + break; + + case CPE_CMD_SEND_MSG: + rc = cpe_process_send_msg(t_info, &cpe_rc, + command_node); + break; + + case CPE_CMD_SEND_TRANS_MSG: + m = (struct cpe_send_msg *)command_node->data; + + while (retries < CPE_SVC_INACTIVE_STATE_RETRIES_MAX) { + if (t_info->tgt->tgt_is_active()) { + ++retries; + /* Wait for CPE to be inactive */ + usleep_range(5000, 5100); + } else { + break; + } + } + + pr_debug("%s: cpe inactive after %d attempts\n", + __func__, retries); + + cpe_change_state(t_info, CPE_STATE_SENDING_MSG, + CPE_SS_MSG_SEND_INBOX); + rc = cpe_send_msg_to_inbox(t_info, 0, m); + break; + + case CPE_CMD_SEND_MSG_COMPLETE: + hdr = CMI_GET_HEADER(t_info->tgt->outbox); + service = CMI_HDR_GET_SERVICE(hdr); + pr_debug("%s: msg send success, notifying clients\n", + __func__); + cpe_command_cleanup(command_node); + t_info->pending = NULL; + cpe_change_state(t_info, + CPE_STATE_IDLE, CPE_SS_IDLE); + cpe_notify_cmi_client(t_info, + t_info->tgt->outbox, CPE_SVC_SUCCESS); + break; + + case CPE_CMD_PROC_INCOMING_MSG: + rc = cpe_process_incoming(t_info); + break; + + case CPE_CMD_KILL_THREAD: + rc = cpe_process_kill_thread(t_info, command_node); + break; + + default: + pr_err("%s: unhandled cpe cmd = %d\n", + __func__, command_node->command); + break; + } + + if (cpe_rc != CPE_SVC_SUCCESS) { + pr_err("%s: failed to execute command\n", __func__); + if (t_info->pending) { + m = (struct cpe_send_msg *)t_info->pending; + cpe_notify_cmi_client(t_info, m->payload, + CPE_SVC_FAILED); + t_info->pending = NULL; + } + + cpe_command_cleanup(command_node); + rc = CPE_PROC_FAILED; + cpe_change_state(t_info, CPE_STATE_IDLE, + CPE_SS_IDLE); + } + + return rc; +} + +static enum cpe_svc_result cpe_mt_validate_cmd( + const struct cpe_info *t_info, + enum cpe_command command) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + if ((t_info == NULL) || t_info->initialized == false) { + pr_err("%s: cpe service is not ready\n", + __func__); + return CPE_SVC_NOT_READY; + } + + switch (t_info->state) { + case CPE_STATE_UNINITIALIZED: + case CPE_STATE_INITIALIZED: + switch (command) { + case CPE_CMD_RESET: + case CPE_CMD_DL_SEGMENT: + case CPE_CMD_RAMDUMP: + case CPE_CMD_PROCESS_IRQ: + case CPE_CMD_KILL_THREAD: + case CPE_CMD_DEINITIALIZE: + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_SUCCESS; + break; + default: + rc = CPE_SVC_NOT_READY; + break; + } + break; + + case CPE_STATE_DOWNLOADING: + switch (command) { + case CPE_CMD_RESET: + case CPE_CMD_DL_SEGMENT: + case CPE_CMD_BOOT: + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_SUCCESS; + break; + default: + rc = CPE_SVC_NOT_READY; + break; + } + break; + + case CPE_STATE_BOOTING: + switch (command) { + case CPE_CMD_PROCESS_IRQ: + case CPE_CMD_BOOT_INITIALIZE: + case CPE_CMD_BOOT_COMPLETE: + case CPE_CMD_SHUTDOWN: + rc = CPE_SVC_SUCCESS; + break; + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_BUSY; + break; + default: + rc = CPE_SVC_NOT_READY; + break; + } + break; + + case CPE_STATE_IDLE: + switch (command) { + case CPE_CMD_SEND_MSG: + case CPE_CMD_SEND_TRANS_MSG: + case CPE_CMD_SEND_MSG_COMPLETE: + case CPE_CMD_PROCESS_IRQ: + case CPE_CMD_RESET: + case CPE_CMD_SHUTDOWN: + case CPE_CMD_KILL_THREAD: + case CPE_CMD_PROC_INCOMING_MSG: + rc = CPE_SVC_SUCCESS; + break; + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_BUSY; + break; + default: + rc = CPE_SVC_FAILED; + break; + } + break; + + case CPE_STATE_SENDING_MSG: + switch (command) { + case CPE_CMD_SEND_MSG: + case CPE_CMD_SEND_TRANS_MSG: + case CPE_CMD_SEND_MSG_COMPLETE: + case CPE_CMD_PROCESS_IRQ: + case CPE_CMD_SHUTDOWN: + case CPE_CMD_KILL_THREAD: + case CPE_CMD_PROC_INCOMING_MSG: + rc = CPE_SVC_SUCCESS; + break; + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_BUSY; + break; + default: + rc = CPE_SVC_FAILED; + break; + } + break; + + case CPE_STATE_OFFLINE: + switch (command) { + case CPE_CMD_RESET: + case CPE_CMD_RAMDUMP: + case CPE_CMD_KILL_THREAD: + rc = CPE_SVC_SUCCESS; + break; + default: + rc = CPE_SVC_NOT_READY; + break; + } + break; + + default: + pr_debug("%s: unhandled state %d\n", + __func__, t_info->state); + break; + } + + if (rc != CPE_SVC_SUCCESS) + pr_err("%s: invalid command %d, state = %d\n", + __func__, command, t_info->state); + return rc; +} + +void *cpe_svc_initialize( + void irq_control_callback(u32 enable), + const void *codec_info, void *context) +{ + struct cpe_info *t_info = NULL; + const struct cpe_svc_hw_cfg *cap = NULL; + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_svc_init_param *init_context = + (struct cpe_svc_init_param *) context; + void *client_context = NULL; + + if (cpe_d.cpe_default_handle && + cpe_d.cpe_default_handle->initialized == true) + return (void *)cpe_d.cpe_default_handle; + cpe_d.cpe_query_freq_plans_cb = NULL; + cpe_d.cpe_change_freq_plan_cb = NULL; + + if (context) { + client_context = init_context->context; + switch (init_context->version) { + case CPE_SVC_INIT_PARAM_V1: + cpe_d.cpe_query_freq_plans_cb = + init_context->query_freq_plans_cb; + cpe_d.cpe_change_freq_plan_cb = + init_context->change_freq_plan_cb; + break; + default: + break; + } + } + + if (!cpe_d.cpe_default_handle) { + cpe_d.cpe_default_handle = kzalloc(sizeof(struct cpe_info), + GFP_KERNEL); + if (!cpe_d.cpe_default_handle) + goto err_register; + + memset(cpe_d.cpe_default_handle, 0, + sizeof(struct cpe_info)); + } + + t_info = cpe_d.cpe_default_handle; + t_info->client_context = client_context; + + INIT_LIST_HEAD(&t_info->client_list); + cpe_d.cdc_priv = client_context; + INIT_WORK(&t_info->clk_plan_work, cpe_clk_plan_work); + init_completion(&t_info->core_svc_cmd_compl); + + t_info->tgt = kzalloc(sizeof(struct cpe_svc_tgt_abstraction), + GFP_KERNEL); + if (!t_info->tgt) + goto err_tgt_alloc; + t_info->codec_id = + ((struct cpe_svc_codec_info_v1 *) codec_info)->id; + + rc = cpe_svc_tgt_init((struct cpe_svc_codec_info_v1 *)codec_info, + t_info->tgt); + + if (rc != CPE_SVC_SUCCESS) + goto err_tgt_init; + + cap = t_info->tgt->tgt_get_cpe_info(); + + memset(t_info->tgt->outbox, 0, cap->outbox_size); + memset(t_info->tgt->inbox, 0, cap->inbox_size); + mutex_init(&t_info->msg_lock); + cpe_d.cpe_irq_control_callback = irq_control_callback; + t_info->cpe_process_command = cpe_mt_process_cmd; + t_info->cpe_cmd_validate = cpe_mt_validate_cmd; + t_info->cpe_start_notification = broadcast_boot_event; + mutex_init(&cpe_d.cpe_api_mutex); + mutex_init(&cpe_d.cpe_svc_lock); + pr_debug("%s: cpe services initialized\n", __func__); + t_info->state = CPE_STATE_INITIALIZED; + t_info->initialized = true; + + return t_info; + +err_tgt_init: + kfree(t_info->tgt); + +err_tgt_alloc: + kfree(cpe_d.cpe_default_handle); + cpe_d.cpe_default_handle = NULL; + +err_register: + return NULL; +} + +enum cpe_svc_result cpe_svc_deinitialize(void *cpe_handle) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_DEINITIALIZE); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Invalid command %d\n", + __func__, CPE_CMD_DEINITIALIZE); + return rc; + } + + if (cpe_d.cpe_default_handle == t_info) + cpe_d.cpe_default_handle = NULL; + + t_info->tgt->tgt_deinit(t_info->tgt); + cpe_change_state(t_info, CPE_STATE_UNINITIALIZED, + CPE_SS_IDLE); + mutex_destroy(&t_info->msg_lock); + kfree(t_info->tgt); + kfree(t_info); + mutex_destroy(&cpe_d.cpe_api_mutex); + mutex_destroy(&cpe_d.cpe_svc_lock); + + return rc; +} + +void *cpe_svc_register(void *cpe_handle, + void (*notification_callback) + (const struct cpe_svc_notification *parameter), + u32 mask, const char *name) +{ + void *reg_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!cpe_d.cpe_default_handle) { + cpe_d.cpe_default_handle = kzalloc(sizeof(struct cpe_info), + GFP_KERNEL); + if (!cpe_d.cpe_default_handle) { + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return NULL; + } + + memset(cpe_d.cpe_default_handle, 0, + sizeof(struct cpe_info)); + } + + if (!cpe_handle) + cpe_handle = cpe_d.cpe_default_handle; + + reg_handle = cpe_register_generic((struct cpe_info *)cpe_handle, + notification_callback, + NULL, + mask, CPE_NO_SERVICE, name); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return reg_handle; +} + +enum cpe_svc_result cpe_svc_deregister(void *cpe_handle, void *reg_handle) +{ + enum cpe_svc_result rc; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!cpe_handle) + cpe_handle = cpe_d.cpe_default_handle; + + rc = cpe_deregister_generic((struct cpe_info *)cpe_handle, + reg_handle); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +enum cpe_svc_result cpe_svc_download_segment(void *cpe_handle, + const struct cpe_svc_mem_segment *segment) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_DL_SEGMENT); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_DL_SEGMENT); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; + } + + cpe_toggle_irq_notification(t_info, false); + t_info->state = CPE_STATE_DOWNLOADING; + t_info->substate = CPE_SS_DL_DOWNLOADING; + rc = t_info->tgt->tgt_write_ram(t_info, segment); + cpe_toggle_irq_notification(t_info, true); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +enum cpe_svc_result cpe_svc_boot(void *cpe_handle, int debug_mode) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_BOOT); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_BOOT); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; + } + + if (rc == CPE_SVC_SUCCESS) { + t_info->tgt->tgt_boot(debug_mode); + t_info->state = CPE_STATE_BOOTING; + t_info->substate = CPE_SS_BOOT; + pr_debug("%s: cpe service booting\n", + __func__); + } + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +enum cpe_svc_result cpe_svc_process_irq(void *cpe_handle, u32 cpe_irq) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + cpe_toggle_irq_notification(t_info, false); + cpe_process_irq_int(cpe_irq, t_info); + cpe_toggle_irq_notification(t_info, true); + + return rc; +} + +enum cpe_svc_result cpe_svc_route_notification(void *cpe_handle, + enum cpe_svc_module module, enum cpe_svc_route_dest dest) +{ + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + enum cpe_svc_result rc = CPE_SVC_NOT_READY; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + if (t_info->tgt) + rc = t_info->tgt->tgt_route_notification(module, dest); + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +static enum cpe_svc_result __cpe_svc_shutdown(void *cpe_handle) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + struct cpe_command_node *n = NULL; + struct cpe_command_node kill_cmd; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_SHUTDOWN); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_SHUTDOWN); + return rc; + } + + while (!list_empty(&t_info->main_queue)) { + n = list_first_entry(&t_info->main_queue, + struct cpe_command_node, list); + + if (n->command == CPE_CMD_SEND_MSG) { + cpe_notify_cmi_client(t_info, (u8 *)n->data, + CPE_SVC_SHUTTING_DOWN); + } + /* + * Since command cannot be processed, + * delete it from the list and perform cleanup + */ + list_del(&n->list); + cpe_command_cleanup(n); + kfree(n); + } + + pr_debug("%s: cpe service OFFLINE state\n", __func__); + + t_info->state = CPE_STATE_OFFLINE; + t_info->substate = CPE_SS_IDLE; + + memset(&kill_cmd, 0, sizeof(kill_cmd)); + kill_cmd.command = CPE_CMD_KILL_THREAD; + + if (t_info->pending) { + struct cpe_send_msg *m = + (struct cpe_send_msg *)t_info->pending; + cpe_notify_cmi_client(t_info, m->payload, + CPE_SVC_SHUTTING_DOWN); + kfree(t_info->pending); + t_info->pending = NULL; + } + + cpe_cleanup_worker_thread(t_info); + t_info->cpe_process_command(&kill_cmd); + + return rc; +} + +enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + rc = __cpe_svc_shutdown(cpe_handle); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +enum cpe_svc_result cpe_svc_reset(void *cpe_handle) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_RESET); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_RESET); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; + } + + if (t_info && t_info->tgt) { + rc = t_info->tgt->tgt_reset(); + pr_debug("%s: cpe services in INITIALIZED state\n", + __func__); + t_info->state = CPE_STATE_INITIALIZED; + t_info->substate = CPE_SS_IDLE; + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +enum cpe_svc_result cpe_svc_ramdump(void *cpe_handle, + struct cpe_svc_mem_segment *buffer) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_RAMDUMP); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_RAMDUMP); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; + } + + if (t_info->tgt) { + rc = t_info->tgt->tgt_read_ram(t_info, buffer); + } else { + pr_err("%s: cpe service not ready\n", __func__); + rc = CPE_SVC_NOT_READY; + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +enum cpe_svc_result cpe_svc_set_debug_mode(void *cpe_handle, u32 mode) +{ + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + enum cpe_svc_result rc = CPE_SVC_INVALID_HANDLE; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + if (t_info->tgt) + rc = t_info->tgt->tgt_set_debug_mode(mode); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +const struct cpe_svc_hw_cfg *cpe_svc_get_hw_cfg(void *cpe_handle) +{ + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + if (t_info->tgt) + return t_info->tgt->tgt_get_cpe_info(); + + return NULL; +} + +void *cmi_register( + void notification_callback( + const struct cmi_api_notification *parameter), + u32 service) +{ + void *reg_handle = NULL; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + reg_handle = cpe_register_generic(cpe_d.cpe_default_handle, + NULL, + notification_callback, + (CPE_SVC_CMI_MSG | CPE_SVC_OFFLINE | + CPE_SVC_ONLINE), + service, + "CMI_CLIENT"); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return reg_handle; +} + +enum cmi_api_result cmi_deregister(void *reg_handle) +{ + u32 clients = 0; + struct cpe_notif_node *n = NULL; + enum cmi_api_result rc = CMI_API_SUCCESS; + struct cpe_svc_notification payload; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + rc = (enum cmi_api_result) cpe_deregister_generic( + cpe_d.cpe_default_handle, reg_handle); + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + list_for_each_entry(n, &cpe_d.cpe_default_handle->client_list, list) { + if (n->mask & CPE_SVC_CMI_MSG) + clients++; + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + + if (clients == 0) { + payload.event = CPE_SVC_CMI_CLIENTS_DEREG; + payload.payload = NULL; + payload.result = CPE_SVC_SUCCESS; + cpe_broadcast_notification(cpe_d.cpe_default_handle, &payload); + } + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +enum cmi_api_result cmi_send_msg(void *message) +{ + enum cmi_api_result rc = CMI_API_SUCCESS; + struct cpe_send_msg *msg = NULL; + struct cmi_hdr *hdr; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + hdr = CMI_GET_HEADER(message); + msg = kzalloc(sizeof(struct cpe_send_msg), + GFP_ATOMIC); + if (!msg) { + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return CPE_SVC_NO_MEMORY; + } + + if (CMI_HDR_GET_OBM_FLAG(hdr) == CMI_OBM_FLAG_OUT_BAND) + msg->isobm = 1; + else + msg->isobm = 0; + + msg->size = sizeof(struct cmi_hdr) + + CMI_HDR_GET_PAYLOAD_SIZE(hdr); + + msg->payload = kzalloc(msg->size, GFP_ATOMIC); + if (!msg->payload) { + kfree(msg); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return CPE_SVC_NO_MEMORY; + } + + msg->address = 0; + memcpy((void *)msg->payload, message, msg->size); + + rc = (enum cmi_api_result) cpe_send_cmd_to_thread( + cpe_d.cpe_default_handle, + CPE_CMD_SEND_MSG, + (void *)msg, false); + + if (rc != 0) { + pr_err("%s: Failed to queue message\n", __func__); + kfree(msg->payload); + kfree(msg); + } + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +enum cpe_svc_result cpe_svc_ftm_test(void *cpe_handle, u32 *status) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + struct cpe_svc_mem_segment backup_seg; + struct cpe_svc_mem_segment waiti_seg; + u8 *backup_data = NULL; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_FTM_TEST); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_FTM_TEST); + goto fail_cmd; + } + + if (t_info && t_info->tgt) { + backup_data = kzalloc( + t_info->tgt->tgt_waiti_info->tgt_waiti_size, + GFP_KERNEL); + + /* CPE reset */ + rc = t_info->tgt->tgt_reset(); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: CPE reset fail! err = %d\n", + __func__, rc); + goto err_return; + } + + /* Back up the 4 byte IRAM data first */ + backup_seg.type = CPE_SVC_INSTRUCTION_MEM; + backup_seg.cpe_addr = + t_info->tgt->tgt_get_cpe_info()->IRAM_offset; + backup_seg.size = t_info->tgt->tgt_waiti_info->tgt_waiti_size; + backup_seg.data = backup_data; + + pr_debug("%s: Backing up IRAM data from CPE\n", + __func__); + + rc = t_info->tgt->tgt_read_ram(t_info, &backup_seg); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Fail to backup CPE IRAM data, err = %d\n", + __func__, rc); + goto err_return; + } + + pr_debug("%s: Complete backing up IRAM data from CPE\n", + __func__); + + /* Write the WAITI instruction data */ + waiti_seg.type = CPE_SVC_INSTRUCTION_MEM; + waiti_seg.cpe_addr = + t_info->tgt->tgt_get_cpe_info()->IRAM_offset; + waiti_seg.size = t_info->tgt->tgt_waiti_info->tgt_waiti_size; + waiti_seg.data = t_info->tgt->tgt_waiti_info->tgt_waiti_data; + + rc = t_info->tgt->tgt_write_ram(t_info, &waiti_seg); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Fail to write the WAITI data, err = %d\n", + __func__, rc); + goto restore_iram; + } + + /* Boot up cpe to execute the WAITI instructions */ + rc = t_info->tgt->tgt_boot(1); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Fail to boot CPE, err = %d\n", + __func__, rc); + goto reset; + } + + /* + * 1ms delay is suggested by the hw team to + * wait for cpe to boot up. + */ + usleep_range(1000, 1100); + + /* Check if the cpe init is done after executing the WAITI */ + *status = t_info->tgt->tgt_cpar_init_done(); + +reset: + /* Set the cpe back to reset state */ + rc = t_info->tgt->tgt_reset(); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: CPE reset fail! err = %d\n", + __func__, rc); + goto restore_iram; + } + +restore_iram: + /* Restore the IRAM 4 bytes data */ + rc = t_info->tgt->tgt_write_ram(t_info, &backup_seg); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Fail to restore the IRAM data, err = %d\n", + __func__, rc); + goto err_return; + } + } + +err_return: + kfree(backup_data); +fail_cmd: + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_boot(int debug_mode) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + if (!debug_mode) + rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_WDOG_CFG, + 0x3F, 0x31); + else + pr_info("%s: CPE in debug mode, WDOG disabled\n", + __func__); + + rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL, + 0x02, 0x00); + rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL, + 0x0C, 0x04); + rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_CFG, + 0x01, 0x01); + + return rc; +} + +static u32 cpe_tgt_tomtom_is_cpar_init_done(void) +{ + u8 status = 0; + + cpe_register_read(TOMTOM_A_SVASS_STATUS, &status); + return status & 0x01; +} + +static u32 cpe_tgt_tomtom_is_active(void) +{ + u8 status = 0; + + cpe_register_read(TOMTOM_A_SVASS_STATUS, &status); + return status & 0x04; +} + +static enum cpe_svc_result cpe_tgt_tomtom_reset(void) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_WDOG_CFG, + 0x30, 0x00); + + rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_CFG, + 0x01, 0x00); + rc = cpe_update_bits(TOMTOM_A_MEM_LEAKAGE_CTL, + 0x07, 0x03); + rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL, + 0x08, 0x08); + rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL, + 0x02, 0x02); + return rc; +} + +enum cpe_svc_result cpe_tgt_tomtom_voicetx(bool enable) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 val = 0; + + if (enable) + val = 0x02; + else + val = 0x00; + rc = cpe_update_bits(TOMTOM_A_SVASS_CFG, + 0x02, val); + val = 0; + cpe_register_read(TOMTOM_A_SVASS_CFG, &val); + return rc; +} + +enum cpe_svc_result cpe_svc_toggle_lab(void *cpe_handle, bool enable) +{ + + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + if (t_info->tgt) + return t_info->tgt->tgt_voice_tx_lab(enable); + else + return CPE_SVC_INVALID_HANDLE; +} + +static enum cpe_svc_result cpe_tgt_tomtom_read_mailbox(u8 *buffer, + size_t size) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 cnt = 0; + + if (size >= TOMTOM_A_SVASS_SPE_OUTBOX_SIZE) + size = TOMTOM_A_SVASS_SPE_OUTBOX_SIZE - 1; + for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) { + rc = cpe_register_read(TOMTOM_A_SVASS_SPE_OUTBOX(cnt), + &(buffer[cnt])); + } + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_write_mailbox(u8 *buffer, + size_t size) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 cnt = 0; + + if (size >= TOMTOM_A_SVASS_SPE_INBOX_SIZE) + size = TOMTOM_A_SVASS_SPE_INBOX_SIZE - 1; + for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) { + rc = cpe_register_write(TOMTOM_A_SVASS_SPE_INBOX(cnt), + buffer[cnt]); + } + + if (rc == CPE_SVC_SUCCESS) + rc = cpe_register_write(TOMTOM_A_SVASS_SPE_INBOX_TRG, 1); + + return rc; +} + +static enum cpe_svc_result cpe_get_mem_addr(struct cpe_info *t_info, + const struct cpe_svc_mem_segment *mem_seg, + u32 *addr, u8 *mem) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 offset, mem_sz, address; + u8 mem_type; + + switch (mem_seg->type) { + + case CPE_SVC_DATA_MEM: + mem_type = MEM_ACCESS_DRAM_VAL; + offset = TOMTOM_A_SVASS_SPE_DRAM_OFFSET; + mem_sz = TOMTOM_A_SVASS_SPE_DRAM_SIZE; + break; + + case CPE_SVC_INSTRUCTION_MEM: + mem_type = MEM_ACCESS_IRAM_VAL; + offset = TOMTOM_A_SVASS_SPE_IRAM_OFFSET; + mem_sz = TOMTOM_A_SVASS_SPE_IRAM_SIZE; + break; + + default: + pr_err("%s: Invalid mem type = %u\n", + __func__, mem_seg->type); + return CPE_SVC_INVALID_HANDLE; + } + + if (mem_seg->cpe_addr < offset) { + pr_err("%s: Invalid addr %x for mem type %u\n", + __func__, mem_seg->cpe_addr, mem_type); + return CPE_SVC_INVALID_HANDLE; + } + + address = mem_seg->cpe_addr - offset; + if (address + mem_seg->size > mem_sz) { + pr_err("%s: wrong size %zu, start address %x, mem_type %u\n", + __func__, mem_seg->size, address, mem_type); + return CPE_SVC_INVALID_HANDLE; + } + + (*addr) = address; + (*mem) = mem_type; + + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_read_RAM(struct cpe_info *t_info, + struct cpe_svc_mem_segment *mem_seg) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 mem_reg_val = 0; + u32 cnt = 0; + bool autoinc; + u8 mem = MEM_ACCESS_NONE_VAL; + u32 addr = 0; + u32 ptr_update = true; + + if (!mem_seg) { + pr_err("%s: Invalid mem segment\n", + __func__); + return CPE_SVC_INVALID_HANDLE; + } + + rc = cpe_get_mem_addr(t_info, mem_seg, &addr, &mem); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Cannot obtain address, mem_type %u\n", + __func__, mem_seg->type); + return rc; + } + + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0); + autoinc = cpe_register_read_autoinc_supported(); + if (autoinc) + mem_reg_val |= 0x04; + + mem_reg_val |= 0x08; + mem_reg_val |= mem; + + do { + if (!autoinc || ptr_update) { + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR0, + (addr & 0xFF)); + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR1, + ((addr >> 8) & 0xFF)); + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR2, + ((addr >> 16) & 0xFF)); + + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, + mem_reg_val); + + ptr_update = false; + } + rc = cpe_register_read(TOMTOM_A_SVASS_MEM_BANK, + &mem_seg->data[cnt]); + + if (!autoinc) + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0); + } while (++cnt < mem_seg->size); + + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0); + + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_write_RAM(struct cpe_info *t_info, + const struct cpe_svc_mem_segment *mem_seg) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 mem_reg_val = 0; + u8 mem = MEM_ACCESS_NONE_VAL; + u32 addr = 0; + u8 *temp_ptr = NULL; + u32 temp_size = 0; + bool autoinc; + + if (!mem_seg) { + pr_err("%s: Invalid mem segment\n", + __func__); + return CPE_SVC_INVALID_HANDLE; + } + + rc = cpe_get_mem_addr(t_info, mem_seg, &addr, &mem); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Cannot obtain address, mem_type %u\n", + __func__, mem_seg->type); + return rc; + } + + autoinc = cpe_register_read_autoinc_supported(); + if (autoinc) + mem_reg_val |= 0x04; + mem_reg_val |= mem; + + rc = cpe_update_bits(TOMTOM_A_SVASS_MEM_CTL, + 0x0F, mem_reg_val); + + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR0, + (addr & 0xFF)); + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR1, + ((addr >> 8) & 0xFF)); + + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR2, + ((addr >> 16) & 0xFF)); + + temp_size = 0; + temp_ptr = mem_seg->data; + + while (temp_size <= mem_seg->size) { + u32 to_write = (mem_seg->size >= temp_size+CHUNK_SIZE) + ? CHUNK_SIZE : (mem_seg->size-temp_size); + + if (t_info->state == CPE_STATE_OFFLINE) { + pr_err("%s: CPE is offline\n", __func__); + return CPE_SVC_FAILED; + } + + cpe_register_write_repeat(TOMTOM_A_SVASS_MEM_BANK, + temp_ptr, to_write); + temp_size += CHUNK_SIZE; + temp_ptr += CHUNK_SIZE; + } + + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0); + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_route_notification( + enum cpe_svc_module module, + enum cpe_svc_route_dest dest) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 ctl_reg_val = 0; + + switch (module) { + case CPE_SVC_LISTEN_PROC: + switch (dest) { + case CPE_SVC_EXTERNAL: + ctl_reg_val = LISTEN_CTL_MSM_VAL; + break; + case CPE_SVC_INTERNAL: + ctl_reg_val = LISTEN_CTL_SPE_VAL; + break; + default: + pr_err("%s: Invalid dest %d\n", + __func__, dest); + return CPE_SVC_FAILED; + } + + rc = cpe_update_bits(TOMTOM_A_SVASS_CFG, + 0x01, ctl_reg_val); + break; + default: + pr_err("%s: Invalid module %d\n", + __func__, module); + rc = CPE_SVC_FAILED; + break; + } + + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_set_debug_mode(u32 enable) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 dbg_reg_val = 0x00; + + if (enable) + dbg_reg_val = 0x08; + rc = cpe_update_bits(TOMTOM_A_SVASS_DEBUG, + 0x08, dbg_reg_val); + return rc; +} + +static const struct cpe_svc_hw_cfg *cpe_tgt_tomtom_get_cpe_info(void) +{ + return &cpe_svc_tomtom_info; +} + +static enum cpe_svc_result cpe_tgt_tomtom_deinit( + struct cpe_svc_tgt_abstraction *param) +{ + kfree(param->inbox); + param->inbox = NULL; + kfree(param->outbox); + param->outbox = NULL; + memset(param, 0, sizeof(struct cpe_svc_tgt_abstraction)); + return CPE_SVC_SUCCESS; +} + +static u8 cpe_tgt_tomtom_waiti_data[] = {0x00, 0x70, 0x00, 0x00}; + +static struct cpe_tgt_waiti_info cpe_tgt_tomtom_waiti_info = { + .tgt_waiti_size = ARRAY_SIZE(cpe_tgt_tomtom_waiti_data), + .tgt_waiti_data = cpe_tgt_tomtom_waiti_data, +}; + +static enum cpe_svc_result cpe_tgt_tomtom_init( + struct cpe_svc_codec_info_v1 *codec_info, + struct cpe_svc_tgt_abstraction *param) +{ + if (!codec_info) + return CPE_SVC_INVALID_HANDLE; + if (!param) + return CPE_SVC_INVALID_HANDLE; + + if (codec_info->id == CPE_SVC_CODEC_TOMTOM) { + param->tgt_boot = cpe_tgt_tomtom_boot; + param->tgt_cpar_init_done = cpe_tgt_tomtom_is_cpar_init_done; + param->tgt_is_active = cpe_tgt_tomtom_is_active; + param->tgt_reset = cpe_tgt_tomtom_reset; + param->tgt_read_mailbox = cpe_tgt_tomtom_read_mailbox; + param->tgt_write_mailbox = cpe_tgt_tomtom_write_mailbox; + param->tgt_read_ram = cpe_tgt_tomtom_read_RAM; + param->tgt_write_ram = cpe_tgt_tomtom_write_RAM; + param->tgt_route_notification = + cpe_tgt_tomtom_route_notification; + param->tgt_set_debug_mode = cpe_tgt_tomtom_set_debug_mode; + param->tgt_get_cpe_info = cpe_tgt_tomtom_get_cpe_info; + param->tgt_deinit = cpe_tgt_tomtom_deinit; + param->tgt_voice_tx_lab = cpe_tgt_tomtom_voicetx; + param->tgt_waiti_info = &cpe_tgt_tomtom_waiti_info; + + param->inbox = kzalloc(TOMTOM_A_SVASS_SPE_INBOX_SIZE, + GFP_KERNEL); + if (!param->inbox) + return CPE_SVC_NO_MEMORY; + + param->outbox = kzalloc(TOMTOM_A_SVASS_SPE_OUTBOX_SIZE, + GFP_KERNEL); + if (!param->outbox) { + kfree(param->inbox); + return CPE_SVC_NO_MEMORY; + } + } + + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_boot(int debug_mode) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + if (!debug_mode) + rc |= cpe_update_bits( + WCD9335_CPE_SS_WDOG_CFG, + 0x3f, 0x31); + else + pr_info("%s: CPE in debug mode, WDOG disabled\n", + __func__); + + rc |= cpe_register_write(WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 19); + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x04, 0x00); + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x02, 0x02); + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x01, 0x01); + + if (unlikely(rc)) { + pr_err("%s: Failed to boot, err = %d\n", + __func__, rc); + rc = CPE_SVC_FAILED; + } + + return rc; +} + +static u32 cpe_tgt_wcd9335_is_cpar_init_done(void) +{ + u8 temp = 0; + + cpe_register_read(WCD9335_CPE_SS_STATUS, &temp); + return temp & 0x1; +} + +static u32 cpe_tgt_wcd9335_is_active(void) +{ + u8 temp = 0; + + cpe_register_read(WCD9335_CPE_SS_STATUS, &temp); + return temp & 0x4; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_reset(void) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CFG, 0x01, 0x00); + + rc |= cpe_register_write( + WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN, 0x00); + rc |= cpe_register_write( + WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, 0x00); + rc |= cpe_register_write( + WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1, 0x00); + rc |= cpe_register_write( + WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2, 0x00); + + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x04, 0x04); + + if (unlikely(rc)) { + pr_err("%s: failed to reset cpe, err = %d\n", + __func__, rc); + rc = CPE_SVC_FAILED; + } + + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_read_mailbox(u8 *buffer, + size_t size) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 cnt = 0; + + pr_debug("%s: size=%zd\n", __func__, size); + + if (size > WCD9335_CPE_SS_SPE_OUTBOX_SIZE) + size = WCD9335_CPE_SS_SPE_OUTBOX_SIZE; + + for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) + rc = cpe_register_read(WCD9335_CPE_SS_SPE_OUTBOX1(cnt), + &buffer[cnt]); + + rc = cpe_register_write(WCD9335_CPE_SS_OUTBOX1_ACK, 0x01); + + if (unlikely(rc)) { + pr_err("%s: failed to ACK outbox, err = %d\n", + __func__, rc); + rc = CPE_SVC_FAILED; + } + + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_write_mailbox(u8 *buffer, + size_t size) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 cnt = 0; + + pr_debug("%s: size = %zd\n", __func__, size); + if (size > WCD9335_CPE_SS_SPE_INBOX_SIZE) + size = WCD9335_CPE_SS_SPE_INBOX_SIZE; + for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) { + rc |= cpe_register_write(WCD9335_CPE_SS_SPE_INBOX1(cnt), + buffer[cnt]); + } + + if (unlikely(rc)) { + pr_err("%s: Error %d writing mailbox registers\n", + __func__, rc); + return rc; + } + + rc = cpe_register_write(WCD9335_CPE_SS_INBOX1_TRG, 1); + return rc; +} + +static enum cpe_svc_result cpe_wcd9335_get_mem_addr(struct cpe_info *t_info, + const struct cpe_svc_mem_segment *mem_seg, + u32 *addr, u8 *mem) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 offset, mem_sz, address; + u8 mem_type; + + switch (mem_seg->type) { + case CPE_SVC_DATA_MEM: + mem_type = MEM_ACCESS_DRAM_VAL; + offset = WCD9335_CPE_SS_SPE_DRAM_OFFSET; + mem_sz = WCD9335_CPE_SS_SPE_DRAM_SIZE; + break; + + case CPE_SVC_INSTRUCTION_MEM: + mem_type = MEM_ACCESS_IRAM_VAL; + offset = WCD9335_CPE_SS_SPE_IRAM_OFFSET; + mem_sz = WCD9335_CPE_SS_SPE_IRAM_SIZE; + break; + + default: + pr_err("%s: Invalid mem type = %u\n", + __func__, mem_seg->type); + return CPE_SVC_INVALID_HANDLE; + } + + if (mem_seg->cpe_addr < offset) { + pr_err("%s: Invalid addr %x for mem type %u\n", + __func__, mem_seg->cpe_addr, mem_type); + return CPE_SVC_INVALID_HANDLE; + } + + address = mem_seg->cpe_addr - offset; + if (address + mem_seg->size > mem_sz) { + pr_err("%s: wrong size %zu, start address %x, mem_type %u\n", + __func__, mem_seg->size, address, mem_type); + return CPE_SVC_INVALID_HANDLE; + } + + (*addr) = address; + (*mem) = mem_type; + + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_read_RAM(struct cpe_info *t_info, + struct cpe_svc_mem_segment *mem_seg) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 temp = 0; + u32 cnt = 0; + u8 mem = 0x0; + u32 addr = 0; + u32 lastaddr = 0; + u32 ptr_update = true; + bool autoinc; + + if (!mem_seg) { + pr_err("%s: Invalid buffer\n", __func__); + return CPE_SVC_INVALID_HANDLE; + } + + rc = cpe_wcd9335_get_mem_addr(t_info, mem_seg, &addr, &mem); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Cannot obtain address, mem_type %u\n", + __func__, mem_seg->type); + return rc; + } + + rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + autoinc = cpe_register_read_autoinc_supported(); + + if (autoinc) + temp = 0x18; + else + temp = 0x10; + + temp |= mem; + + lastaddr = ~addr; + do { + if (!autoinc || (ptr_update)) { + /* write LSB only if modified */ + if ((lastaddr & 0xFF) != (addr & 0xFF)) + rc |= cpe_register_write( + WCD9335_CPE_SS_MEM_PTR_0, + (addr & 0xFF)); + /* write middle byte only if modified */ + if (((lastaddr >> 8) & 0xFF) != ((addr >> 8) & 0xFF)) + rc |= cpe_register_write( + WCD9335_CPE_SS_MEM_PTR_1, + ((addr>>8) & 0xFF)); + /* write MSB only if modified */ + if (((lastaddr >> 16) & 0xFF) != ((addr >> 16) & 0xFF)) + rc |= cpe_register_write( + WCD9335_CPE_SS_MEM_PTR_2, + ((addr>>16) & 0xFF)); + + rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, temp); + lastaddr = addr; + addr++; + ptr_update = false; + } + + rc |= cpe_register_read(WCD9335_CPE_SS_MEM_BANK_0, + &mem_seg->data[cnt]); + + if (!autoinc) + rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + } while ((++cnt < mem_seg->size) || + (rc != CPE_SVC_SUCCESS)); + + rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + + if (rc) + pr_err("%s: Failed to read registers, err = %d\n", + __func__, rc); + + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_write_RAM(struct cpe_info *t_info, + const struct cpe_svc_mem_segment *mem_seg) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 mem_reg_val = 0; + u8 mem = MEM_ACCESS_NONE_VAL; + u32 addr = 0; + u8 *temp_ptr = NULL; + u32 temp_size = 0; + bool autoinc; + + if (!mem_seg) { + pr_err("%s: Invalid mem segment\n", + __func__); + return CPE_SVC_INVALID_HANDLE; + } + + rc = cpe_wcd9335_get_mem_addr(t_info, mem_seg, &addr, &mem); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Cannot obtain address, mem_type %u\n", + __func__, mem_seg->type); + return rc; + } + + autoinc = cpe_register_read_autoinc_supported(); + if (autoinc) + mem_reg_val = 0x18; + else + mem_reg_val = 0x10; + + mem_reg_val |= mem; + + rc = cpe_update_bits(WCD9335_CPE_SS_MEM_CTRL, + 0x0F, mem_reg_val); + + rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_0, + (addr & 0xFF)); + rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_1, + ((addr >> 8) & 0xFF)); + + rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_2, + ((addr >> 16) & 0xFF)); + + temp_size = 0; + temp_ptr = mem_seg->data; + + while (temp_size <= mem_seg->size) { + u32 to_write = (mem_seg->size >= temp_size+CHUNK_SIZE) + ? CHUNK_SIZE : (mem_seg->size - temp_size); + + if (t_info->state == CPE_STATE_OFFLINE) { + pr_err("%s: CPE is offline\n", __func__); + return CPE_SVC_FAILED; + } + + cpe_register_write_repeat(WCD9335_CPE_SS_MEM_BANK_0, + temp_ptr, to_write); + temp_size += CHUNK_SIZE; + temp_ptr += CHUNK_SIZE; + } + + rc = cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + + if (rc) + pr_err("%s: Failed to write registers, err = %d\n", + __func__, rc); + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_route_notification( + enum cpe_svc_module module, + enum cpe_svc_route_dest dest) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + pr_debug("%s: Module = %d, Destination = %d\n", + __func__, module, dest); + + switch (module) { + case CPE_SVC_LISTEN_PROC: + switch (dest) { + case CPE_SVC_EXTERNAL: + rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x01, 0x01); + break; + case CPE_SVC_INTERNAL: + rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x01, 0x00); + break; + default: + pr_err("%s: Invalid destination %d\n", + __func__, dest); + return CPE_SVC_FAILED; + } + break; + default: + pr_err("%s: Invalid module %d\n", + __func__, module); + rc = CPE_SVC_FAILED; + break; + } + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_set_debug_mode(u32 enable) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + pr_debug("%s: enable = %s\n", __func__, + (enable) ? "true" : "false"); + + return rc; +} + +static const struct cpe_svc_hw_cfg *cpe_tgt_wcd9335_get_cpe_info(void) +{ + return &cpe_svc_wcd9335_info; +} + +static enum cpe_svc_result +cpe_tgt_wcd9335_deinit(struct cpe_svc_tgt_abstraction *param) +{ + kfree(param->inbox); + param->inbox = NULL; + kfree(param->outbox); + param->outbox = NULL; + memset(param, 0, sizeof(struct cpe_svc_tgt_abstraction)); + + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result + cpe_tgt_wcd9335_voicetx(bool enable) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 val = 0; + + pr_debug("%s: enable = %u\n", __func__, enable); + if (enable) + val = 0x02; + else + val = 0x00; + + rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x02, val); + val = 0; + cpe_register_read(WCD9335_CPE_SS_CFG, &val); + + return rc; +} + +static u8 cpe_tgt_wcd9335_waiti_data[] = {0x00, 0x70, 0x00, 0x00}; + +static struct cpe_tgt_waiti_info cpe_tgt_wcd9335_waiti_info = { + .tgt_waiti_size = ARRAY_SIZE(cpe_tgt_wcd9335_waiti_data), + .tgt_waiti_data = cpe_tgt_wcd9335_waiti_data, +}; + +static enum cpe_svc_result cpe_tgt_wcd9335_init( + struct cpe_svc_codec_info_v1 *codec_info, + struct cpe_svc_tgt_abstraction *param) +{ + if (!codec_info) + return CPE_SVC_INVALID_HANDLE; + if (!param) + return CPE_SVC_INVALID_HANDLE; + + if (codec_info->id == CPE_SVC_CODEC_WCD9335) { + param->tgt_boot = cpe_tgt_wcd9335_boot; + param->tgt_cpar_init_done = cpe_tgt_wcd9335_is_cpar_init_done; + param->tgt_is_active = cpe_tgt_wcd9335_is_active; + param->tgt_reset = cpe_tgt_wcd9335_reset; + param->tgt_read_mailbox = cpe_tgt_wcd9335_read_mailbox; + param->tgt_write_mailbox = cpe_tgt_wcd9335_write_mailbox; + param->tgt_read_ram = cpe_tgt_wcd9335_read_RAM; + param->tgt_write_ram = cpe_tgt_wcd9335_write_RAM; + param->tgt_route_notification = + cpe_tgt_wcd9335_route_notification; + param->tgt_set_debug_mode = cpe_tgt_wcd9335_set_debug_mode; + param->tgt_get_cpe_info = cpe_tgt_wcd9335_get_cpe_info; + param->tgt_deinit = cpe_tgt_wcd9335_deinit; + param->tgt_voice_tx_lab = cpe_tgt_wcd9335_voicetx; + param->tgt_waiti_info = &cpe_tgt_wcd9335_waiti_info; + + param->inbox = kzalloc(WCD9335_CPE_SS_SPE_INBOX_SIZE, + GFP_KERNEL); + if (!param->inbox) + return CPE_SVC_NO_MEMORY; + + param->outbox = kzalloc(WCD9335_CPE_SS_SPE_OUTBOX_SIZE, + GFP_KERNEL); + if (!param->outbox) { + kfree(param->inbox); + return CPE_SVC_NO_MEMORY; + } + } + + return CPE_SVC_SUCCESS; +} + +MODULE_DESCRIPTION("WCD CPE Services"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd_cpe_services.h b/sound/soc/codecs/wcd_cpe_services.h new file mode 100644 index 0000000000000000000000000000000000000000..68eb61996a69ccbf4da1afebd7c60b5b8713b71d --- /dev/null +++ b/sound/soc/codecs/wcd_cpe_services.h @@ -0,0 +1,179 @@ +/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __CPE_SERVICES__ +#define __CPE_SERVICES__ + +#define CPE_IRQ_OUTBOX_IRQ 0x01 +#define CPE_IRQ_MEM_ACCESS_ERROR 0x02 +#define CPE_IRQ_WDOG_BITE 0x04 +#define CPE_IRQ_BUFFER_OVERFLOW 0x08 +#define CPE_IRQ_LAB_OVFUNF 0x10 +#define CPE_IRQ_FLL_LOCK_LOST 0x20 +#define CPE_IRQ_RCO_WDOG_INT 0x40 + +#define EFAILED (MAX_ERRNO - 1) +#define ENOTREADY (MAX_ERRNO - 2) + +#define MAX_SUPPORTED_CLKFREQ 8 +#define CPE_SVC_INIT_PARAM_V1 1 + +enum cpe_svc_result { + CPE_SVC_SUCCESS = 0, + CPE_SVC_FAILED = -EFAILED, + CPE_SVC_NO_MEMORY = -ENOMEM, + CPE_SVC_INVALID_HANDLE = -EINVAL, + CPE_SVC_NOT_READY = -ENOTREADY, + CPE_SVC_SHUTTING_DOWN = -ESHUTDOWN, + CPE_SVC_BUSY = -EBUSY, +}; + +enum cpe_svc_event { + CPE_SVC_CMI_MSG = 0x01, + CPE_SVC_OFFLINE = 0x02, + CPE_SVC_ONLINE = 0x04, + CPE_SVC_BOOT_FAILED = 0x08, + CPE_SVC_READ_COMPLETE = 0x10, + CPE_SVC_READ_ERROR = 0x20, + CPE_SVC_BOOT = 0x40, + CPE_SVC_CMI_CLIENTS_DEREG = 0x100, + CPE_SVC_EVENT_ANCHOR = 0x7FFF +}; + +enum cpe_svc_module { + CPE_SVC_LISTEN_PROC = 1, + CPE_SVC_MODULE_ANCHOR = 0x7F +}; + +enum cpe_svc_route_dest { + CPE_SVC_EXTERNAL = 1, + CPE_SVC_INTERNAL = 2, + CPE_SVC_ROUTE_ANCHOR = 0x7F +}; + +enum cpe_svc_mem_type { + CPE_SVC_DATA_MEM = 1, + CPE_SVC_INSTRUCTION_MEM = 2, + CPE_SVC_IPC_MEM = 3, + CPE_SVC_MEM_TYPE_ANCHOR = 0x7F +}; + +enum cpe_svc_codec_id { + CPE_SVC_CODEC_TOMTOM = 5, + CPE_SVC_CODEC_WCD9335 = 7, + CPE_SVC_CODEC_WCD9326 = 8, + CPE_SVC_CODEC_ID_ANCHOR = 0x7ffffff +}; + +enum cpe_svc_codec_version { + CPE_SVC_CODEC_V1P0 = 1, + CPE_SVC_CODEC_VERSION_ANCHOR = 0x7fffffff +}; + +struct cpe_svc_codec_info_v1 { + u16 major_version;/*must be 1*/ + u16 minor_version;/*must be 0*/ + u32 id; + u32 version; + /*Add 1.1 version fields after this line*/ +}; + +struct cpe_svc_notification { + enum cpe_svc_event event; + enum cpe_svc_result result; + void *payload; + void *private_data; +}; + +struct cpe_svc_msg_payload { + u8 *cmi_msg; +}; + +struct cpe_svc_read_complete { + u8 *buffer; + size_t size; +}; + +struct cpe_svc_boot_event { + u32 debug_address; + size_t debug_buffer_size; + u32 status; +}; + +struct cpe_svc_mem_segment { + enum cpe_svc_mem_type type; + u32 cpe_addr; + size_t size; + u8 *data; +}; + +struct cpe_svc_hw_cfg { + size_t DRAM_size; + u32 DRAM_offset; + size_t IRAM_size; + u32 IRAM_offset; + u8 inbox_size; + u8 outbox_size; +}; + +struct cpe_svc_cfg_clk_plan { + u32 current_clk_feq; + u32 num_clk_freqs; + u32 clk_freqs[MAX_SUPPORTED_CLKFREQ]; +}; + +struct cpe_svc_init_param { + void *context; + u32 version; + void (*query_freq_plans_cb)(void *cdc_priv, + struct cpe_svc_cfg_clk_plan *clk_freq); + void (*change_freq_plan_cb)(void *cdc_priv, + u32 clk_freq); +}; + + +void *cpe_svc_initialize( + void irq_control_callback(u32 enable), + const void *codec_info, void *context); +enum cpe_svc_result cpe_svc_deinitialize(void *cpe_handle); + +void *cpe_svc_register(void *cpe_handle, + void (*notification_callback)( + const struct cpe_svc_notification *parameter), + u32 mask, const char *name); + +enum cpe_svc_result cpe_svc_deregister(void *cpe_handle, void *reg_handle); + +enum cpe_svc_result cpe_svc_download_segment(void *cpe_handle, + const struct cpe_svc_mem_segment *segment); + +enum cpe_svc_result cpe_svc_boot(void *cpe_handle, int debug_mode); + +enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle); + +enum cpe_svc_result cpe_svc_reset(void *cpe_handle); + +enum cpe_svc_result cpe_svc_process_irq(void *cpe_handle, u32 cpe_irq); + +enum cpe_svc_result +cpe_svc_route_notification(void *cpe_handle, enum cpe_svc_module module, + enum cpe_svc_route_dest dest); + +enum cpe_svc_result cpe_svc_ramdump(void *cpe_handle, + struct cpe_svc_mem_segment *buffer); + +enum cpe_svc_result cpe_svc_set_debug_mode(void *cpe_handle, u32 mode); + +const struct cpe_svc_hw_cfg *cpe_svc_get_hw_cfg(void *cpe_handle); +enum cpe_svc_result cpe_svc_toggle_lab(void *cpe_handle, bool enable); +enum cpe_svc_result cpe_svc_ftm_test(void *cpe_handle, u32 *status); +#endif /*__CPE_SERVICES__*/ diff --git a/sound/soc/codecs/wcdcal-hwdep.c b/sound/soc/codecs/wcdcal-hwdep.c new file mode 100644 index 0000000000000000000000000000000000000000..31eae69b1f4be78a4d8ec362b9729381c11e95b8 --- /dev/null +++ b/sound/soc/codecs/wcdcal-hwdep.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcdcal-hwdep.h" + +const int cal_size_info[WCD9XXX_MAX_CAL] = { + [WCD9XXX_ANC_CAL] = 16384, + [WCD9XXX_MBHC_CAL] = 4096, + [WCD9XXX_MAD_CAL] = 4096, + [WCD9XXX_VBAT_CAL] = 72, +}; + +const char *cal_name_info[WCD9XXX_MAX_CAL] = { + [WCD9XXX_ANC_CAL] = "anc", + [WCD9XXX_MBHC_CAL] = "mbhc", + [WCD9XXX_MAD_CAL] = "mad", + [WCD9XXX_VBAT_CAL] = "vbat", +}; + +struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data, + enum wcd_cal_type type) +{ + if (!fw_data) { + pr_err("%s: fw_data is NULL\n", __func__); + return NULL; + } + if (type >= WCD9XXX_MAX_CAL || + type < WCD9XXX_MIN_CAL) { + pr_err("%s: wrong cal type sent %d\n", __func__, type); + return NULL; + } + mutex_lock(&fw_data->lock); + if (!test_bit(WCDCAL_RECIEVED, + &fw_data->wcdcal_state[type])) { + pr_err("%s: cal not sent by userspace %d\n", + __func__, type); + mutex_unlock(&fw_data->lock); + return NULL; + } + mutex_unlock(&fw_data->lock); + return fw_data->fw[type]; +} +EXPORT_SYMBOL(wcdcal_get_fw_cal); + +static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw, + struct wcdcal_ioctl_buffer fw_user) +{ + struct fw_info *fw_data = hw->private_data; + struct firmware_cal **fw = fw_data->fw; + void *data; + + if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) { + pr_err("%s: codec didn't set this %d!!\n", + __func__, fw_user.cal_type); + return -EFAULT; + } + if (fw_user.cal_type >= WCD9XXX_MAX_CAL || + fw_user.cal_type < WCD9XXX_MIN_CAL) { + pr_err("%s: wrong cal type sent %d\n", + __func__, fw_user.cal_type); + return -EFAULT; + } + if (fw_user.size > cal_size_info[fw_user.cal_type] || + fw_user.size <= 0) { + pr_err("%s: incorrect firmware size %d for %s\n", + __func__, fw_user.size, + cal_name_info[fw_user.cal_type]); + return -EFAULT; + } + data = fw[fw_user.cal_type]->data; + if (copy_from_user(data, fw_user.buffer, fw_user.size)) + return -EFAULT; + fw[fw_user.cal_type]->size = fw_user.size; + mutex_lock(&fw_data->lock); + set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]); + mutex_unlock(&fw_data->lock); + return 0; +} + +#ifdef CONFIG_COMPAT +struct wcdcal_ioctl_buffer32 { + u32 size; + compat_uptr_t buffer; + enum wcd_cal_type cal_type; +}; + +enum { + SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 = + _IOW('U', 0x1, struct wcdcal_ioctl_buffer32), +}; + +static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg; + struct wcdcal_ioctl_buffer32 fw_user32; + struct wcdcal_ioctl_buffer fw_user_compat; + + if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) { + pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd); + return -ENOIOCTLCMD; + } + if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) { + pr_err("%s: failed to copy\n", __func__); + return -EFAULT; + } + fw_user_compat.size = fw_user32.size; + fw_user_compat.buffer = compat_ptr(fw_user32.buffer); + fw_user_compat.cal_type = fw_user32.cal_type; + return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat); +} +#else +#define wcdcal_hwdep_ioctl_compat NULL +#endif + +static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg; + struct wcdcal_ioctl_buffer fw_user; + + if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) { + pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd); + return -ENOIOCTLCMD; + } + if (copy_from_user(&fw_user, argp, sizeof(fw_user))) { + pr_err("%s: failed to copy\n", __func__); + return -EFAULT; + } + return wcdcal_hwdep_ioctl_shared(hw, fw_user); +} + +static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file) +{ + struct fw_info *fw_data = hw->private_data; + + mutex_lock(&fw_data->lock); + /* clear all the calibrations */ + memset(fw_data->wcdcal_state, 0, + sizeof(fw_data->wcdcal_state)); + mutex_unlock(&fw_data->lock); + return 0; +} + +int wcd_cal_create_hwdep(void *data, int node, struct snd_soc_codec *codec) +{ + char hwname[40]; + struct snd_hwdep *hwdep; + struct firmware_cal **fw; + struct fw_info *fw_data = data; + int err, cal_bit; + + if (!fw_data || !codec) { + pr_err("%s: wrong arguments passed\n", __func__); + return -EINVAL; + } + + fw = fw_data->fw; + snprintf(hwname, strlen("Codec %s"), "Codec %s", + codec->component.name); + err = snd_hwdep_new(codec->component.card->snd_card, + hwname, node, &hwdep); + if (err < 0) { + dev_err(codec->dev, "%s: new hwdep failed %d\n", + __func__, err); + return err; + } + snprintf(hwdep->name, strlen("Codec %s"), "Codec %s", + codec->component.name); + hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC; + hwdep->private_data = fw_data; + hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat; + hwdep->ops.ioctl = wcdcal_hwdep_ioctl; + hwdep->ops.release = wcdcal_hwdep_release; + mutex_init(&fw_data->lock); + + for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) { + set_bit(WCDCAL_UNINITIALISED, + &fw_data->wcdcal_state[cal_bit]); + fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL); + if (!fw[cal_bit]) { + dev_err(codec->dev, "%s: no memory for %s cal\n", + __func__, cal_name_info[cal_bit]); + goto end; + } + } + for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) { + fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit], + GFP_KERNEL); + if (!fw[cal_bit]->data) + goto exit; + set_bit(WCDCAL_INITIALISED, + &fw_data->wcdcal_state[cal_bit]); + } + return 0; +exit: + for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) { + kfree(fw[cal_bit]->data); + fw[cal_bit]->data = NULL; + } +end: + for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) { + kfree(fw[cal_bit]); + fw[cal_bit] = NULL; + } + return -ENOMEM; +} +EXPORT_SYMBOL(wcd_cal_create_hwdep); diff --git a/sound/soc/codecs/wcdcal-hwdep.h b/sound/soc/codecs/wcdcal-hwdep.h new file mode 100644 index 0000000000000000000000000000000000000000..632e2f11f323277274ae759d48558c8de42c9a83 --- /dev/null +++ b/sound/soc/codecs/wcdcal-hwdep.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __WCD9XXX_HWDEP_H__ +#define __WCD9XXX_HWDEP_H__ +#include + +enum wcd_cal_states { + WCDCAL_UNINITIALISED, + WCDCAL_INITIALISED, + WCDCAL_RECIEVED +}; + +struct fw_info { + struct firmware_cal *fw[WCD9XXX_MAX_CAL]; + DECLARE_BITMAP(cal_bit, WCD9XXX_MAX_CAL); + /* for calibration tracking */ + unsigned long wcdcal_state[WCD9XXX_MAX_CAL]; + struct mutex lock; +}; + +struct firmware_cal { + u8 *data; + size_t size; +}; + +struct snd_soc_codec; +int wcd_cal_create_hwdep(void *fw, int node, struct snd_soc_codec *codec); +struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data, + enum wcd_cal_type type); +#endif /* __WCD9XXX_HWDEP_H__ */ diff --git a/sound/soc/codecs/wsa881x-analog.c b/sound/soc/codecs/wsa881x-analog.c new file mode 100644 index 0000000000000000000000000000000000000000..9ca7e473601b21c36d582a981e967586f4bf89dd --- /dev/null +++ b/sound/soc/codecs/wsa881x-analog.c @@ -0,0 +1,1444 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wsa881x-analog.h" +#include "wsa881x-temp-sensor.h" +#include "../msm/msm-audio-pinctrl.h" + +#define SPK_GAIN_12DB 4 +#define WIDGET_NAME_MAX_SIZE 80 + +/* + * Private data Structure for wsa881x. All parameters related to + * WSA881X codec needs to be defined here. + */ +struct wsa881x_pdata { + struct regmap *regmap[2]; + struct i2c_client *client[2]; + struct snd_soc_codec *codec; + + /* track wsa881x status during probe */ + int status; + bool boost_enable; + bool visense_enable; + int spk_pa_gain; + struct i2c_msg xfer_msg[2]; + struct mutex xfer_lock; + bool regmap_flag; + bool wsa_active; + int index; + int (*enable_mclk)(struct snd_soc_card *, bool); + struct wsa881x_tz_priv tz_pdata; + int bg_cnt; + int clk_cnt; + int enable_cnt; + int version; + struct mutex bg_lock; + struct mutex res_lock; + struct delayed_work ocp_ctl_work; +}; + +enum { + WSA881X_STATUS_PROBING, + WSA881X_STATUS_I2C, +}; + +#define WSA881X_OCP_CTL_TIMER_SEC 2 +#define WSA881X_OCP_CTL_TEMP_CELSIUS 25 +#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60 + +static int wsa881x_ocp_poll_timer_sec = WSA881X_OCP_CTL_POLL_TIMER_SEC; +module_param(wsa881x_ocp_poll_timer_sec, int, 0664); +MODULE_PARM_DESC(wsa881x_ocp_poll_timer_sec, "timer for ocp ctl polling"); + +static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec, + bool enable); + +const char *wsa_tz_names[] = {"wsa881x.0e", "wsa881x.0f"}; + +struct wsa881x_pdata wsa_pdata[MAX_WSA881X_DEVICE]; + +static bool pinctrl_init; + +static int wsa881x_populate_dt_pdata(struct device *dev); +static int wsa881x_reset(struct wsa881x_pdata *pdata, bool enable); +static int wsa881x_startup(struct wsa881x_pdata *pdata); +static int wsa881x_shutdown(struct wsa881x_pdata *pdata); + +static int delay_array_msec[] = {10, 20, 30, 40, 50}; + +static int wsa881x_i2c_addr = -1; +static int wsa881x_probing_count; +static int wsa881x_presence_count; + +static const char * const wsa881x_spk_pa_gain_text[] = { +"POS_13P5_DB", "POS_12_DB", "POS_10P5_DB", "POS_9_DB", "POS_7P5_DB", +"POS_6_DB", "POS_4P5_DB", "POS_3_DB", "POS_1P5_DB", "POS_0_DB"}; + +static const struct soc_enum wsa881x_spk_pa_gain_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa881x_spk_pa_gain_text), + wsa881x_spk_pa_gain_text), +}; + +static int wsa881x_spk_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->spk_pa_gain; + + dev_dbg(codec->dev, "%s: spk_pa_gain = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int wsa881x_spk_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] > 0xC) { + dev_err(codec->dev, "%s: Unsupported gain val %ld\n", + __func__, ucontrol->value.integer.value[0]); + return -EINVAL; + } + wsa881x->spk_pa_gain = ucontrol->value.integer.value[0]; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int get_i2c_wsa881x_device_index(u16 reg) +{ + u16 mask = 0x0f00; + int value = 0; + + value = ((reg & mask) >> 8) & 0x000f; + + switch (value) { + case 0: + return 0; + case 1: + return 1; + default: + break; + } + return -EINVAL; +} + +static int wsa881x_i2c_write_device(struct wsa881x_pdata *wsa881x, + unsigned int reg, unsigned int val) +{ + int i = 0, rc = 0; + int wsa881x_index; + struct i2c_msg *msg; + int ret = 0; + int bytes = 1; + u8 reg_addr = 0; + u8 data[bytes + 1]; + + wsa881x_index = get_i2c_wsa881x_device_index(reg); + if (wsa881x_index < 0) { + pr_err("%s:invalid register to write\n", __func__); + return -EINVAL; + } + if (wsa881x->regmap_flag) { + rc = regmap_write(wsa881x->regmap[wsa881x_index], reg, val); + for (i = 0; rc && i < ARRAY_SIZE(delay_array_msec); i++) { + pr_err("Failed writing reg=%u - retry(%d)\n", reg, i); + /* retry after delay of increasing order */ + msleep(delay_array_msec[i]); + rc = regmap_write(wsa881x->regmap[wsa881x_index], + reg, val); + } + if (rc) + pr_err("Failed writing reg=%u rc=%d\n", reg, rc); + else + pr_err("write success register = %x val = %x\n", + reg, val); + } else { + reg_addr = (u8)reg; + msg = &wsa881x->xfer_msg[0]; + msg->addr = wsa881x->client[wsa881x_index]->addr; + msg->len = bytes + 1; + msg->flags = 0; + data[0] = reg; + data[1] = (u8)val; + msg->buf = data; + ret = i2c_transfer(wsa881x->client[wsa881x_index]->adapter, + wsa881x->xfer_msg, 1); + /* Try again if the write fails */ + if (ret != 1) { + ret = i2c_transfer( + wsa881x->client[wsa881x_index]->adapter, + wsa881x->xfer_msg, 1); + if (ret != 1) { + pr_err("failed to write the device\n"); + return ret; + } + } + pr_debug("write success reg = %x val = %x\n", reg, data[1]); + } + return rc; +} + +static int wsa881x_i2c_read_device(struct wsa881x_pdata *wsa881x, + unsigned int reg) +{ + int wsa881x_index; + int i = 0, rc = 0; + unsigned int val; + struct i2c_msg *msg; + int ret = 0; + u8 reg_addr = 0; + u8 dest[5]; + + wsa881x_index = get_i2c_wsa881x_device_index(reg); + if (wsa881x_index < 0) { + pr_err("%s:invalid register to read\n", __func__); + return -EINVAL; + } + if (wsa881x->regmap_flag) { + rc = regmap_read(wsa881x->regmap[wsa881x_index], reg, &val); + for (i = 0; rc && i < ARRAY_SIZE(delay_array_msec); i++) { + pr_err("Failed reading reg=%u - retry(%d)\n", reg, i); + /* retry after delay of increasing order */ + msleep(delay_array_msec[i]); + rc = regmap_read(wsa881x->regmap[wsa881x_index], + reg, &val); + } + if (rc) { + pr_err("Failed reading reg=%u rc=%d\n", reg, rc); + return rc; + } + pr_debug("read success reg = %x val = %x\n", + reg, val); + } else { + reg_addr = (u8)reg; + msg = &wsa881x->xfer_msg[0]; + msg->addr = wsa881x->client[wsa881x_index]->addr; + msg->len = 1; + msg->flags = 0; + msg->buf = ®_addr; + + msg = &wsa881x->xfer_msg[1]; + msg->addr = wsa881x->client[wsa881x_index]->addr; + msg->len = 1; + msg->flags = I2C_M_RD; + msg->buf = dest; + ret = i2c_transfer(wsa881x->client[wsa881x_index]->adapter, + wsa881x->xfer_msg, 2); + + /* Try again if read fails first time */ + if (ret != 2) { + ret = i2c_transfer( + wsa881x->client[wsa881x_index]->adapter, + wsa881x->xfer_msg, 2); + if (ret != 2) { + pr_err("failed to read wsa register:%d\n", + reg); + return ret; + } + } + val = dest[0]; + } + return val; +} + +static unsigned int wsa881x_i2c_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + struct wsa881x_pdata *wsa881x; + unsigned int val; + int ret; + + if (codec == NULL) { + pr_err("%s: invalid codec\n", __func__); + return -EINVAL; + } + wsa881x = snd_soc_codec_get_drvdata(codec); + if (!wsa881x->wsa_active) { + ret = snd_soc_cache_read(codec, reg, &val); + if (ret >= 0) + return val; + dev_err(codec->dev, + "cache read failed for reg: 0x%x ret: %d\n", + reg, ret); + return ret; + } + return wsa881x_i2c_read_device(wsa881x, reg); +} + +static int wsa881x_i2c_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + struct wsa881x_pdata *wsa881x; + int ret = 0; + + if (codec == NULL) { + pr_err("%s: invalid codec\n", __func__); + return -EINVAL; + } + wsa881x = snd_soc_codec_get_drvdata(codec); + if (!wsa881x->wsa_active) { + ret = snd_soc_cache_write(codec, reg, val); + if (ret != 0) + dev_err(codec->dev, "cache write to %x failed: %d\n", + reg, ret); + return ret; + } + return wsa881x_i2c_write_device(wsa881x, reg, val); +} + +static int wsa881x_i2c_get_client_index(struct i2c_client *client, + int *wsa881x_index) +{ + int ret = 0; + + switch (client->addr) { + case WSA881X_I2C_SPK0_SLAVE0_ADDR: + case WSA881X_I2C_SPK0_SLAVE1_ADDR: + *wsa881x_index = WSA881X_I2C_SPK0_SLAVE0; + break; + case WSA881X_I2C_SPK1_SLAVE0_ADDR: + case WSA881X_I2C_SPK1_SLAVE1_ADDR: + *wsa881x_index = WSA881X_I2C_SPK1_SLAVE0; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int wsa881x_boost_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: enable:%d\n", __func__, enable); + if (enable) { + if (!WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_ANA_CTL, + 0x01, 0x01); + snd_soc_update_bits(codec, WSA881X_ANA_CTL, + 0x04, 0x04); + snd_soc_update_bits(codec, WSA881X_BOOST_PS_CTL, + 0x40, 0x00); + snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1, + 0xF0, 0xB0); + snd_soc_update_bits(codec, WSA881X_BOOST_ZX_CTL, + 0x20, 0x00); + snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, WSA881X_BOOST_LOOP_STABILITY, + 0x03, 0x03); + snd_soc_update_bits(codec, WSA881X_BOOST_MISC2_CTL, + 0xFF, 0x14); + snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, + 0x80, 0x80); + snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, + 0x03, 0x00); + snd_soc_update_bits(codec, + WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, + 0x0C, 0x04); + snd_soc_update_bits(codec, + WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, + 0x03, 0x00); + snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1, + 0xF0, 0x70); + snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x03, 0x01); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, + 0x08, 0x08); + snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x04, 0x04); + snd_soc_update_bits(codec, WSA881X_BOOST_CURRENT_LIMIT, + 0x0F, 0x08); + snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, + 0x80, 0x80); + } + /* For WSA8810, start-up time is 1500us as per qcrg sequence */ + usleep_range(1500, 1510); + } else { + /* ENSURE: Class-D amp is shutdown. CLK is still on */ + snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x00); + /* boost settle time is 1500us as per qcrg sequence */ + usleep_range(1500, 1510); + } + return 0; +} + +static int wsa881x_visense_txfe_ctrl(struct snd_soc_codec *codec, bool enable, + u8 isense1_gain, u8 isense2_gain, + u8 vsense_gain) +{ + u8 value = 0; + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: enable:%d\n", __func__, enable); + + if (enable) { + if (WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_OTP_REG_28, + 0x3F, 0x3A); + snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG1, + 0xFF, 0xB2); + snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG2, + 0xFF, 0x05); + } + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_VSENSE_VCM, + 0x08, 0x00); + if (WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2, + 0x1C, 0x04); + } else { + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2, + 0x08, 0x08); + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2, + 0x02, 0x02); + } + value = ((isense2_gain << 6) | (isense1_gain << 4) | + (vsense_gain << 3)); + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN, + 0xF8, value); + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN, + 0x01, 0x01); + } else { + if (WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, + WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x10, 0x10); + else + snd_soc_update_bits(codec, + 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_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN, + 0x01, 0x00); + } + return 0; +} + +static int wsa881x_visense_adc_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: enable:%d\n", __func__, enable); + if (enable) { + if (!WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, WSA881X_ADC_SEL_IBIAS, + 0x70, 0x40); + snd_soc_update_bits(codec, WSA881X_ADC_EN_SEL_IBIAS, + 0x07, 0x04); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, 0x80, 0x80); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, 0x80, 0x80); + } else { + /* Ensure: Speaker Protection has been stopped */ + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, 0x80, 0x00); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, 0x80, 0x00); + } + + return 0; +} + +static void wsa881x_bandgap_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: enable:%d, bg_count:%d\n", __func__, + enable, wsa881x->bg_cnt); + mutex_lock(&wsa881x->bg_lock); + if (enable) { + ++wsa881x->bg_cnt; + if (wsa881x->bg_cnt == 1) { + snd_soc_update_bits(codec, WSA881X_TEMP_OP, + 0x08, 0x08); + /* 400usec sleep is needed as per HW requirement */ + usleep_range(400, 410); + snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x04); + } + } else { + --wsa881x->bg_cnt; + if (wsa881x->bg_cnt <= 0) { + WARN_ON(wsa881x->bg_cnt < 0); + wsa881x->bg_cnt = 0; + snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x00); + snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x08, 0x00); + } + } + mutex_unlock(&wsa881x->bg_lock); +} + +static void wsa881x_clk_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s:ss enable:%d, clk_count:%d\n", __func__, + enable, wsa881x->clk_cnt); + mutex_lock(&wsa881x->res_lock); + if (enable) { + ++wsa881x->clk_cnt; + if (wsa881x->clk_cnt == 1) { + snd_soc_write(codec, WSA881X_CDC_RST_CTL, 0x02); + snd_soc_write(codec, WSA881X_CDC_RST_CTL, 0x03); + snd_soc_write(codec, WSA881X_CLOCK_CONFIG, 0x01); + snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x01); + snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x01); + } + } else { + --wsa881x->clk_cnt; + if (wsa881x->clk_cnt <= 0) { + WARN_ON(wsa881x->clk_cnt < 0); + wsa881x->clk_cnt = 0; + snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x00); + snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x00); + if (WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, + WSA881X_CDC_TOP_CLK_CTL, 0x01, 0x00); + } + } + mutex_unlock(&wsa881x->res_lock); +} + +static int wsa881x_rdac_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: enable:%d\n", __func__, enable); + if (enable) { + snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x08, 0x00); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0x08, 0x08); + snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x40, 0x40); + snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x80, 0x80); + if (WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL, + 0x01, 0x01); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, + 0x30, 0x30); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, + 0x0C, 0x00); + } + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0xF0, 0x40); + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x01, 0x01); + } else { + /* Ensure class-D amp is off */ + snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x80, 0x00); + } + return 0; +} + +static int wsa881x_spkr_pa_ctrl(struct snd_soc_codec *codec, bool enable) +{ + int ret = 0; + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: enable:%d\n", __func__, enable); + if (enable) { + /* + * Ensure: Boost is enabled and stable, Analog input is up + * and outputting silence + */ + if (!WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_I, + 0xFF, 0x01); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, + 0x02, 0x02); + snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_V, + 0xFF, 0x10); + snd_soc_update_bits(codec, WSA881X_SPKR_PWRSTG_DBG, + 0xA0, 0xA0); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, + 0x80, 0x80); + usleep_range(700, 710); + snd_soc_update_bits(codec, WSA881X_SPKR_PWRSTG_DBG, + 0x00, 0x00); + snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_V, + 0xFF, 0x00); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, + 0x02, 0x00); + snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_I, + 0xFF, 0x00); + } else + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, + 0x80, 0x80); + /* add 1000us delay as per qcrg */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x01, 0x01); + if (WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL, + 0x01, 0x00); + usleep_range(1000, 1010); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0xF0, + (wsa881x->spk_pa_gain << 4)); + if (wsa881x->visense_enable) { + ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, + "wsa_vi"); + if (ret) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "wsa_vi"); + return ret; + } + wsa881x_visense_txfe_ctrl(codec, true, + 0x00, 0x01, 0x00); + wsa881x_visense_adc_ctrl(codec, true); + } + } else { + /* + * Ensure: Boost is still on, Stream from Analog input and + * Speaker Protection has been stopped and input is at 0V + */ + if (WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL, + 0x01, 0x01); + usleep_range(1000, 1010); + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL, + 0x01, 0x00); + msleep(20); + snd_soc_update_bits(codec, WSA881X_ANA_CTL, + 0x03, 0x00); + usleep_range(200, 210); + } + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x80, 0x00); + } + return 0; +} + +static int wsa881x_get_boost(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->boost_enable; + return 0; +} + +static int wsa881x_set_boost(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Boost enable current %d, new %d\n", + __func__, wsa881x->boost_enable, value); + wsa881x->boost_enable = value; + return 0; +} + +static int wsa881x_get_visense(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->visense_enable; + return 0; +} + +static int wsa881x_set_visense(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: VIsense enable current %d, new %d\n", + __func__, wsa881x->visense_enable, value); + wsa881x->visense_enable = value; + return 0; +} + +static const struct snd_kcontrol_new wsa881x_snd_controls[] = { + SOC_SINGLE_EXT("BOOST Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_boost, wsa881x_set_boost), + + SOC_SINGLE_EXT("VISENSE Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_visense, wsa881x_set_visense), + + SOC_ENUM_EXT("WSA_SPK PA Gain", wsa881x_spk_pa_gain_enum[0], + wsa881x_spk_pa_gain_get, wsa881x_spk_pa_gain_put), +}; + +static const char * const rdac_text[] = { + "ZERO", "Switch", +}; + +static const struct soc_enum rdac_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(rdac_text), rdac_text); + +static const struct snd_kcontrol_new rdac_mux[] = { + SOC_DAPM_ENUM("RDAC", rdac_enum) +}; + +static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(codec->dev, "%s: %s %d boost %d visense %d\n", + __func__, w->name, event, + wsa881x->boost_enable, wsa881x->visense_enable); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = wsa881x_startup(wsa881x); + if (ret) { + pr_err("%s: wsa startup failed ret: %d", __func__, ret); + return ret; + } + wsa881x_clk_ctrl(codec, true); + snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x02, 0x02); + if (!WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, WSA881X_BIAS_REF_CTRL, + 0x0F, 0x08); + wsa881x_bandgap_ctrl(codec, true); + if (!WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, WSA881X_SPKR_BBM_CTL, + 0x02, 0x02); + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80); + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06); + if (!WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL2, + 0x04, 0x04); + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_INT, + 0x09, 0x09); + } + snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0xF0, 0x20); + if (WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, + 0x0E, 0x0E); + if (wsa881x->boost_enable) + wsa881x_boost_ctrl(codec, true); + break; + case SND_SOC_DAPM_POST_PMU: + wsa881x_rdac_ctrl(codec, true); + break; + case SND_SOC_DAPM_PRE_PMD: + wsa881x_rdac_ctrl(codec, false); + if (wsa881x->visense_enable) { + wsa881x_visense_adc_ctrl(codec, false); + wsa881x_visense_txfe_ctrl(codec, false, + 0x00, 0x01, 0x00); + ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, + "wsa_vi"); + if (ret) { + pr_err("%s: gpio set cannot be suspended %s\n", + __func__, "wsa_vi"); + return ret; + } + } + break; + case SND_SOC_DAPM_POST_PMD: + if (wsa881x->boost_enable) + wsa881x_boost_ctrl(codec, false); + wsa881x_clk_ctrl(codec, false); + wsa881x_bandgap_ctrl(codec, false); + ret = wsa881x_shutdown(wsa881x); + if (ret < 0) { + pr_err("%s: wsa shutdown failed ret: %d", + __func__, ret); + return ret; + } + break; + default: + pr_err("%s: invalid event:%d\n", __func__, event); + return -EINVAL; + } + return 0; +} + +static void wsa881x_ocp_ctl_work(struct work_struct *work) +{ + struct wsa881x_pdata *wsa881x; + struct delayed_work *dwork; + struct snd_soc_codec *codec; + unsigned long temp_val; + + dwork = to_delayed_work(work); + wsa881x = container_of(dwork, struct wsa881x_pdata, ocp_ctl_work); + + if (!wsa881x) + return; + + codec = wsa881x->codec; + wsa881x_get_temp(wsa881x->tz_pdata.tz_dev, &temp_val); + dev_dbg(codec->dev, " temp = %ld\n", temp_val); + + if (temp_val <= WSA881X_OCP_CTL_TEMP_CELSIUS) + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x00); + else + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0); + + schedule_delayed_work(&wsa881x->ocp_ctl_work, + msecs_to_jiffies(wsa881x_ocp_poll_timer_sec * 1000)); +} + +static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x80); + break; + case SND_SOC_DAPM_POST_PMU: + wsa881x_spkr_pa_ctrl(codec, true); + schedule_delayed_work(&wsa881x->ocp_ctl_work, + msecs_to_jiffies(WSA881X_OCP_CTL_TIMER_SEC * 1000)); + break; + case SND_SOC_DAPM_PRE_PMD: + wsa881x_spkr_pa_ctrl(codec, false); + break; + case SND_SOC_DAPM_POST_PMD: + cancel_delayed_work_sync(&wsa881x->ocp_ctl_work); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0); + break; + default: + pr_err("%s: invalid event:%d\n", __func__, event); + return -EINVAL; + } + return 0; +} + + +static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("WSA_IN"), + + SND_SOC_DAPM_DAC_E("RDAC Analog", NULL, SND_SOC_NOPM, 0, 0, + wsa881x_rdac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("WSA_RDAC", SND_SOC_NOPM, 0, 0, + rdac_mux), + + SND_SOC_DAPM_PGA_S("WSA_SPKR PGA", 1, SND_SOC_NOPM, 0, 0, + wsa881x_spkr_pa_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("WSA_SPKR"), +}; + +static const struct snd_soc_dapm_route wsa881x_audio_map[] = { + {"WSA_RDAC", "Switch", "WSA_IN"}, + {"RDAC Analog", NULL, "WSA_RDAC"}, + {"WSA_SPKR PGA", NULL, "RDAC Analog"}, + {"WSA_SPKR", NULL, "WSA_SPKR PGA"}, +}; + + +static int wsa881x_startup(struct wsa881x_pdata *pdata) +{ + int ret = 0; + struct snd_soc_codec *codec = pdata->codec; + struct snd_soc_card *card = codec->component.card; + + pr_debug("%s(): wsa startup, enable_cnt:%d\n", __func__, + pdata->enable_cnt); + + if (pdata->enable_cnt++ > 0) + return 0; + ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_clk"); + if (ret) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "wsa_clk"); + return ret; + } + if (pdata->enable_mclk) { + ret = pdata->enable_mclk(card, true); + if (ret < 0) { + dev_err_ratelimited(codec->dev, + "%s: mclk enable failed %d\n", + __func__, ret); + return ret; + } + } + ret = wsa881x_reset(pdata, true); + return ret; +} + +static int wsa881x_shutdown(struct wsa881x_pdata *pdata) +{ + int ret = 0, reg; + struct snd_soc_codec *codec = pdata->codec; + struct snd_soc_card *card = codec->component.card; + + pr_debug("%s(): wsa shutdown, enable_cnt:%d\n", __func__, + pdata->enable_cnt); + if (--pdata->enable_cnt > 0) + return 0; + ret = wsa881x_reset(pdata, false); + if (ret) { + pr_err("%s: wsa reset failed suspend %d\n", + __func__, ret); + return ret; + } + + if (pdata->enable_mclk) { + ret = pdata->enable_mclk(card, false); + if (ret < 0) { + pr_err("%s: mclk disable failed %d\n", + __func__, ret); + return ret; + } + } + + ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_clk"); + if (ret) { + pr_err("%s: gpio set cannot be suspended %s\n", + __func__, "wsa_clk"); + return ret; + } + if (pdata->codec) { + /* restore defaults to cache */ + for (reg = 0; reg < ARRAY_SIZE(wsa881x_ana_reg_defaults); + reg++) { + if (wsa881x_ana_reg_readable[reg]) + snd_soc_cache_write(pdata->codec, + wsa881x_ana_reg_defaults[reg].reg, + wsa881x_ana_reg_defaults[reg].def); + } + } + return 0; +} + +static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec, + bool enable) +{ + int ret = 0; + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + if (enable) { + ret = wsa881x_startup(wsa881x); + if (ret < 0) { + dev_err_ratelimited(codec->dev, + "%s: failed to startup\n", __func__); + return ret; + } + } + wsa881x_clk_ctrl(codec, enable); + wsa881x_bandgap_ctrl(codec, enable); + if (!enable) { + ret = wsa881x_shutdown(wsa881x); + if (ret < 0) + dev_err_ratelimited(codec->dev, + "%s: failed to shutdown\n", __func__); + } + return ret; +} + +static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec, + struct wsa_temp_register *wsa_temp_reg) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (!wsa881x) { + dev_err(codec->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + ret = wsa881x_resource_acquire(codec, true); + if (ret) { + dev_err_ratelimited(codec->dev, + "%s: resource acquire fail\n", __func__); + return ret; + } + + if (WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x00); + wsa_temp_reg->dmeas_msb = snd_soc_read(codec, WSA881X_TEMP_MSB); + wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, WSA881X_TEMP_LSB); + snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x01); + } else { + wsa_temp_reg->dmeas_msb = snd_soc_read(codec, + WSA881X_TEMP_DOUT_MSB); + wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, + WSA881X_TEMP_DOUT_LSB); + } + wsa_temp_reg->d1_msb = snd_soc_read(codec, WSA881X_OTP_REG_1); + wsa_temp_reg->d1_lsb = snd_soc_read(codec, WSA881X_OTP_REG_2); + wsa_temp_reg->d2_msb = snd_soc_read(codec, WSA881X_OTP_REG_3); + wsa_temp_reg->d2_lsb = snd_soc_read(codec, WSA881X_OTP_REG_4); + + ret = wsa881x_resource_acquire(codec, false); + if (ret) + dev_err_ratelimited(codec->dev, + "%s: resource release fail\n", __func__); + + return ret; +} + +static int wsa881x_probe(struct snd_soc_codec *codec) +{ + struct i2c_client *client; + int ret = 0; + int wsa881x_index = 0; + struct snd_soc_dapm_context *dapm = &codec->dapm; + char *widget_name = NULL; + struct snd_soc_card *card = codec->component.card; + struct snd_soc_codec_conf *codec_conf = card->codec_conf; + + client = dev_get_drvdata(codec->dev); + ret = wsa881x_i2c_get_client_index(client, &wsa881x_index); + if (ret != 0) { + dev_err(&client->dev, "%s: I2C get codec I2C\n" + "client failed\n", __func__); + return ret; + } + mutex_init(&wsa_pdata[wsa881x_index].bg_lock); + mutex_init(&wsa_pdata[wsa881x_index].res_lock); + snprintf(wsa_pdata[wsa881x_index].tz_pdata.name, 100, "%s", + wsa_tz_names[wsa881x_index]); + wsa_pdata[wsa881x_index].codec = codec; + wsa_pdata[wsa881x_index].spk_pa_gain = SPK_GAIN_12DB; + wsa_pdata[wsa881x_index].codec = codec; + wsa_pdata[wsa881x_index].tz_pdata.codec = codec; + wsa_pdata[wsa881x_index].tz_pdata.wsa_temp_reg_read = + wsa881x_temp_reg_read; + snd_soc_codec_set_drvdata(codec, &wsa_pdata[wsa881x_index]); + wsa881x_init_thermal(&wsa_pdata[wsa881x_index].tz_pdata); + INIT_DELAYED_WORK(&wsa_pdata[wsa881x_index].ocp_ctl_work, + wsa881x_ocp_ctl_work); + + if (codec_conf->name_prefix) { + widget_name = kcalloc(WIDGET_NAME_MAX_SIZE, sizeof(char), + GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + snprintf(widget_name, WIDGET_NAME_MAX_SIZE, + "%s WSA_SPKR", codec_conf->name_prefix); + snd_soc_dapm_ignore_suspend(dapm, widget_name); + snprintf(widget_name, WIDGET_NAME_MAX_SIZE, + "%s WSA_IN", codec_conf->name_prefix); + snd_soc_dapm_ignore_suspend(dapm, widget_name); + kfree(widget_name); + } else { + snd_soc_dapm_ignore_suspend(dapm, "WSA_SPKR"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_IN"); + } + + snd_soc_dapm_sync(dapm); + return 0; +} + +static int wsa881x_remove(struct snd_soc_codec *codec) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + if (wsa881x->tz_pdata.tz_dev) + wsa881x_deinit_thermal(wsa881x->tz_pdata.tz_dev); + + mutex_destroy(&wsa881x->bg_lock); + mutex_destroy(&wsa881x->res_lock); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_wsa881x = { + .probe = wsa881x_probe, + .remove = wsa881x_remove, + + .read = wsa881x_i2c_read, + .write = wsa881x_i2c_write, + + .reg_cache_size = WSA881X_CACHE_SIZE, + .reg_cache_default = wsa881x_ana_reg_defaults, + .reg_word_size = 1, + + .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_reset(struct wsa881x_pdata *pdata, bool enable) +{ + int ret = 0; + + /* + * shutdown the GPIOs WSA_EN, WSA_MCLK, regulators + * and restore defaults in soc cache when shutdown. + * Enable regulators, GPIOs WSA_MCLK, WSA_EN when powerup. + */ + if (enable) { + if (pdata->wsa_active) + return 0; + ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_reset"); + if (ret) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "wsa_reset"); + return ret; + } + ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_reset"); + if (ret) { + pr_err("%s: gpio set cannot be suspended(powerup) %s\n", + __func__, "wsa_reset"); + return ret; + } + ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_reset"); + if (ret) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "wsa_reset"); + return ret; + } + pdata->wsa_active = true; + } else { + if (!pdata->wsa_active) + return 0; + ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_reset"); + if (ret) { + pr_err("%s: gpio set cannot be suspended %s\n", + __func__, "wsa_reset"); + return ret; + } + pdata->wsa_active = false; + } + return ret; +} + +int wsa881x_get_client_index(void) +{ + return wsa881x_i2c_addr; +} +EXPORT_SYMBOL(wsa881x_get_client_index); + +int wsa881x_get_probing_count(void) +{ + return wsa881x_probing_count; +} +EXPORT_SYMBOL(wsa881x_get_probing_count); + +int wsa881x_get_presence_count(void) +{ + return wsa881x_presence_count; +} +EXPORT_SYMBOL(wsa881x_get_presence_count); + +int wsa881x_set_mclk_callback( + int (*enable_mclk_callback)(struct snd_soc_card *, bool)) +{ + int i; + + for (i = 0; i < MAX_WSA881X_DEVICE; i++) { + if (wsa_pdata[i].status == WSA881X_STATUS_I2C) + wsa_pdata[i].enable_mclk = enable_mclk_callback; + } + return 0; +} +EXPORT_SYMBOL(wsa881x_set_mclk_callback); + +static int check_wsa881x_presence(struct i2c_client *client) +{ + int ret = 0; + int wsa881x_index = 0; + + ret = wsa881x_i2c_get_client_index(client, &wsa881x_index); + if (ret != 0) { + dev_err(&client->dev, "%s: I2C get codec I2C\n" + "client failed\n", __func__); + return ret; + } + ret = wsa881x_i2c_read_device(&wsa_pdata[wsa881x_index], + WSA881X_CDC_RST_CTL); + if (ret < 0) { + dev_err(&client->dev, "failed to read wsa881x with addr %x\n", + client->addr); + return ret; + } + ret = wsa881x_i2c_write_device(&wsa_pdata[wsa881x_index], + WSA881X_CDC_RST_CTL, 0x01); + if (ret < 0) { + dev_err(&client->dev, "failed write addr %x reg:0x5 val:0x1\n", + client->addr); + return ret; + } + /* allow 20ms before trigger next write to verify WSA881x presence */ + msleep(20); + ret = wsa881x_i2c_write_device(&wsa_pdata[wsa881x_index], + WSA881X_CDC_RST_CTL, 0x00); + if (ret < 0) { + dev_err(&client->dev, "failed write addr %x reg:0x5 val:0x0\n", + client->addr); + return ret; + } + return ret; +} + +static int wsa881x_populate_dt_pdata(struct device *dev) +{ + int ret = 0; + + /* reading the gpio configurations from dtsi file */ + if (!pinctrl_init) { + ret = msm_gpioset_initialize(CLIENT_WSA_BONGO_1, dev); + if (ret < 0) { + dev_err(dev, + "%s: error reading dtsi files%d\n", __func__, ret); + goto err; + } + pinctrl_init = true; + } +err: + return ret; +} + +static int wsa881x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + int wsa881x_index = 0; + struct wsa881x_pdata *pdata = NULL; + + ret = wsa881x_i2c_get_client_index(client, &wsa881x_index); + if (ret != 0) { + dev_err(&client->dev, "%s: I2C get codec I2C\n" + "client failed\n", __func__); + return ret; + } + + pdata = &wsa_pdata[wsa881x_index]; + + if ((client->addr == WSA881X_I2C_SPK0_SLAVE1_ADDR || + client->addr == WSA881X_I2C_SPK1_SLAVE1_ADDR) && + (pdata->status == WSA881X_STATUS_PROBING)) + return ret; + + if (pdata->status == WSA881X_STATUS_I2C) { + dev_dbg(&client->dev, "%s:probe for other slaves\n" + "devices of codec I2C slave Addr = %x\n", + __func__, client->addr); + + dev_dbg(&client->dev, "%s:wsa_idx = %d SLAVE = %d\n", + __func__, wsa881x_index, WSA881X_ANALOG_SLAVE); + pdata->regmap[WSA881X_ANALOG_SLAVE] = + devm_regmap_init_i2c( + client, + &wsa881x_ana_regmap_config[WSA881X_ANALOG_SLAVE]); + regcache_cache_bypass(pdata->regmap[WSA881X_ANALOG_SLAVE], + true); + if (IS_ERR(pdata->regmap[WSA881X_ANALOG_SLAVE])) { + ret = PTR_ERR(pdata->regmap[WSA881X_ANALOG_SLAVE]); + dev_err(&client->dev, + "%s: regmap_init failed %d\n", + __func__, ret); + } + client->dev.platform_data = pdata; + i2c_set_clientdata(client, pdata); + pdata->client[WSA881X_ANALOG_SLAVE] = client; + if (pdata->version == WSA881X_2_0) + wsa881x_update_regmap_2_0( + pdata->regmap[WSA881X_ANALOG_SLAVE], + WSA881X_ANALOG_SLAVE); + + return ret; + } else if (pdata->status == WSA881X_STATUS_PROBING) { + pdata->index = wsa881x_index; + if (client->dev.of_node) { + dev_dbg(&client->dev, "%s:Platform data\n" + "from device tree\n", __func__); + ret = wsa881x_populate_dt_pdata(&client->dev); + if (ret < 0) { + dev_err(&client->dev, + "%s: Fail to obtain pdata from device tree\n", + __func__); + ret = -EINVAL; + goto err; + } + client->dev.platform_data = pdata; + } else { + dev_dbg(&client->dev, "%s:Platform data from\n" + "board file\n", __func__); + pdata = client->dev.platform_data; + } + if (!pdata) { + dev_dbg(&client->dev, "no platform data?\n"); + ret = -EINVAL; + goto err; + } + i2c_set_clientdata(client, pdata); + dev_set_drvdata(&client->dev, client); + + pdata->regmap[WSA881X_DIGITAL_SLAVE] = + devm_regmap_init_i2c( + client, + &wsa881x_ana_regmap_config[WSA881X_DIGITAL_SLAVE]); + regcache_cache_bypass(pdata->regmap[WSA881X_DIGITAL_SLAVE], + true); + if (IS_ERR(pdata->regmap[WSA881X_DIGITAL_SLAVE])) { + ret = PTR_ERR(pdata->regmap[WSA881X_DIGITAL_SLAVE]); + dev_err(&client->dev, "%s: regmap_init failed %d\n", + __func__, ret); + goto err; + } + /* bus reset sequence */ + ret = wsa881x_reset(pdata, true); + if (ret < 0) { + dev_err(&client->dev, "%s: WSA enable Failed %d\n", + __func__, ret); + goto err; + } + pdata->client[WSA881X_DIGITAL_SLAVE] = client; + pdata->regmap_flag = true; + ret = check_wsa881x_presence(client); + if (ret < 0) { + dev_err(&client->dev, + "failed to ping wsa with addr:%x, ret = %d\n", + client->addr, ret); + wsa881x_probing_count++; + goto err1; + } + pdata->version = wsa881x_i2c_read_device(pdata, + WSA881X_CHIP_ID1); + pr_debug("%s: wsa881x version: %d\n", __func__, pdata->version); + if (pdata->version == WSA881X_2_0) { + wsa881x_update_reg_defaults_2_0(); + wsa881x_update_regmap_2_0( + pdata->regmap[WSA881X_DIGITAL_SLAVE], + WSA881X_DIGITAL_SLAVE); + } + wsa881x_presence_count++; + wsa881x_probing_count++; + ret = snd_soc_register_codec(&client->dev, + &soc_codec_dev_wsa881x, + NULL, 0); + if (ret < 0) + goto err1; + pdata->status = WSA881X_STATUS_I2C; + } +err1: + wsa881x_reset(pdata, false); +err: + return 0; +} + +static int wsa881x_i2c_remove(struct i2c_client *client) +{ + struct wsa881x_pdata *wsa881x = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + i2c_set_clientdata(client, NULL); + kfree(wsa881x); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int wsa881x_i2c_suspend(struct device *dev) +{ + pr_debug("%s: system suspend\n", __func__); + return 0; +} + +static int wsa881x_i2c_resume(struct device *dev) +{ + pr_debug("%s: system resume\n", __func__); + return 0; +} + +static const struct dev_pm_ops wsa881x_i2c_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(wsa881x_i2c_suspend, wsa881x_i2c_resume) +}; +#endif /* CONFIG_PM_SLEEP */ + +static const struct i2c_device_id wsa881x_i2c_id[] = { + {"wsa881x-i2c-dev", WSA881X_I2C_SPK0_SLAVE0_ADDR}, + {"wsa881x-i2c-dev", WSA881X_I2C_SPK0_SLAVE1_ADDR}, + {"wsa881x-i2c-dev", WSA881X_I2C_SPK1_SLAVE0_ADDR}, + {"wsa881x-i2c-dev", WSA881X_I2C_SPK1_SLAVE1_ADDR}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, wsa881x_i2c_id); + + +static const struct of_device_id msm_match_table[] = { + {.compatible = "qcom,wsa881x-i2c-codec"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_match_table); + +static struct i2c_driver wsa881x_codec_driver = { + .driver = { + .name = "wsa881x-i2c-codec", + .owner = THIS_MODULE, +#ifdef CONFIG_PM_SLEEP + .pm = &wsa881x_i2c_pm_ops, +#endif + .of_match_table = msm_match_table, + }, + .id_table = wsa881x_i2c_id, + .probe = wsa881x_i2c_probe, + .remove = wsa881x_i2c_remove, +}; + +static int __init wsa881x_codec_init(void) +{ + int i = 0; + + for (i = 0; i < MAX_WSA881X_DEVICE; i++) + wsa_pdata[i].status = WSA881X_STATUS_PROBING; + return i2c_add_driver(&wsa881x_codec_driver); +} +module_init(wsa881x_codec_init); + +static void __exit wsa881x_codec_exit(void) +{ + i2c_del_driver(&wsa881x_codec_driver); +} + +module_exit(wsa881x_codec_exit); + +MODULE_DESCRIPTION("WSA881x Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wsa881x-analog.h b/sound/soc/codecs/wsa881x-analog.h new file mode 100644 index 0000000000000000000000000000000000000000..a2ef2a284c23868b0453cfb2708f185632ba94d6 --- /dev/null +++ b/sound/soc/codecs/wsa881x-analog.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _WSA881X_H +#define _WSA881X_H + +#include +#include "wsa881x-registers-analog.h" +#include + +#define WSA881X_I2C_SPK0_SLAVE0_ADDR 0x0E +#define WSA881X_I2C_SPK0_SLAVE1_ADDR 0x44 +#define WSA881X_I2C_SPK1_SLAVE0_ADDR 0x0F +#define WSA881X_I2C_SPK1_SLAVE1_ADDR 0x45 + +#define WSA881X_I2C_SPK0_SLAVE0 0 +#define WSA881X_I2C_SPK1_SLAVE0 1 +#define MAX_WSA881X_DEVICE 2 +#define WSA881X_DIGITAL_SLAVE 0 +#define WSA881X_ANALOG_SLAVE 1 + +enum { + WSA881X_1_X = 0, + WSA881X_2_0, +}; + +#define WSA881X_IS_2_0(ver) \ + ((ver == WSA881X_2_0) ? 1 : 0) + +extern const u8 wsa881x_ana_reg_readable[WSA881X_CACHE_SIZE]; +extern struct reg_default wsa881x_ana_reg_defaults[WSA881X_CACHE_SIZE]; +extern struct regmap_config wsa881x_ana_regmap_config[2]; +int wsa881x_get_client_index(void); +int wsa881x_get_probing_count(void); +int wsa881x_get_presence_count(void); +int wsa881x_set_mclk_callback( + int (*enable_mclk_callback)(struct snd_soc_card *, bool)); +void wsa881x_update_reg_defaults_2_0(void); +void wsa881x_update_regmap_2_0(struct regmap *regmap, int flag); + +#endif /* _WSA881X_H */ diff --git a/sound/soc/codecs/wsa881x-irq.c b/sound/soc/codecs/wsa881x-irq.c new file mode 100644 index 0000000000000000000000000000000000000000..9afbd92b8f72111a66517a56f104254e924d57a7 --- /dev/null +++ b/sound/soc/codecs/wsa881x-irq.c @@ -0,0 +1,610 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wsa881x-irq.h" +#include "wsa881x-registers-analog.h" + +#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE)) +#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE) + + +#define WSA_MAX_NUM_IRQS 8 + +#ifndef NO_IRQ +#define NO_IRQ (-1) +#endif + +static int virq_to_phyirq( + struct wsa_resource *wsa_res, int virq); +static int phyirq_to_virq( + struct wsa_resource *wsa_res, int irq); +static unsigned int wsa_irq_get_upstream_irq( + struct wsa_resource *wsa_res); +static void wsa_irq_put_upstream_irq( + struct wsa_resource *wsa_res); +static int wsa_map_irq( + struct wsa_resource *wsa_res, int irq); + +static struct snd_soc_codec *ptr_codec; + +/** + * wsa_set_codec() - to update codec pointer + * @codec: codec pointer. + * + * To update the codec pointer, which is used to read/write + * wsa register. + * + * Return: void. + */ +void wsa_set_codec(struct snd_soc_codec *codec) +{ + if (codec == NULL) { + pr_err("%s: codec pointer is NULL\n", __func__); + ptr_codec = NULL; + return; + } + ptr_codec = codec; + /* Initialize interrupt mask and level registers */ + snd_soc_write(codec, WSA881X_INTR_LEVEL, 0x8F); + snd_soc_write(codec, WSA881X_INTR_MASK, 0x8F); +} + +static void wsa_irq_lock(struct irq_data *data) +{ + struct wsa_resource *wsa_res = + irq_data_get_irq_chip_data(data); + + if (wsa_res == NULL) { + pr_err("%s: wsa_res pointer is NULL\n", __func__); + return; + } + mutex_lock(&wsa_res->irq_lock); +} + +static void wsa_irq_sync_unlock(struct irq_data *data) +{ + struct wsa_resource *wsa_res = + irq_data_get_irq_chip_data(data); + + if (wsa_res == NULL) { + pr_err("%s: wsa_res pointer is NULL\n", __func__); + return; + } + if (wsa_res->codec == NULL) { + pr_err("%s: codec pointer not registered\n", __func__); + if (ptr_codec == NULL) { + pr_err("%s: did not receive valid codec pointer\n", + __func__); + goto unlock; + } else { + wsa_res->codec = ptr_codec; + } + } + + /* + * If there's been a change in the mask write it back + * to the hardware. + */ + if (wsa_res->irq_masks_cur != + wsa_res->irq_masks_cache) { + + wsa_res->irq_masks_cache = + wsa_res->irq_masks_cur; + snd_soc_write(wsa_res->codec, + WSA881X_INTR_MASK, + wsa_res->irq_masks_cur); + } +unlock: + mutex_unlock(&wsa_res->irq_lock); +} + +static void wsa_irq_enable(struct irq_data *data) +{ + struct wsa_resource *wsa_res = + irq_data_get_irq_chip_data(data); + int wsa_irq; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res pointer is NULL\n", __func__); + return; + } + wsa_irq = virq_to_phyirq(wsa_res, data->irq); + pr_debug("%s: wsa_irq = %d\n", __func__, wsa_irq); + wsa_res->irq_masks_cur &= + ~(BYTE_BIT_MASK(wsa_irq)); +} + +static void wsa_irq_disable(struct irq_data *data) +{ + struct wsa_resource *wsa_res = + irq_data_get_irq_chip_data(data); + int wsa_irq; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res pointer is NULL\n", __func__); + return; + } + wsa_irq = virq_to_phyirq(wsa_res, data->irq); + pr_debug("%s: wsa_irq = %d\n", __func__, wsa_irq); + wsa_res->irq_masks_cur + |= BYTE_BIT_MASK(wsa_irq); +} + +static void wsa_irq_ack(struct irq_data *data) +{ + int wsa_irq = 0; + struct wsa_resource *wsa_res = + irq_data_get_irq_chip_data(data); + + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + wsa_irq = virq_to_phyirq(wsa_res, data->irq); + pr_debug("%s: IRQ_ACK called for WCD9XXX IRQ: %d\n", + __func__, wsa_irq); +} + +static void wsa_irq_mask(struct irq_data *d) +{ + /* do nothing but required as linux calls irq_mask without NULL check */ +} + +static struct irq_chip wsa_irq_chip = { + .name = "wsa", + .irq_bus_lock = wsa_irq_lock, + .irq_bus_sync_unlock = wsa_irq_sync_unlock, + .irq_disable = wsa_irq_disable, + .irq_enable = wsa_irq_enable, + .irq_mask = wsa_irq_mask, + .irq_ack = wsa_irq_ack, +}; + +static irqreturn_t wsa_irq_thread(int irq, void *data) +{ + struct wsa_resource *wsa_res = data; + int i; + u8 status; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return IRQ_HANDLED; + } + if (wsa_res->codec == NULL) { + pr_err("%s: codec pointer not registered\n", __func__); + if (ptr_codec == NULL) { + pr_err("%s: did not receive valid codec pointer\n", + __func__); + return IRQ_HANDLED; + } + wsa_res->codec = ptr_codec; + } + status = snd_soc_read(wsa_res->codec, WSA881X_INTR_STATUS); + /* Apply masking */ + status &= ~wsa_res->irq_masks_cur; + + for (i = 0; i < wsa_res->num_irqs; i++) { + if (status & BYTE_BIT_MASK(i)) { + mutex_lock(&wsa_res->nested_irq_lock); + handle_nested_irq(phyirq_to_virq(wsa_res, i)); + mutex_unlock(&wsa_res->nested_irq_lock); + } + } + + return IRQ_HANDLED; +} + +/** + * wsa_free_irq() - to free an interrupt + * @irq: interrupt number. + * @data: pointer to wsa resource. + * + * To free already requested interrupt. + * + * Return: void. + */ +void wsa_free_irq(int irq, void *data) +{ + struct wsa_resource *wsa_res = data; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + free_irq(phyirq_to_virq(wsa_res, irq), data); +} + +/** + * wsa_enable_irq() - to enable an interrupt + * @wsa_res: pointer to wsa resource. + * @irq: interrupt number. + * + * This function is to enable an interrupt. + * + * Return: void. + */ +void wsa_enable_irq(struct wsa_resource *wsa_res, int irq) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + enable_irq(phyirq_to_virq(wsa_res, irq)); +} + +/** + * wsa_disable_irq() - to disable an interrupt + * @wsa_res: pointer to wsa resource. + * @irq: interrupt number. + * + * To disable an interrupt without waiting for executing + * handler to complete. + * + * Return: void. + */ +void wsa_disable_irq(struct wsa_resource *wsa_res, int irq) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + disable_irq_nosync(phyirq_to_virq(wsa_res, irq)); +} + +/** + * wsa_disable_irq_sync() - to disable an interrupt + * @wsa_res: pointer to wsa resource. + * @irq: interrupt number. + * + * To disable an interrupt, wait for executing IRQ + * handler to complete. + * + * Return: void. + */ +void wsa_disable_irq_sync( + struct wsa_resource *wsa_res, int irq) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + disable_irq(phyirq_to_virq(wsa_res, irq)); +} + +static int wsa_irq_setup_downstream_irq(struct wsa_resource *wsa_res) +{ + int irq, virq, ret; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: enter\n", __func__); + + for (irq = 0; irq < wsa_res->num_irqs; irq++) { + /* Map OF irq */ + virq = wsa_map_irq(wsa_res, irq); + pr_debug("%s: irq %d -> %d\n", __func__, irq, virq); + if (virq == NO_IRQ) { + pr_err("%s, No interrupt specifier for irq %d\n", + __func__, irq); + return NO_IRQ; + } + + ret = irq_set_chip_data(virq, wsa_res); + if (ret) { + pr_err("%s: Failed to configure irq %d (%d)\n", + __func__, irq, ret); + return ret; + } + + if (wsa_res->irq_level_high[irq]) + irq_set_chip_and_handler(virq, &wsa_irq_chip, + handle_level_irq); + else + irq_set_chip_and_handler(virq, &wsa_irq_chip, + handle_edge_irq); + + irq_set_nested_thread(virq, 1); + } + + pr_debug("%s: leave\n", __func__); + + return 0; +} + +static int wsa_irq_init(struct wsa_resource *wsa_res) +{ + int i, ret; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return -EINVAL; + } + mutex_init(&wsa_res->irq_lock); + mutex_init(&wsa_res->nested_irq_lock); + + wsa_res->irq = wsa_irq_get_upstream_irq(wsa_res); + if (!wsa_res->irq) { + pr_warn("%s: irq driver is not yet initialized\n", __func__); + mutex_destroy(&wsa_res->irq_lock); + mutex_destroy(&wsa_res->nested_irq_lock); + return -EPROBE_DEFER; + } + pr_debug("%s: probed irq %d\n", __func__, wsa_res->irq); + + /* Setup downstream IRQs */ + ret = wsa_irq_setup_downstream_irq(wsa_res); + if (ret) { + pr_err("%s: Failed to setup downstream IRQ\n", __func__); + goto fail_irq_init; + } + + /* mask all the interrupts */ + for (i = 0; i < wsa_res->num_irqs; i++) { + wsa_res->irq_masks_cur |= BYTE_BIT_MASK(i); + wsa_res->irq_masks_cache |= BYTE_BIT_MASK(i); + } + + ret = request_threaded_irq(wsa_res->irq, NULL, wsa_irq_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "wsa", wsa_res); + if (ret != 0) { + dev_err(wsa_res->dev, "Failed to request IRQ %d: %d\n", + wsa_res->irq, ret); + } else { + ret = enable_irq_wake(wsa_res->irq); + if (ret) { + dev_err(wsa_res->dev, + "Failed to set wake interrupt on IRQ %d: %d\n", + wsa_res->irq, ret); + free_irq(wsa_res->irq, wsa_res); + } + } + + if (ret) + goto fail_irq_init; + + return ret; + +fail_irq_init: + dev_err(wsa_res->dev, + "%s: Failed to init wsa irq\n", __func__); + wsa_irq_put_upstream_irq(wsa_res); + mutex_destroy(&wsa_res->irq_lock); + mutex_destroy(&wsa_res->nested_irq_lock); + return ret; +} + +/** + * wsa_request_irq() - to request/register an interrupt + * @wsa_res: pointer to wsa_resource. + * @irq: interrupt number. + * @handler: interrupt handler function pointer. + * @name: interrupt name. + * @data: device info. + * + * Convert physical irq to virtual irq and then + * reguest for threaded handler. + * + * Return: Retuns success/failure. + */ +int wsa_request_irq(struct wsa_resource *wsa_res, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + int virq; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return -EINVAL; + } + virq = phyirq_to_virq(wsa_res, irq); + + /* + * ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. + */ +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) + set_irq_flags(virq, IRQF_VALID); +#else + set_irq_noprobe(virq); +#endif + + return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING, + name, data); +} + +/** + * wsa_irq_exit() - to disable/clear interrupt/resources + * @wsa_res: pointer to wsa_resource + * + * Disable and free the interrupts and then release resources. + * + * Return: void. + */ +void wsa_irq_exit(struct wsa_resource *wsa_res) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + dev_dbg(wsa_res->dev, "%s: Cleaning up irq %d\n", __func__, + wsa_res->irq); + + if (wsa_res->irq) { + disable_irq_wake(wsa_res->irq); + free_irq(wsa_res->irq, wsa_res); + /* Release parent's of node */ + wsa_irq_put_upstream_irq(wsa_res); + } + mutex_destroy(&wsa_res->irq_lock); + mutex_destroy(&wsa_res->nested_irq_lock); +} + +static int phyirq_to_virq(struct wsa_resource *wsa_res, int offset) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return -EINVAL; + } + return irq_linear_revmap(wsa_res->domain, offset); +} + +static int virq_to_phyirq(struct wsa_resource *wsa_res, int virq) +{ + struct irq_data *irq_data = irq_get_irq_data(virq); + + if (unlikely(!irq_data)) { + pr_err("%s: irq_data is NULL\n", __func__); + return -EINVAL; + } + return irq_data->hwirq; +} + +static unsigned int wsa_irq_get_upstream_irq(struct wsa_resource *wsa_res) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return -EINVAL; + } + return wsa_res->irq; +} + +static void wsa_irq_put_upstream_irq(struct wsa_resource *wsa_res) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + /* Hold parent's of node */ + of_node_put(wsa_res->dev->of_node); +} + +static int wsa_map_irq(struct wsa_resource *wsa_res, int irq) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return -EINVAL; + } + return of_irq_to_resource(wsa_res->dev->of_node, irq, NULL); +} + +static int wsa_irq_probe(struct platform_device *pdev) +{ + int irq; + struct wsa_resource *wsa_res = NULL; + int ret = -EINVAL; + + irq = platform_get_irq_byname(pdev, "wsa-int"); + if (irq < 0) { + dev_err(&pdev->dev, "%s: Couldn't find wsa-int node(%d)\n", + __func__, irq); + return -EINVAL; + } + pr_debug("%s: node %s\n", __func__, pdev->name); + wsa_res = kzalloc(sizeof(*wsa_res), GFP_KERNEL); + if (!wsa_res) { + pr_err("%s: could not allocate memory\n", __func__); + return -ENOMEM; + } + /* + * wsa interrupt controller supports N to N irq mapping with + * single cell binding with irq numbers(offsets) only. + * Use irq_domain_simple_ops that has irq_domain_simple_map and + * irq_domain_xlate_onetwocell. + */ + wsa_res->dev = &pdev->dev; + wsa_res->domain = irq_domain_add_linear(wsa_res->dev->of_node, + WSA_MAX_NUM_IRQS, &irq_domain_simple_ops, + wsa_res); + if (!wsa_res->domain) { + dev_err(&pdev->dev, "%s: domain is NULL\n", __func__); + ret = -ENOMEM; + goto err; + } + wsa_res->dev = &pdev->dev; + + dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq); + wsa_res->irq = irq; + wsa_res->num_irq_regs = 1; + wsa_res->num_irqs = WSA_NUM_IRQS; + ret = wsa_irq_init(wsa_res); + if (ret < 0) { + dev_err(&pdev->dev, "%s: failed to do irq init %d\n", + __func__, ret); + goto err; + } + + return ret; +err: + kfree(wsa_res); + return ret; +} + +static int wsa_irq_remove(struct platform_device *pdev) +{ + struct irq_domain *domain; + struct wsa_resource *data; + + domain = irq_find_host(pdev->dev.of_node); + if (unlikely(!domain)) { + pr_err("%s: domain is NULL\n", __func__); + return -EINVAL; + } + data = (struct wsa_resource *)domain->host_data; + data->irq = 0; + + return 0; +} + +static const struct of_device_id of_match[] = { + { .compatible = "qcom,wsa-irq" }, + { } +}; + +static struct platform_driver wsa_irq_driver = { + .probe = wsa_irq_probe, + .remove = wsa_irq_remove, + .driver = { + .name = "wsa_intc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_match), + }, +}; + +static int wsa_irq_drv_init(void) +{ + return platform_driver_register(&wsa_irq_driver); +} +subsys_initcall(wsa_irq_drv_init); + +static void wsa_irq_drv_exit(void) +{ + platform_driver_unregister(&wsa_irq_driver); +} +module_exit(wsa_irq_drv_exit); + +MODULE_DESCRIPTION("WSA881x IRQ driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wsa881x-irq.h b/sound/soc/codecs/wsa881x-irq.h new file mode 100644 index 0000000000000000000000000000000000000000..270eb917a6669a87bc03fa42dc1af77447807ada --- /dev/null +++ b/sound/soc/codecs/wsa881x-irq.h @@ -0,0 +1,82 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __WSA881X_IRQ_H__ +#define __WSA881X_IRQ_H__ + +#include +#include +#include + +/** + * enum wsa_interrupts - wsa interrupt number + * @WSA_INT_SAF2WAR: Temp irq interrupt, from safe state to warning state. + * @WSA_INT_WAR2SAF: Temp irq interrupt, from warning state to safe state. + * @WSA_INT_DISABLE: Disable Temp sensor interrupts. + * @WSA_INT_OCP: OCP interrupt. + * @WSA_INT_CLIP: CLIP detect interrupt. + * @WSA_NUM_IRQS: MAX Interrupt number. + * + * WSA IRQ Interrupt numbers. + */ +enum wsa_interrupts { + WSA_INT_SAF2WAR = 0, + WSA_INT_WAR2SAF, + WSA_INT_DISABLE, + WSA_INT_OCP, + WSA_INT_CLIP, + WSA_NUM_IRQS, +}; + +/** + * struct wsa_resource - the basic wsa_resource structure + * @irq_lock: lock used by irq_chip functions. + * @nested_irq_lock: lock used while handling nested interrupts. + * @irq: interrupt number. + * @irq_masks_cur: current mask value to be written to mask registers. + * @irq_masks_cache: cached mask value. + * @num_irqs: number of supported interrupts. + * @num_irq_regs: number of irq registers. + * @parent: parent pointer. + * @dev: device pointer. + * @domain: irq domain pointer. + * codec: codec pointer. + * + * Contains required members used in wsa irq driver. + */ + +struct wsa_resource { + struct mutex irq_lock; + struct mutex nested_irq_lock; + unsigned int irq; + u8 irq_masks_cur; + u8 irq_masks_cache; + bool irq_level_high[8]; + int num_irqs; + int num_irq_regs; + void *parent; + struct device *dev; + struct irq_domain *domain; + struct snd_soc_codec *codec; +}; + +void wsa_set_codec(struct snd_soc_codec *codec); +void wsa_free_irq(int irq, void *data); +void wsa_enable_irq(struct wsa_resource *wsa_res, int irq); +void wsa_disable_irq(struct wsa_resource *wsa_res, int irq); +void wsa_disable_irq_sync(struct wsa_resource *wsa_res, int irq); +int wsa_request_irq(struct wsa_resource *wsa_res, + int irq, irq_handler_t handler, + const char *name, void *data); + +void wsa_irq_exit(struct wsa_resource *wsa_res); + +#endif /* __WSA881X_IRQ_H__ */ diff --git a/sound/soc/codecs/wsa881x-registers-analog.h b/sound/soc/codecs/wsa881x-registers-analog.h new file mode 100644 index 0000000000000000000000000000000000000000..a5ebf8e1aab457b3593395514485f2451668875d --- /dev/null +++ b/sound/soc/codecs/wsa881x-registers-analog.h @@ -0,0 +1,206 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef WSA881X_REGISTERS_H +#define WSA881X_REGISTERS_H + +#define WSA881X_DIGITAL_BASE 0x0000 +#define WSA881X_ANALOG_BASE 0x0100 + +#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_32 (WSA881X_DIGITAL_BASE+0x00A0) +#define WSA881X_OTP_REG_33 (WSA881X_DIGITAL_BASE+0x00A1) +#define WSA881X_OTP_REG_34 (WSA881X_DIGITAL_BASE+0x00A2) +#define WSA881X_OTP_REG_35 (WSA881X_DIGITAL_BASE+0x00A3) +#define WSA881X_OTP_REG_36 (WSA881X_DIGITAL_BASE+0x00A4) +#define WSA881X_OTP_REG_37 (WSA881X_DIGITAL_BASE+0x00A5) +#define WSA881X_OTP_REG_38 (WSA881X_DIGITAL_BASE+0x00A6) +#define WSA881X_OTP_REG_39 (WSA881X_DIGITAL_BASE+0x00A7) +#define WSA881X_OTP_REG_40 (WSA881X_DIGITAL_BASE+0x00A8) +#define WSA881X_OTP_REG_41 (WSA881X_DIGITAL_BASE+0x00A9) +#define WSA881X_OTP_REG_42 (WSA881X_DIGITAL_BASE+0x00AA) +#define WSA881X_OTP_REG_43 (WSA881X_DIGITAL_BASE+0x00AB) +#define WSA881X_OTP_REG_44 (WSA881X_DIGITAL_BASE+0x00AC) +#define WSA881X_OTP_REG_45 (WSA881X_DIGITAL_BASE+0x00AD) +#define WSA881X_OTP_REG_46 (WSA881X_DIGITAL_BASE+0x00AE) +#define WSA881X_OTP_REG_47 (WSA881X_DIGITAL_BASE+0x00AF) +#define WSA881X_OTP_REG_48 (WSA881X_DIGITAL_BASE+0x00B0) +#define WSA881X_OTP_REG_49 (WSA881X_DIGITAL_BASE+0x00B1) +#define WSA881X_OTP_REG_50 (WSA881X_DIGITAL_BASE+0x00B2) +#define WSA881X_OTP_REG_51 (WSA881X_DIGITAL_BASE+0x00B3) +#define WSA881X_OTP_REG_52 (WSA881X_DIGITAL_BASE+0x00B4) +#define WSA881X_OTP_REG_53 (WSA881X_DIGITAL_BASE+0x00B5) +#define WSA881X_OTP_REG_54 (WSA881X_DIGITAL_BASE+0x00B6) +#define WSA881X_OTP_REG_55 (WSA881X_DIGITAL_BASE+0x00B7) +#define WSA881X_OTP_REG_56 (WSA881X_DIGITAL_BASE+0x00B8) +#define WSA881X_OTP_REG_57 (WSA881X_DIGITAL_BASE+0x00B9) +#define WSA881X_OTP_REG_58 (WSA881X_DIGITAL_BASE+0x00BA) +#define WSA881X_OTP_REG_59 (WSA881X_DIGITAL_BASE+0x00BB) +#define WSA881X_OTP_REG_60 (WSA881X_DIGITAL_BASE+0x00BC) +#define WSA881X_OTP_REG_61 (WSA881X_DIGITAL_BASE+0x00BD) +#define WSA881X_OTP_REG_62 (WSA881X_DIGITAL_BASE+0x00BE) +#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_IBIAS (WSA881X_ANALOG_BASE+0x0015) +#define WSA881X_SPKR_DRV_EN (WSA881X_ANALOG_BASE+0x001A) +#define WSA881X_SPKR_DRV_GAIN (WSA881X_ANALOG_BASE+0x001B) +#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_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_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 WSA881X_NUM_REGISTERS (WSA881X_SPKR_STATUS3+1) +#define WSA881X_MAX_REGISTER (WSA881X_NUM_REGISTERS-1) +#define WSA881X_CACHE_SIZE WSA881X_NUM_REGISTERS +#endif /* WSA881X_REGISTERS_H */ diff --git a/sound/soc/codecs/wsa881x-registers.h b/sound/soc/codecs/wsa881x-registers.h new file mode 100644 index 0000000000000000000000000000000000000000..825a5f034da82db1a4f9a9e26011f7e57077f225 --- /dev/null +++ b/sound/soc/codecs/wsa881x-registers.h @@ -0,0 +1,178 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef WSA881X_REGISTERS_H +#define WSA881X_REGISTERS_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_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_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_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 WSA881X_NUM_REGISTERS (WSA881X_SPKR_STATUS3+1) +#define WSA881X_MAX_REGISTER (WSA881X_NUM_REGISTERS-1) +#define WSA881X_CACHE_SIZE WSA881X_NUM_REGISTERS + +#endif /* WSA881X_REGISTERS_H */ diff --git a/sound/soc/codecs/wsa881x-regmap-analog.c b/sound/soc/codecs/wsa881x-regmap-analog.c new file mode 100644 index 0000000000000000000000000000000000000000..2bc3c9eb7061f2462bae5cd3a7c39ff02a71c79e --- /dev/null +++ b/sound/soc/codecs/wsa881x-regmap-analog.c @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "wsa881x-registers-analog.h" +#include "wsa881x-analog.h" + +struct reg_default wsa881x_ana_reg_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_MASK, 0x1F}, + {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_28, 0xFF}, + {WSA881X_OTP_REG_29, 0xFF}, + {WSA881X_OTP_REG_30, 0xFF}, + {WSA881X_OTP_REG_31, 0xFF}, + {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_ADC_CTRL, 0x00}, + {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_SEL_IBIAS, 0x25}, + {WSA881X_ADC_EN_SEL_IBIAS, 0x10}, + {WSA881X_SPKR_DRV_EN, 0x74}, + {WSA881X_SPKR_DRV_GAIN, 0x01}, + {WSA881X_SPKR_DAC_CTL, 0x40}, + {WSA881X_SPKR_DRV_DBG, 0x15}, + {WSA881X_SPKR_PWRSTG_DBG, 0x00}, + {WSA881X_SPKR_OCP_CTL, 0xD4}, + {WSA881X_SPKR_CLIP_CTL, 0x90}, + {WSA881X_SPKR_BBM_CTL, 0x00}, + {WSA881X_SPKR_MISC_CTL1, 0x80}, + {WSA881X_SPKR_MISC_CTL2, 0x00}, + {WSA881X_SPKR_BIAS_INT, 0x56}, + {WSA881X_SPKR_PA_INT, 0x54}, + {WSA881X_SPKR_BIAS_CAL, 0xAC}, + {WSA881X_SPKR_BIAS_PSRR, 0x54}, + {WSA881X_SPKR_STATUS1, 0x00}, + {WSA881X_SPKR_STATUS2, 0x00}, + {WSA881X_BOOST_EN_CTL, 0x18}, + {WSA881X_BOOST_CURRENT_LIMIT, 0x7A}, + {WSA881X_BOOST_PS_CTL, 0xC0}, + {WSA881X_BOOST_PRESET_OUT1, 0x77}, + {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_LOOP_STABILITY, 0xAD}, + {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_ATEST2, 0x00}, + {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D}, + {WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D}, + {WSA881X_BONGO_RESRV_REG1, 0x00}, + {WSA881X_BONGO_RESRV_REG2, 0x00}, + {WSA881X_SPKR_PROT_SAR, 0x00}, + {WSA881X_SPKR_STATUS3, 0x00}, +}; + +struct reg_default wsa881x_ana_reg_defaults_0[] = { + {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_MASK, 0x1F}, + {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_28, 0xFF}, + {WSA881X_OTP_REG_29, 0xFF}, + {WSA881X_OTP_REG_30, 0xFF}, + {WSA881X_OTP_REG_31, 0xFF}, + {WSA881X_OTP_REG_63, 0x40}, +}; + +struct reg_default wsa881x_ana_reg_defaults_1[] = { + {WSA881X_BIAS_REF_CTRL - WSA881X_ANALOG_BASE, 0x6C}, + {WSA881X_BIAS_TEST - WSA881X_ANALOG_BASE, 0x16}, + {WSA881X_BIAS_BIAS - WSA881X_ANALOG_BASE, 0xF0}, + {WSA881X_TEMP_OP - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_TEMP_IREF_CTRL - WSA881X_ANALOG_BASE, 0x56}, + {WSA881X_TEMP_ISENS_CTRL - WSA881X_ANALOG_BASE, 0x47}, + {WSA881X_TEMP_CLK_CTRL - WSA881X_ANALOG_BASE, 0x87}, + {WSA881X_TEMP_TEST - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_TEMP_BIAS - WSA881X_ANALOG_BASE, 0x51}, + {WSA881X_TEMP_ADC_CTRL - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_TEMP_DOUT_MSB - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_TEMP_DOUT_LSB - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_ADC_EN_MODU_V - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_ADC_EN_MODU_I - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_ADC_EN_DET_TEST_V - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_ADC_EN_DET_TEST_I - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_ADC_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x25}, + {WSA881X_ADC_EN_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x10}, + {WSA881X_SPKR_DRV_EN - WSA881X_ANALOG_BASE, 0x74}, + {WSA881X_SPKR_DRV_GAIN - WSA881X_ANALOG_BASE, 0x01}, + {WSA881X_SPKR_DAC_CTL - WSA881X_ANALOG_BASE, 0x40}, + {WSA881X_SPKR_DRV_DBG - WSA881X_ANALOG_BASE, 0x15}, + {WSA881X_SPKR_PWRSTG_DBG - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_OCP_CTL - WSA881X_ANALOG_BASE, 0xD4}, + {WSA881X_SPKR_CLIP_CTL - WSA881X_ANALOG_BASE, 0x90}, + {WSA881X_SPKR_BBM_CTL - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_MISC_CTL1 - WSA881X_ANALOG_BASE, 0x80}, + {WSA881X_SPKR_MISC_CTL2 - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_BIAS_INT - WSA881X_ANALOG_BASE, 0x56}, + {WSA881X_SPKR_PA_INT - WSA881X_ANALOG_BASE, 0x54}, + {WSA881X_SPKR_BIAS_CAL - WSA881X_ANALOG_BASE, 0xAC}, + {WSA881X_SPKR_BIAS_PSRR - WSA881X_ANALOG_BASE, 0x54}, + {WSA881X_SPKR_STATUS1 - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_STATUS2 - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_BOOST_EN_CTL - WSA881X_ANALOG_BASE, 0x18}, + {WSA881X_BOOST_CURRENT_LIMIT - WSA881X_ANALOG_BASE, 0x7A}, + {WSA881X_BOOST_PS_CTL - WSA881X_ANALOG_BASE, 0xC0}, + {WSA881X_BOOST_PRESET_OUT1 - WSA881X_ANALOG_BASE, 0x77}, + {WSA881X_BOOST_PRESET_OUT2 - WSA881X_ANALOG_BASE, 0x70}, + {WSA881X_BOOST_FORCE_OUT - WSA881X_ANALOG_BASE, 0x0E}, + {WSA881X_BOOST_LDO_PROG - WSA881X_ANALOG_BASE, 0x16}, + {WSA881X_BOOST_SLOPE_COMP_ISENSE_FB - WSA881X_ANALOG_BASE, 0x71}, + {WSA881X_BOOST_RON_CTL - WSA881X_ANALOG_BASE, 0x0F}, + {WSA881X_BOOST_LOOP_STABILITY - WSA881X_ANALOG_BASE, 0xAD}, + {WSA881X_BOOST_ZX_CTL - WSA881X_ANALOG_BASE, 0x34}, + {WSA881X_BOOST_START_CTL - WSA881X_ANALOG_BASE, 0x23}, + {WSA881X_BOOST_MISC1_CTL - WSA881X_ANALOG_BASE, 0x80}, + {WSA881X_BOOST_MISC2_CTL - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_BOOST_MISC3_CTL - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_BOOST_ATEST_CTL - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_PROT_FE_GAIN - WSA881X_ANALOG_BASE, 0x46}, + {WSA881X_SPKR_PROT_FE_CM_LDO_SET - WSA881X_ANALOG_BASE, 0x3B}, + {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 - WSA881X_ANALOG_BASE, 0x8D}, + {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 - WSA881X_ANALOG_BASE, 0x8D}, + {WSA881X_SPKR_PROT_ATEST1 - WSA881X_ANALOG_BASE, 0x01}, + {WSA881X_SPKR_PROT_ATEST2 - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_PROT_FE_VSENSE_VCM - WSA881X_ANALOG_BASE, 0x8D}, + {WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 - WSA881X_ANALOG_BASE, 0x4D}, + {WSA881X_BONGO_RESRV_REG1 - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_BONGO_RESRV_REG2 - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_PROT_SAR - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_STATUS3 - WSA881X_ANALOG_BASE, 0x00}, +}; + +struct reg_default wsa881x_rev_2_0_dig[] = { + {WSA881X_RESET_CTL, 0x00}, + {WSA881X_TADC_VALUE_CTL, 0x01}, + {WSA881X_INTR_MASK, 0x1B}, + {WSA881X_IOPAD_CTL, 0x00}, + {WSA881X_OTP_REG_28, 0x3F}, + {WSA881X_OTP_REG_29, 0x3F}, + {WSA881X_OTP_REG_30, 0x01}, + {WSA881X_OTP_REG_31, 0x01}, +}; + +struct reg_default wsa881x_rev_2_0_ana[] = { + {WSA881X_TEMP_ADC_CTRL, 0x03}, + {WSA881X_ADC_SEL_IBIAS, 0x45}, + {WSA881X_SPKR_DRV_GAIN, 0xC1}, + {WSA881X_SPKR_DAC_CTL, 0x42}, + {WSA881X_SPKR_BBM_CTL, 0x02}, + {WSA881X_SPKR_MISC_CTL1, 0x40}, + {WSA881X_SPKR_MISC_CTL2, 0x07}, + {WSA881X_SPKR_BIAS_INT, 0x5F}, + {WSA881X_SPKR_BIAS_PSRR, 0x44}, + {WSA881X_BOOST_PS_CTL, 0xA0}, + {WSA881X_BOOST_PRESET_OUT1, 0xB7}, + {WSA881X_BOOST_LOOP_STABILITY, 0x8D}, + {WSA881X_SPKR_PROT_ATEST2, 0x02}, + {WSA881X_BONGO_RESRV_REG1, 0x5E}, + {WSA881X_BONGO_RESRV_REG2, 0x07}, +}; + +struct reg_default wsa881x_rev_2_0_regmap_ana[] = { + {WSA881X_TEMP_ADC_CTRL - WSA881X_ANALOG_BASE, 0x03}, + {WSA881X_ADC_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x45}, + {WSA881X_SPKR_DRV_GAIN - WSA881X_ANALOG_BASE, 0xC1}, + {WSA881X_SPKR_DAC_CTL - WSA881X_ANALOG_BASE, 0x42}, + {WSA881X_SPKR_BBM_CTL - WSA881X_ANALOG_BASE, 0x02}, + {WSA881X_SPKR_MISC_CTL1 - WSA881X_ANALOG_BASE, 0x40}, + {WSA881X_SPKR_MISC_CTL2 - WSA881X_ANALOG_BASE, 0x07}, + {WSA881X_SPKR_BIAS_INT - WSA881X_ANALOG_BASE, 0x5F}, + {WSA881X_SPKR_BIAS_PSRR - WSA881X_ANALOG_BASE, 0x44}, + {WSA881X_BOOST_PS_CTL - WSA881X_ANALOG_BASE, 0xA0}, + {WSA881X_BOOST_PRESET_OUT1 - WSA881X_ANALOG_BASE, 0xB7}, + {WSA881X_BOOST_LOOP_STABILITY - WSA881X_ANALOG_BASE, 0x8D}, + {WSA881X_SPKR_PROT_ATEST2 - WSA881X_ANALOG_BASE, 0x02}, + {WSA881X_BONGO_RESRV_REG1 - WSA881X_ANALOG_BASE, 0x5E}, + {WSA881X_BONGO_RESRV_REG2 - WSA881X_ANALOG_BASE, 0x07}, +}; + +/** + * wsa881x_update_reg_defaults_2_0 - update default values of regs for v2.0 + * + * WSA881x v2.0 has different default values for certain analog and digital + * registers compared to v1.x. Therefore, update the values of these registers + * with the values from tables defined above for v2.0. + */ +void wsa881x_update_reg_defaults_2_0(void) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(wsa881x_rev_2_0_dig); i++) { + for (j = 0; j < ARRAY_SIZE(wsa881x_ana_reg_defaults); j++) + if (wsa881x_ana_reg_defaults[j].reg == + wsa881x_rev_2_0_dig[i].reg) + wsa881x_ana_reg_defaults[j].def = + wsa881x_rev_2_0_dig[i].def; + } + for (i = 0; i < ARRAY_SIZE(wsa881x_rev_2_0_ana); i++) { + for (j = 0; j < ARRAY_SIZE(wsa881x_ana_reg_defaults); j++) + if (wsa881x_ana_reg_defaults[j].reg == + wsa881x_rev_2_0_ana[i].reg) + wsa881x_ana_reg_defaults[j].def = + wsa881x_rev_2_0_ana[i].def; + } +} +EXPORT_SYMBOL(wsa881x_update_reg_defaults_2_0); + +/** + * wsa881x_update_regmap_2_0 - update regmap framework with new tables + * @regmap: pointer to WSA881x regmap structure + * @flag: indicates digital or analog WSA881x slave + * + * WSA881x v2.0 has some new registers for both analog and digital slaves. + * Update the regmap framework with all the new registers. + */ +void wsa881x_update_regmap_2_0(struct regmap *regmap, int flag) +{ + u16 ret = 0; + + switch (flag) { + case WSA881X_DIGITAL_SLAVE: + ret = regmap_register_patch(regmap, wsa881x_rev_2_0_dig, + ARRAY_SIZE(wsa881x_rev_2_0_dig)); + break; + case WSA881X_ANALOG_SLAVE: + ret = regmap_register_patch(regmap, wsa881x_rev_2_0_ana, + ARRAY_SIZE(wsa881x_rev_2_0_ana)); + break; + default: + pr_debug("%s: unknown version", __func__); + ret = -EINVAL; + break; + } + if (ret) + pr_err("%s: Failed to update regmap defaults ret= %d\n", + __func__, ret); +} +EXPORT_SYMBOL(wsa881x_update_regmap_2_0); + +static bool wsa881x_readable_register(struct device *dev, unsigned int reg) +{ + return wsa881x_ana_reg_readable[reg]; +} + +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_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_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_SPKR_PROT_SAR: + return true; + default: + return false; + } +} + +struct regmap_config wsa881x_ana_regmap_config[] = { +{ + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, + .reg_defaults = wsa881x_ana_reg_defaults_0, + .num_reg_defaults = ARRAY_SIZE(wsa881x_ana_reg_defaults_0), + .max_register = WSA881X_MAX_REGISTER, + .volatile_reg = wsa881x_volatile_register, + .readable_reg = wsa881x_readable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}, +{ + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, + .reg_defaults = wsa881x_ana_reg_defaults_1, + .num_reg_defaults = ARRAY_SIZE(wsa881x_ana_reg_defaults_1), + .max_register = WSA881X_MAX_REGISTER, + .volatile_reg = wsa881x_volatile_register, + .readable_reg = wsa881x_readable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +} +}; diff --git a/sound/soc/codecs/wsa881x-regmap.c b/sound/soc/codecs/wsa881x-regmap.c new file mode 100644 index 0000000000000000000000000000000000000000..63bbbfa6beab7c3664b9889f0e51277c40ed00ed --- /dev/null +++ b/sound/soc/codecs/wsa881x-regmap.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "wsa881x-registers.h" +#include "wsa881x.h" + +/* + * Default register reset values that are common across different versions + * are defined here. If a register reset value is changed based on version + * then remove it from this structure and add it in version specific + * structures. + */ +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}, +}; + +/* 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}, +}; + +/* + * wsa881x_regmap_defaults - update regmap default register values + * @regmap: pointer to regmap structure + * @version: wsa881x version id + * + * Update regmap default register values based on version id + * + */ +void wsa881x_regmap_defaults(struct regmap *regmap, u8 version) +{ + u16 ret = 0; + + if (!regmap) { + pr_debug("%s: regmap structure is NULL\n", __func__); + return; + } + + regcache_cache_only(regmap, true); + ret = regmap_multi_reg_write(regmap, wsa881x_rev_2_0, + ARRAY_SIZE(wsa881x_rev_2_0)); + regcache_cache_only(regmap, false); + + if (ret) + pr_debug("%s: Failed to update regmap defaults ret= %d\n", + __func__, ret); +} +EXPORT_SYMBOL(wsa881x_regmap_defaults); + +static bool wsa881x_readable_register(struct device *dev, unsigned int reg) +{ + return wsa881x_reg_readable[reg]; +} + +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; + } +} + +struct regmap_config wsa881x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wsa881x_defaults, + .num_reg_defaults = ARRAY_SIZE(wsa881x_defaults), + .max_register = WSA881X_MAX_REGISTER, + .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, +}; diff --git a/sound/soc/codecs/wsa881x-tables-analog.c b/sound/soc/codecs/wsa881x-tables-analog.c new file mode 100644 index 0000000000000000000000000000000000000000..061ed6fff7989163b288911cc45fc2389bcb87c9 --- /dev/null +++ b/sound/soc/codecs/wsa881x-tables-analog.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "wsa881x-registers-analog.h" + +const u8 wsa881x_ana_reg_readable[WSA881X_CACHE_SIZE] = { + [WSA881X_CHIP_ID0] = 1, + [WSA881X_CHIP_ID1] = 1, + [WSA881X_CHIP_ID2] = 1, + [WSA881X_CHIP_ID3] = 1, + [WSA881X_BUS_ID] = 1, + [WSA881X_CDC_RST_CTL] = 1, + [WSA881X_CDC_TOP_CLK_CTL] = 1, + [WSA881X_CDC_ANA_CLK_CTL] = 1, + [WSA881X_CDC_DIG_CLK_CTL] = 1, + [WSA881X_CLOCK_CONFIG] = 1, + [WSA881X_ANA_CTL] = 1, + [WSA881X_SWR_RESET_EN] = 1, + [WSA881X_RESET_CTL] = 1, + [WSA881X_TADC_VALUE_CTL] = 1, + [WSA881X_TEMP_DETECT_CTL] = 1, + [WSA881X_TEMP_MSB] = 1, + [WSA881X_TEMP_LSB] = 1, + [WSA881X_TEMP_CONFIG0] = 1, + [WSA881X_TEMP_CONFIG1] = 1, + [WSA881X_CDC_CLIP_CTL] = 1, + [WSA881X_SDM_PDM9_LSB] = 1, + [WSA881X_SDM_PDM9_MSB] = 1, + [WSA881X_CDC_RX_CTL] = 1, + [WSA881X_DEM_BYPASS_DATA0] = 1, + [WSA881X_DEM_BYPASS_DATA1] = 1, + [WSA881X_DEM_BYPASS_DATA2] = 1, + [WSA881X_DEM_BYPASS_DATA3] = 1, + [WSA881X_OTP_CTRL0] = 1, + [WSA881X_OTP_CTRL1] = 1, + [WSA881X_HDRIVE_CTL_GROUP1] = 1, + [WSA881X_INTR_MODE] = 1, + [WSA881X_INTR_MASK] = 1, + [WSA881X_INTR_STATUS] = 1, + [WSA881X_INTR_CLEAR] = 1, + [WSA881X_INTR_LEVEL] = 1, + [WSA881X_INTR_SET] = 1, + [WSA881X_INTR_TEST] = 1, + [WSA881X_PDM_TEST_MODE] = 1, + [WSA881X_ATE_TEST_MODE] = 1, + [WSA881X_PIN_CTL_MODE] = 1, + [WSA881X_PIN_CTL_OE] = 1, + [WSA881X_PIN_WDATA_IOPAD] = 1, + [WSA881X_PIN_STATUS] = 1, + [WSA881X_DIG_DEBUG_MODE] = 1, + [WSA881X_DIG_DEBUG_SEL] = 1, + [WSA881X_DIG_DEBUG_EN] = 1, + [WSA881X_SWR_HM_TEST1] = 1, + [WSA881X_SWR_HM_TEST2] = 1, + [WSA881X_TEMP_DETECT_DBG_CTL] = 1, + [WSA881X_TEMP_DEBUG_MSB] = 1, + [WSA881X_TEMP_DEBUG_LSB] = 1, + [WSA881X_SAMPLE_EDGE_SEL] = 1, + [WSA881X_IOPAD_CTL] = 1, + [WSA881X_SPARE_0] = 1, + [WSA881X_SPARE_1] = 1, + [WSA881X_SPARE_2] = 1, + [WSA881X_OTP_REG_0] = 1, + [WSA881X_OTP_REG_1] = 1, + [WSA881X_OTP_REG_2] = 1, + [WSA881X_OTP_REG_3] = 1, + [WSA881X_OTP_REG_4] = 1, + [WSA881X_OTP_REG_5] = 1, + [WSA881X_OTP_REG_6] = 1, + [WSA881X_OTP_REG_7] = 1, + [WSA881X_OTP_REG_8] = 1, + [WSA881X_OTP_REG_9] = 1, + [WSA881X_OTP_REG_10] = 1, + [WSA881X_OTP_REG_11] = 1, + [WSA881X_OTP_REG_12] = 1, + [WSA881X_OTP_REG_13] = 1, + [WSA881X_OTP_REG_14] = 1, + [WSA881X_OTP_REG_15] = 1, + [WSA881X_OTP_REG_16] = 1, + [WSA881X_OTP_REG_17] = 1, + [WSA881X_OTP_REG_18] = 1, + [WSA881X_OTP_REG_19] = 1, + [WSA881X_OTP_REG_20] = 1, + [WSA881X_OTP_REG_21] = 1, + [WSA881X_OTP_REG_22] = 1, + [WSA881X_OTP_REG_23] = 1, + [WSA881X_OTP_REG_24] = 1, + [WSA881X_OTP_REG_25] = 1, + [WSA881X_OTP_REG_26] = 1, + [WSA881X_OTP_REG_27] = 1, + [WSA881X_OTP_REG_28] = 1, + [WSA881X_OTP_REG_29] = 1, + [WSA881X_OTP_REG_30] = 1, + [WSA881X_OTP_REG_31] = 1, + [WSA881X_OTP_REG_63] = 1, + /* Analog Registers */ + [WSA881X_BIAS_REF_CTRL] = 1, + [WSA881X_BIAS_TEST] = 1, + [WSA881X_BIAS_BIAS] = 1, + [WSA881X_TEMP_OP] = 1, + [WSA881X_TEMP_IREF_CTRL] = 1, + [WSA881X_TEMP_ISENS_CTRL] = 1, + [WSA881X_TEMP_CLK_CTRL] = 1, + [WSA881X_TEMP_TEST] = 1, + [WSA881X_TEMP_BIAS] = 1, + [WSA881X_TEMP_ADC_CTRL] = 1, + [WSA881X_TEMP_DOUT_MSB] = 1, + [WSA881X_TEMP_DOUT_LSB] = 1, + [WSA881X_ADC_EN_MODU_V] = 1, + [WSA881X_ADC_EN_MODU_I] = 1, + [WSA881X_ADC_EN_DET_TEST_V] = 1, + [WSA881X_ADC_EN_DET_TEST_I] = 1, + [WSA881X_ADC_SEL_IBIAS] = 1, + [WSA881X_ADC_EN_SEL_IBIAS] = 1, + [WSA881X_SPKR_DRV_EN] = 1, + [WSA881X_SPKR_DRV_GAIN] = 1, + [WSA881X_SPKR_DAC_CTL] = 1, + [WSA881X_SPKR_DRV_DBG] = 1, + [WSA881X_SPKR_PWRSTG_DBG] = 1, + [WSA881X_SPKR_OCP_CTL] = 1, + [WSA881X_SPKR_CLIP_CTL] = 1, + [WSA881X_SPKR_BBM_CTL] = 1, + [WSA881X_SPKR_MISC_CTL1] = 1, + [WSA881X_SPKR_MISC_CTL2] = 1, + [WSA881X_SPKR_BIAS_INT] = 1, + [WSA881X_SPKR_PA_INT] = 1, + [WSA881X_SPKR_BIAS_CAL] = 1, + [WSA881X_SPKR_BIAS_PSRR] = 1, + [WSA881X_SPKR_STATUS1] = 1, + [WSA881X_SPKR_STATUS2] = 1, + [WSA881X_BOOST_EN_CTL] = 1, + [WSA881X_BOOST_CURRENT_LIMIT] = 1, + [WSA881X_BOOST_PS_CTL] = 1, + [WSA881X_BOOST_PRESET_OUT1] = 1, + [WSA881X_BOOST_PRESET_OUT2] = 1, + [WSA881X_BOOST_FORCE_OUT] = 1, + [WSA881X_BOOST_LDO_PROG] = 1, + [WSA881X_BOOST_SLOPE_COMP_ISENSE_FB] = 1, + [WSA881X_BOOST_RON_CTL] = 1, + [WSA881X_BOOST_LOOP_STABILITY] = 1, + [WSA881X_BOOST_ZX_CTL] = 1, + [WSA881X_BOOST_START_CTL] = 1, + [WSA881X_BOOST_MISC1_CTL] = 1, + [WSA881X_BOOST_MISC2_CTL] = 1, + [WSA881X_BOOST_MISC3_CTL] = 1, + [WSA881X_BOOST_ATEST_CTL] = 1, + [WSA881X_SPKR_PROT_FE_GAIN] = 1, + [WSA881X_SPKR_PROT_FE_CM_LDO_SET] = 1, + [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1] = 1, + [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2] = 1, + [WSA881X_SPKR_PROT_ATEST1] = 1, + [WSA881X_SPKR_PROT_ATEST2] = 1, + [WSA881X_SPKR_PROT_FE_VSENSE_VCM] = 1, + [WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1] = 1, + [WSA881X_BONGO_RESRV_REG1] = 1, + [WSA881X_BONGO_RESRV_REG2] = 1, + [WSA881X_SPKR_PROT_SAR] = 1, + [WSA881X_SPKR_STATUS3] = 1, +}; diff --git a/sound/soc/codecs/wsa881x-tables.c b/sound/soc/codecs/wsa881x-tables.c new file mode 100644 index 0000000000000000000000000000000000000000..4f1212be9c5bc90c66e014e8bbd030a3454ef7ea --- /dev/null +++ b/sound/soc/codecs/wsa881x-tables.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "wsa881x-registers.h" + +const u8 wsa881x_reg_readable[WSA881X_CACHE_SIZE] = { + [WSA881X_CHIP_ID0] = 1, + [WSA881X_CHIP_ID1] = 1, + [WSA881X_CHIP_ID2] = 1, + [WSA881X_CHIP_ID3] = 1, + [WSA881X_BUS_ID] = 1, + [WSA881X_CDC_RST_CTL] = 1, + [WSA881X_CDC_TOP_CLK_CTL] = 1, + [WSA881X_CDC_ANA_CLK_CTL] = 1, + [WSA881X_CDC_DIG_CLK_CTL] = 1, + [WSA881X_CLOCK_CONFIG] = 1, + [WSA881X_ANA_CTL] = 1, + [WSA881X_SWR_RESET_EN] = 1, + [WSA881X_RESET_CTL] = 1, + [WSA881X_TADC_VALUE_CTL] = 1, + [WSA881X_TEMP_DETECT_CTL] = 1, + [WSA881X_TEMP_MSB] = 1, + [WSA881X_TEMP_LSB] = 1, + [WSA881X_TEMP_CONFIG0] = 1, + [WSA881X_TEMP_CONFIG1] = 1, + [WSA881X_CDC_CLIP_CTL] = 1, + [WSA881X_SDM_PDM9_LSB] = 1, + [WSA881X_SDM_PDM9_MSB] = 1, + [WSA881X_CDC_RX_CTL] = 1, + [WSA881X_DEM_BYPASS_DATA0] = 1, + [WSA881X_DEM_BYPASS_DATA1] = 1, + [WSA881X_DEM_BYPASS_DATA2] = 1, + [WSA881X_DEM_BYPASS_DATA3] = 1, + [WSA881X_OTP_CTRL0] = 1, + [WSA881X_OTP_CTRL1] = 1, + [WSA881X_HDRIVE_CTL_GROUP1] = 1, + [WSA881X_INTR_MODE] = 1, + [WSA881X_INTR_MASK] = 1, + [WSA881X_INTR_STATUS] = 1, + [WSA881X_INTR_CLEAR] = 1, + [WSA881X_INTR_LEVEL] = 1, + [WSA881X_INTR_SET] = 1, + [WSA881X_INTR_TEST] = 1, + [WSA881X_PDM_TEST_MODE] = 1, + [WSA881X_ATE_TEST_MODE] = 1, + [WSA881X_PIN_CTL_MODE] = 1, + [WSA881X_PIN_CTL_OE] = 1, + [WSA881X_PIN_WDATA_IOPAD] = 1, + [WSA881X_PIN_STATUS] = 1, + [WSA881X_DIG_DEBUG_MODE] = 1, + [WSA881X_DIG_DEBUG_SEL] = 1, + [WSA881X_DIG_DEBUG_EN] = 1, + [WSA881X_SWR_HM_TEST1] = 1, + [WSA881X_SWR_HM_TEST2] = 1, + [WSA881X_TEMP_DETECT_DBG_CTL] = 1, + [WSA881X_TEMP_DEBUG_MSB] = 1, + [WSA881X_TEMP_DEBUG_LSB] = 1, + [WSA881X_SAMPLE_EDGE_SEL] = 1, + [WSA881X_IOPAD_CTL] = 1, + [WSA881X_SPARE_0] = 1, + [WSA881X_SPARE_1] = 1, + [WSA881X_SPARE_2] = 1, + [WSA881X_OTP_REG_0] = 1, + [WSA881X_OTP_REG_1] = 1, + [WSA881X_OTP_REG_2] = 1, + [WSA881X_OTP_REG_3] = 1, + [WSA881X_OTP_REG_4] = 1, + [WSA881X_OTP_REG_5] = 1, + [WSA881X_OTP_REG_6] = 1, + [WSA881X_OTP_REG_7] = 1, + [WSA881X_OTP_REG_8] = 1, + [WSA881X_OTP_REG_9] = 1, + [WSA881X_OTP_REG_10] = 1, + [WSA881X_OTP_REG_11] = 1, + [WSA881X_OTP_REG_12] = 1, + [WSA881X_OTP_REG_13] = 1, + [WSA881X_OTP_REG_14] = 1, + [WSA881X_OTP_REG_15] = 1, + [WSA881X_OTP_REG_16] = 1, + [WSA881X_OTP_REG_17] = 1, + [WSA881X_OTP_REG_18] = 1, + [WSA881X_OTP_REG_19] = 1, + [WSA881X_OTP_REG_20] = 1, + [WSA881X_OTP_REG_21] = 1, + [WSA881X_OTP_REG_22] = 1, + [WSA881X_OTP_REG_23] = 1, + [WSA881X_OTP_REG_24] = 1, + [WSA881X_OTP_REG_25] = 1, + [WSA881X_OTP_REG_26] = 1, + [WSA881X_OTP_REG_27] = 1, + [WSA881X_OTP_REG_28] = 1, + [WSA881X_OTP_REG_29] = 1, + [WSA881X_OTP_REG_30] = 1, + [WSA881X_OTP_REG_31] = 1, + [WSA881X_OTP_REG_63] = 1, + /* Analog Registers */ + [WSA881X_BIAS_REF_CTRL] = 1, + [WSA881X_BIAS_TEST] = 1, + [WSA881X_BIAS_BIAS] = 1, + [WSA881X_TEMP_OP] = 1, + [WSA881X_TEMP_IREF_CTRL] = 1, + [WSA881X_TEMP_ISENS_CTRL] = 1, + [WSA881X_TEMP_CLK_CTRL] = 1, + [WSA881X_TEMP_TEST] = 1, + [WSA881X_TEMP_BIAS] = 1, + [WSA881X_TEMP_ADC_CTRL] = 1, + [WSA881X_TEMP_DOUT_MSB] = 1, + [WSA881X_TEMP_DOUT_LSB] = 1, + [WSA881X_ADC_EN_MODU_V] = 1, + [WSA881X_ADC_EN_MODU_I] = 1, + [WSA881X_ADC_EN_DET_TEST_V] = 1, + [WSA881X_ADC_EN_DET_TEST_I] = 1, + [WSA881X_ADC_SEL_IBIAS] = 1, + [WSA881X_ADC_EN_SEL_IBAIS] = 1, + [WSA881X_SPKR_DRV_EN] = 1, + [WSA881X_SPKR_DRV_GAIN] = 1, + [WSA881X_SPKR_DAC_CTL] = 1, + [WSA881X_SPKR_DRV_DBG] = 1, + [WSA881X_SPKR_PWRSTG_DBG] = 1, + [WSA881X_SPKR_OCP_CTL] = 1, + [WSA881X_SPKR_CLIP_CTL] = 1, + [WSA881X_SPKR_BBM_CTL] = 1, + [WSA881X_SPKR_MISC_CTL1] = 1, + [WSA881X_SPKR_MISC_CTL2] = 1, + [WSA881X_SPKR_BIAS_INT] = 1, + [WSA881X_SPKR_PA_INT] = 1, + [WSA881X_SPKR_BIAS_CAL] = 1, + [WSA881X_SPKR_BIAS_PSRR] = 1, + [WSA881X_SPKR_STATUS1] = 1, + [WSA881X_SPKR_STATUS2] = 1, + [WSA881X_BOOST_EN_CTL] = 1, + [WSA881X_BOOST_CURRENT_LIMIT] = 1, + [WSA881X_BOOST_PS_CTL] = 1, + [WSA881X_BOOST_PRESET_OUT1] = 1, + [WSA881X_BOOST_PRESET_OUT2] = 1, + [WSA881X_BOOST_FORCE_OUT] = 1, + [WSA881X_BOOST_LDO_PROG] = 1, + [WSA881X_BOOST_SLOPE_COMP_ISENSE_FB] = 1, + [WSA881X_BOOST_RON_CTL] = 1, + [WSA881X_BOOST_LOOP_STABILITY] = 1, + [WSA881X_BOOST_ZX_CTL] = 1, + [WSA881X_BOOST_START_CTL] = 1, + [WSA881X_BOOST_MISC1_CTL] = 1, + [WSA881X_BOOST_MISC2_CTL] = 1, + [WSA881X_BOOST_MISC3_CTL] = 1, + [WSA881X_BOOST_ATEST_CTL] = 1, + [WSA881X_SPKR_PROT_FE_GAIN] = 1, + [WSA881X_SPKR_PROT_FE_CM_LDO_SET] = 1, + [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1] = 1, + [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2] = 1, + [WSA881X_SPKR_PROT_ATEST1] = 1, + [WSA881X_SPKR_PROT_ATEST2] = 1, + [WSA881X_SPKR_PROT_FE_VSENSE_VCM] = 1, + [WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1] = 1, + [WSA881X_BONGO_RESRV_REG1] = 1, + [WSA881X_BONGO_RESRV_REG2] = 1, + [WSA881X_SPKR_PROT_SAR] = 1, + [WSA881X_SPKR_STATUS3] = 1, +}; diff --git a/sound/soc/codecs/wsa881x-temp-sensor.c b/sound/soc/codecs/wsa881x-temp-sensor.c new file mode 100644 index 0000000000000000000000000000000000000000..0079d0f6cd526b670b3f35e33428e985596cddab --- /dev/null +++ b/sound/soc/codecs/wsa881x-temp-sensor.c @@ -0,0 +1,143 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include "wsa881x-temp-sensor.h" + +#define T1_TEMP -10 +#define T2_TEMP 150 +#define LOW_TEMP_THRESHOLD 5 +#define HIGH_TEMP_THRESHOLD 45 +#define TEMP_INVALID 0xFFFF + +/* + * wsa881x_get_temp - get wsa temperature + * @thermal: thermal zone device + * @temp: temperature value + * + * Get the temperature of wsa881x. + * + * Return: 0 on success or negative error code on failure. + */ +int wsa881x_get_temp(struct thermal_zone_device *thermal, + int *temp) +{ + struct wsa881x_tz_priv *pdata; + struct snd_soc_codec *codec; + struct wsa_temp_register reg; + int dmeas, d1, d2; + int ret = 0; + int temp_val; + int t1 = T1_TEMP; + int t2 = T2_TEMP; + + if (!thermal) + return -EINVAL; + + if (thermal->devdata) { + pdata = thermal->devdata; + if (pdata->codec) { + codec = pdata->codec; + } else { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + } else { + pr_err("%s: pdata is NULL\n", __func__); + return -EINVAL; + } + if (pdata->wsa_temp_reg_read) { + ret = pdata->wsa_temp_reg_read(codec, ®); + if (ret) { + pr_err("%s: temperature register read failed: %d\n", + __func__, ret); + return ret; + } + } else { + pr_err("%s: wsa_temp_reg_read is NULL\n", __func__); + return -EINVAL; + } + /* + * Temperature register values are expected to be in the + * following range. + * d1_msb = 68 - 92 and d1_lsb = 0, 64, 128, 192 + * d2_msb = 185 -218 and d2_lsb = 0, 64, 128, 192 + */ + if ((reg.d1_msb < 68 || reg.d1_msb > 92) || + (!(reg.d1_lsb == 0 || reg.d1_lsb == 64 || reg.d1_lsb == 128 || + reg.d1_lsb == 192)) || + (reg.d2_msb < 185 || reg.d2_msb > 218) || + (!(reg.d2_lsb == 0 || reg.d2_lsb == 64 || reg.d2_lsb == 128 || + reg.d2_lsb == 192))) { + printk_ratelimited("%s: Temperature registers[%d %d %d %d] are out of range\n", + __func__, reg.d1_msb, reg.d1_lsb, reg.d2_msb, + reg.d2_lsb); + } + dmeas = ((reg.dmeas_msb << 0x8) | reg.dmeas_lsb) >> 0x6; + d1 = ((reg.d1_msb << 0x8) | reg.d1_lsb) >> 0x6; + d2 = ((reg.d2_msb << 0x8) | reg.d2_lsb) >> 0x6; + + if (d1 == d2) + temp_val = TEMP_INVALID; + else + temp_val = t1 + (((dmeas - d1) * (t2 - t1))/(d2 - d1)); + + if (temp_val <= LOW_TEMP_THRESHOLD || + temp_val >= HIGH_TEMP_THRESHOLD) { + printk_ratelimited("%s: T0: %d is out of range[%d, %d]\n", + __func__, temp_val, LOW_TEMP_THRESHOLD, + HIGH_TEMP_THRESHOLD); + } + if (temp) + *temp = temp_val; + pr_debug("%s: t0 measured: %d dmeas = %d, d1 = %d, d2 = %d\n", + __func__, temp_val, dmeas, d1, d2); + return ret; +} +EXPORT_SYMBOL(wsa881x_get_temp); + +static struct thermal_zone_device_ops wsa881x_thermal_ops = { + .get_temp = wsa881x_get_temp, +}; + +int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata) +{ + struct thermal_zone_device *tz_dev; + + if (tz_pdata == NULL) { + pr_err("%s: thermal pdata is NULL\n", __func__); + return -EINVAL; + } + /* Register with the thermal zone */ + tz_dev = thermal_zone_device_register(tz_pdata->name, + 0, 0, tz_pdata, + &wsa881x_thermal_ops, NULL, 0, 0); + if (IS_ERR(tz_dev)) { + pr_err("%s: thermal device register failed.\n", __func__); + return -EINVAL; + } + tz_pdata->tz_dev = tz_dev; + return 0; +} +EXPORT_SYMBOL(wsa881x_init_thermal); + +void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev) +{ + if (tz_dev) + thermal_zone_device_unregister(tz_dev); +} +EXPORT_SYMBOL(wsa881x_deinit_thermal); diff --git a/sound/soc/codecs/wsa881x-temp-sensor.h b/sound/soc/codecs/wsa881x-temp-sensor.h new file mode 100644 index 0000000000000000000000000000000000000000..d6c1eb75e940873a241183cfef63b7eaedbeba59 --- /dev/null +++ b/sound/soc/codecs/wsa881x-temp-sensor.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef WSA881X_TEMP_SENSOR_H +#define WSA881X_TEMP_SENSOR_H + +#include +#include + +struct wsa_temp_register { + u8 d1_msb; + u8 d1_lsb; + u8 d2_msb; + u8 d2_lsb; + u8 dmeas_msb; + u8 dmeas_lsb; +}; +typedef int32_t (*wsa_temp_register_read)(struct snd_soc_codec *codec, + struct wsa_temp_register *wsa_temp_reg); +struct wsa881x_tz_priv { + struct thermal_zone_device *tz_dev; + struct snd_soc_codec *codec; + struct wsa_temp_register *wsa_temp_reg; + char name[80]; + wsa_temp_register_read wsa_temp_reg_read; +}; + +int wsa881x_get_temp(struct thermal_zone_device *tz_dev, int *temp); +int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata); +void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev); +#endif diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c new file mode 100644 index 0000000000000000000000000000000000000000..ff6bfdb18b52d6fea468ac8ef55d91a0d1a1eafe --- /dev/null +++ b/sound/soc/codecs/wsa881x.c @@ -0,0 +1,1404 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wsa881x.h" +#include "wsa881x-temp-sensor.h" + +#define WSA881X_NUM_RETRY 5 + +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, +}; + +enum { + DISABLE = 0, + ENABLE, +}; + +enum { + SWR_DAC_PORT, + SWR_COMP_PORT, + SWR_BOOST_PORT, + SWR_VISENSE_PORT, +}; + +struct swr_port { + u8 port_id; + u8 ch_mask; + u32 ch_rate; + u8 num_ch; +}; + +enum { + WSA881X_DEV_DOWN, + WSA881X_DEV_UP, +}; + +/* + * 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 swr_device *swr_slave; + struct snd_soc_codec *codec; + bool comp_enable; + bool boost_enable; + bool visense_enable; + u8 pa_gain; + struct swr_port port[WSA881X_MAX_SWR_PORTS]; + int pd_gpio; + struct wsa881x_tz_priv tz_pdata; + int bg_cnt; + int clk_cnt; + int version; + struct mutex bg_lock; + struct mutex res_lock; + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + int state; + struct delayed_work ocp_ctl_work; + struct device_node *wsa_rst_np; +}; + +#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_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 + +static int wsa881x_ocp_poll_timer_sec = WSA881X_OCP_CTL_POLL_TIMER_SEC; +module_param(wsa881x_ocp_poll_timer_sec, int, 0664); +MODULE_PARM_DESC(wsa881x_ocp_poll_timer_sec, "timer for ocp ctl polling"); + +static struct wsa881x_priv *dbgwsa881x; +static struct dentry *debugfs_wsa881x_dent; +static struct dentry *debugfs_peek; +static struct dentry *debugfs_poke; +static struct dentry *debugfs_reg_dump; +static unsigned int read_data; +static unsigned int devnum; + +static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec, + bool enable); + +static const char * const wsa_pa_gain_text[] = { + "G_18_DB", "G_16P5_DB", "G_15_DB", "G_13P5_DB", "G_12_DB", "G_10P5_DB", + "G_9_DB", "G_7P5_DB", "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", + "G_0_DB" +}; + +static const struct soc_enum wsa_pa_gain_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa_pa_gain_text), wsa_pa_gain_text); + +static int wsa_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->pa_gain; + + dev_dbg(codec->dev, "%s: PA gain = 0x%x\n", __func__, wsa881x->pa_gain); + + return 0; +} + +static int wsa_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + wsa881x->pa_gain = ucontrol->value.integer.value[0]; + + return 0; +} + +static const struct snd_kcontrol_new wsa_analog_gain_controls[] = { + SOC_ENUM_EXT("WSA PA Gain", wsa_pa_gain_enum, + wsa_pa_gain_get, wsa_pa_gain_put), +}; + +static int codec_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int get_parameters(char *buf, u32 *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtou32(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + return 0; +} + +static ssize_t wsa881x_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + struct wsa881x_priv *wsa881x; + char buffer[WSA881X_VERSION_ENTRY_SIZE]; + int len; + + wsa881x = (struct wsa881x_priv *) entry->private_data; + if (!wsa881x) { + pr_err("%s: wsa881x priv is null\n", __func__); + return -EINVAL; + } + + len = snprintf(buffer, sizeof(buffer), "WSA881X-SOUNDWIRE_2_0\n"); + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops wsa881x_codec_info_ops = { + .read = wsa881x_codec_version_read, +}; + +/* + * wsa881x_codec_info_create_codec_entry - creates wsa881x module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates wsa881x module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int wsa881x_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct wsa881x_priv *wsa881x; + struct snd_soc_card *card; + char name[80]; + + if (!codec_root || !codec) + return -EINVAL; + + wsa881x = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + snprintf(name, sizeof(name), "%s.%x", "wsa881x", + (u32)wsa881x->swr_slave->addr); + + wsa881x->entry = snd_register_module_info(codec_root->module, + (const char *)name, + codec_root); + if (!wsa881x->entry) { + dev_dbg(codec->dev, "%s: failed to create wsa881x entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + wsa881x->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create wsa881x version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = wsa881x; + version_entry->size = WSA881X_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &wsa881x_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + wsa881x->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(wsa881x_codec_info_create_codec_entry); + +static bool is_swr_slv_reg_readable(int reg) +{ + bool ret = true; + + if (((reg > 0x46) && (reg < 0x4A)) || + ((reg > 0x4A) && (reg < 0x50)) || + ((reg > 0x55) && (reg < 0xE0)) || + ((reg > 0xE0) && (reg < 0xF0)) || + ((reg > 0xF0) && (reg < 0x100)) || + ((reg > 0x105) && (reg < 0x120)) || + ((reg > 0x128) && (reg < 0x130)) || + ((reg > 0x138) && (reg < 0x200)) || + ((reg > 0x205) && (reg < 0x220)) || + ((reg > 0x228) && (reg < 0x230)) || + ((reg > 0x238) && (reg < 0x300)) || + ((reg > 0x305) && (reg < 0x320)) || + ((reg > 0x328) && (reg < 0x330)) || + ((reg > 0x338) && (reg < 0x400)) || + ((reg > 0x405) && (reg < 0x420))) + ret = false; + + return ret; +} + +static ssize_t wsa881x_swrslave_reg_show(char __user *ubuf, size_t count, + loff_t *ppos) +{ + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[SWR_SLV_MAX_BUF_LEN]; + + if (!ubuf || !ppos || (devnum == 0)) + return 0; + + for (i = (((int) *ppos / BYTES_PER_LINE) + SWR_SLV_START_REG_ADDR); + i <= SWR_SLV_MAX_REG_ADDR; i++) { + if (!is_swr_slv_reg_readable(i)) + continue; + swr_read(dbgwsa881x->swr_slave, devnum, + i, ®_val, 1); + len = snprintf(tmp_buf, 25, "0x%.3x: 0x%.2x\n", i, + (reg_val & 0xFF)); + if ((total + len) >= count - 1) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + pr_err("%s: fail to copy reg dump\n", __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } + +copy_err: + return total; +} + +static ssize_t codec_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char lbuf[SWR_SLV_RD_BUF_LEN]; + char *access_str; + ssize_t ret_cnt; + + if (!count || !file || !ppos || !ubuf) + return -EINVAL; + + access_str = file->private_data; + if (*ppos < 0) + return -EINVAL; + + if (!strcmp(access_str, "swrslave_peek")) { + snprintf(lbuf, sizeof(lbuf), "0x%x\n", (read_data & 0xFF)); + ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf, + strnlen(lbuf, 7)); + } else if (!strcmp(access_str, "swrslave_reg_dump")) { + ret_cnt = wsa881x_swrslave_reg_show(ubuf, count, ppos); + } else { + pr_err("%s: %s not permitted to read\n", __func__, access_str); + ret_cnt = -EPERM; + } + return ret_cnt; +} + +static ssize_t codec_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char lbuf[SWR_SLV_WR_BUF_LEN]; + int rc; + u32 param[5]; + char *access_str; + + if (!filp || !ppos || !ubuf) + return -EINVAL; + + access_str = filp->private_data; + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + if (!strcmp(access_str, "swrslave_poke")) { + /* write */ + rc = get_parameters(lbuf, param, 3); + if ((param[0] <= SWR_SLV_MAX_REG_ADDR) && (param[1] <= 0xFF) && + (rc == 0)) + swr_write(dbgwsa881x->swr_slave, param[2], + param[0], ¶m[1]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "swrslave_peek")) { + /* read */ + rc = get_parameters(lbuf, param, 2); + if ((param[0] <= SWR_SLV_MAX_REG_ADDR) && (rc == 0)) + swr_read(dbgwsa881x->swr_slave, param[1], + param[0], &read_data, 1); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "swrslave_reg_dump")) { + /* reg dump */ + rc = get_parameters(lbuf, param, 1); + if ((rc == 0) && (param[0] > 0) && + (param[0] <= SWR_SLV_MAX_DEVICES)) + devnum = param[0]; + else + rc = -EINVAL; + } + if (rc == 0) + rc = cnt; + else + pr_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations codec_debug_ops = { + .open = codec_debug_open, + .write = codec_debug_write, + .read = codec_debug_read, +}; + +static const struct reg_sequence wsa881x_pre_pmu_pa[] = { + {WSA881X_SPKR_DRV_GAIN, 0x41, 0}, + {WSA881X_SPKR_MISC_CTL1, 0x01, 0}, + {WSA881X_ADC_EN_DET_TEST_I, 0x01, 0}, + {WSA881X_ADC_EN_MODU_V, 0x02, 0}, + {WSA881X_ADC_EN_DET_TEST_V, 0x10, 0}, + {WSA881X_SPKR_PWRSTG_DBG, 0xA0, 0}, +}; + +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_post_pmu_pa[] = { + {WSA881X_SPKR_PWRSTG_DBG, 0x00, 0}, + {WSA881X_ADC_EN_DET_TEST_V, 0x00, 0}, + {WSA881X_ADC_EN_MODU_V, 0x00, 0}, + {WSA881X_ADC_EN_DET_TEST_I, 0x00, 0}, +}; + +static const struct reg_sequence wsa881x_vi_txfe_en[] = { + {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0}, + {WSA881X_SPKR_PROT_ATEST2, 0x0A, 0}, + {WSA881X_SPKR_PROT_FE_GAIN, 0xCF, 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}, +}; + +static int wsa881x_boost_ctrl(struct snd_soc_codec *codec, bool enable) +{ + dev_dbg(codec->dev, "%s: enable:%d\n", __func__, enable); + if (enable) + snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x80); + else + snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x00); + /* + * 1.5ms sleep is needed after boost enable/disable as per + * HW requirement + */ + usleep_range(1500, 1510); + return 0; +} + +static int wsa881x_visense_txfe_ctrl(struct snd_soc_codec *codec, bool enable, + u8 isense1_gain, u8 isense2_gain, + u8 vsense_gain) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, + "%s: enable:%d, isense1 gain: %d, isense2 gain: %d, vsense_gain %d\n", + __func__, enable, isense1_gain, isense2_gain, vsense_gain); + + 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_update_bits(codec, 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_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN, + 0x01, 0x00); + } + return 0; +} + +static int wsa881x_visense_adc_ctrl(struct snd_soc_codec *codec, bool enable) +{ + + dev_dbg(codec->dev, "%s: enable:%d\n", __func__, enable); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, (0x01 << 7), + (enable << 7)); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, (0x01 << 7), + (enable << 7)); + return 0; +} + +static void wsa881x_bandgap_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: enable:%d, bg_count:%d\n", __func__, + enable, wsa881x->bg_cnt); + mutex_lock(&wsa881x->bg_lock); + if (enable) { + ++wsa881x->bg_cnt; + if (wsa881x->bg_cnt == 1) { + snd_soc_update_bits(codec, WSA881X_TEMP_OP, + 0x08, 0x08); + /* 400usec sleep is needed as per HW requirement */ + usleep_range(400, 410); + snd_soc_update_bits(codec, WSA881X_TEMP_OP, + 0x04, 0x04); + } + } else { + --wsa881x->bg_cnt; + if (wsa881x->bg_cnt <= 0) { + WARN_ON(wsa881x->bg_cnt < 0); + wsa881x->bg_cnt = 0; + snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x00); + snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x08, 0x00); + } + } + mutex_unlock(&wsa881x->bg_lock); +} + +static void wsa881x_clk_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: enable:%d, clk_count:%d\n", __func__, + enable, wsa881x->clk_cnt); + mutex_lock(&wsa881x->res_lock); + if (enable) { + ++wsa881x->clk_cnt; + if (wsa881x->clk_cnt == 1) { + snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x01); + snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x01); + } + } else { + --wsa881x->clk_cnt; + if (wsa881x->clk_cnt <= 0) { + WARN_ON(wsa881x->clk_cnt < 0); + wsa881x->clk_cnt = 0; + snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x00); + snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x00); + } + } + mutex_unlock(&wsa881x->res_lock); +} + +static int wsa881x_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->comp_enable; + return 0; +} + +static int wsa881x_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Compander enable current %d, new %d\n", + __func__, wsa881x->comp_enable, value); + wsa881x->comp_enable = value; + return 0; +} + +static int wsa881x_get_boost(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->boost_enable; + return 0; +} + +static int wsa881x_set_boost(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Boost enable current %d, new %d\n", + __func__, wsa881x->boost_enable, value); + wsa881x->boost_enable = value; + return 0; +} + +static int wsa881x_get_visense(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->visense_enable; + return 0; +} + +static int wsa881x_set_visense(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: VIsense enable current %d, new %d\n", + __func__, wsa881x->visense_enable, value); + wsa881x->visense_enable = value; + return 0; +} + +static const struct snd_kcontrol_new wsa881x_snd_controls[] = { + SOC_SINGLE_EXT("COMP Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_compander, wsa881x_set_compander), + + SOC_SINGLE_EXT("BOOST Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_boost, wsa881x_set_boost), + + SOC_SINGLE_EXT("VISENSE Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_visense, wsa881x_set_visense), +}; + +static const struct snd_kcontrol_new swr_dac_port[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static int wsa881x_set_port(struct snd_soc_codec *codec, int port_idx, + u8 *port_id, u8 *num_ch, u8 *ch_mask, u32 *ch_rate) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + *port_id = wsa881x->port[port_idx].port_id; + *num_ch = wsa881x->port[port_idx].num_ch; + *ch_mask = wsa881x->port[port_idx].ch_mask; + *ch_rate = wsa881x->port[port_idx].ch_rate; + return 0; +} + +static int wsa881x_enable_swr_dac_port(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + u8 port_id[WSA881X_MAX_SWR_PORTS]; + u8 num_ch[WSA881X_MAX_SWR_PORTS]; + u8 ch_mask[WSA881X_MAX_SWR_PORTS]; + u32 ch_rate[WSA881X_MAX_SWR_PORTS]; + u8 num_port = 0; + + dev_dbg(codec->dev, "%s: event %d name %s\n", __func__, + event, w->name); + if (wsa881x == NULL) + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wsa881x_set_port(codec, SWR_DAC_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port]); + ++num_port; + + if (wsa881x->comp_enable) { + wsa881x_set_port(codec, SWR_COMP_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port]); + ++num_port; + } + if (wsa881x->boost_enable) { + wsa881x_set_port(codec, SWR_BOOST_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port]); + ++num_port; + } + if (wsa881x->visense_enable) { + wsa881x_set_port(codec, SWR_VISENSE_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port]); + ++num_port; + } + swr_connect_port(wsa881x->swr_slave, &port_id[0], num_port, + &ch_mask[0], &ch_rate[0], &num_ch[0]); + break; + case SND_SOC_DAPM_POST_PMU: + break; + case SND_SOC_DAPM_PRE_PMD: + break; + case SND_SOC_DAPM_POST_PMD: + port_id[num_port] = wsa881x->port[SWR_DAC_PORT].port_id; + ++num_port; + if (wsa881x->comp_enable) { + port_id[num_port] = + wsa881x->port[SWR_COMP_PORT].port_id; + ++num_port; + } + if (wsa881x->boost_enable) { + port_id[num_port] = + wsa881x->port[SWR_BOOST_PORT].port_id; + ++num_port; + } + if (wsa881x->visense_enable) { + port_id[num_port] = + wsa881x->port[SWR_VISENSE_PORT].port_id; + ++num_port; + } + swr_disconnect_port(wsa881x->swr_slave, &port_id[0], num_port); + break; + default: + break; + } + return 0; +} + +static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: %s %d boost %d visense %d\n", __func__, + w->name, event, wsa881x->boost_enable, + wsa881x->visense_enable); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wsa881x_resource_acquire(codec, ENABLE); + if (wsa881x->boost_enable) + wsa881x_boost_ctrl(codec, ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + swr_slvdev_datapath_control(wsa881x->swr_slave, + wsa881x->swr_slave->dev_num, + false); + if (wsa881x->boost_enable) + wsa881x_boost_ctrl(codec, DISABLE); + wsa881x_resource_acquire(codec, DISABLE); + break; + } + return 0; +} + +static int wsa881x_ramp_pa_gain(struct snd_soc_codec *codec, + int min_gain, int max_gain, int udelay) +{ + int val; + + for (val = min_gain; max_gain <= val; val--) { + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, + 0xF0, val << 4); + /* + * 1ms delay is needed for every step change in gain as per + * HW requirement. + */ + usleep_range(udelay, udelay+10); + } + return 0; +} + +static void wsa881x_ocp_ctl_work(struct work_struct *work) +{ + struct wsa881x_priv *wsa881x; + struct delayed_work *dwork; + struct snd_soc_codec *codec; + int temp_val; + + dwork = to_delayed_work(work); + wsa881x = container_of(dwork, struct wsa881x_priv, ocp_ctl_work); + + codec = wsa881x->codec; + wsa881x_get_temp(wsa881x->tz_pdata.tz_dev, &temp_val); + dev_dbg(codec->dev, " temp = %d\n", temp_val); + + if (temp_val <= WSA881X_OCP_CTL_TEMP_CELSIUS) + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x00); + else + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0); + + schedule_delayed_work(&wsa881x->ocp_ctl_work, + msecs_to_jiffies(wsa881x_ocp_poll_timer_sec * 1000)); +} + +static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int min_gain, max_gain; + + dev_dbg(codec->dev, "%s: %s %d\n", __func__, w->name, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x80); + regmap_multi_reg_write(wsa881x->regmap, + wsa881x_pre_pmu_pa_2_0, + ARRAY_SIZE(wsa881x_pre_pmu_pa_2_0)); + swr_slvdev_datapath_control(wsa881x->swr_slave, + wsa881x->swr_slave->dev_num, + true); + /* Set register mode if compander is not enabled */ + if (!wsa881x->comp_enable) + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, + 0x08, 0x08); + else + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, + 0x08, 0x00); + + break; + case SND_SOC_DAPM_POST_PMU: + if (!wsa881x->comp_enable) { + 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(codec, min_gain, max_gain, 1000); + } + if (wsa881x->visense_enable) { + wsa881x_visense_txfe_ctrl(codec, ENABLE, + 0x00, 0x03, 0x01); + snd_soc_update_bits(codec, WSA881X_ADC_EN_SEL_IBAIS, + 0x07, 0x01); + wsa881x_visense_adc_ctrl(codec, ENABLE); + } + schedule_delayed_work(&wsa881x->ocp_ctl_work, + msecs_to_jiffies(WSA881X_OCP_CTL_TIMER_SEC * 1000)); + /* Force remove group */ + swr_remove_from_group(wsa881x->swr_slave, + wsa881x->swr_slave->dev_num); + break; + case SND_SOC_DAPM_POST_PMD: + if (wsa881x->visense_enable) { + wsa881x_visense_adc_ctrl(codec, DISABLE); + wsa881x_visense_txfe_ctrl(codec, DISABLE, + 0x00, 0x01, 0x01); + } + cancel_delayed_work_sync(&wsa881x->ocp_ctl_work); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + + SND_SOC_DAPM_MIXER_E("SWR DAC_Port", SND_SOC_NOPM, 0, 0, swr_dac_port, + ARRAY_SIZE(swr_dac_port), wsa881x_enable_swr_dac_port, + 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("RDAC", NULL, WSA881X_SPKR_DAC_CTL, 7, 0, + wsa881x_rdac_event, + 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_OUTPUT("SPKR"), +}; + +static const struct snd_soc_dapm_route wsa881x_audio_map[] = { + {"SWR DAC_Port", "Switch", "IN"}, + {"RDAC", NULL, "SWR DAC_Port"}, + {"SPKR PGA", NULL, "RDAC"}, + {"SPKR", NULL, "SPKR PGA"}, +}; + +int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, u8 num_port, + unsigned int *ch_mask, unsigned int *ch_rate) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int i; + + if (!port || !ch_mask || !ch_rate || + (num_port > WSA881X_MAX_SWR_PORTS)) { + dev_err(codec->dev, + "%s: Invalid port=%pK, ch_mask=%pK, ch_rate=%pK\n", + __func__, port, ch_mask, ch_rate); + return -EINVAL; + } + for (i = 0; i < num_port; i++) { + wsa881x->port[i].port_id = port[i]; + wsa881x->port[i].ch_mask = ch_mask[i]; + wsa881x->port[i].ch_rate = ch_rate[i]; + wsa881x->port[i].num_ch = __sw_hweight8(ch_mask[i]); + } + return 0; +} +EXPORT_SYMBOL(wsa881x_set_channel_map); + +static void wsa881x_init(struct snd_soc_codec *codec) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + wsa881x->version = snd_soc_read(codec, WSA881X_CHIP_ID1); + wsa881x_regmap_defaults(wsa881x->regmap, wsa881x->version); + /* Bring out of analog reset */ + snd_soc_update_bits(codec, WSA881X_CDC_RST_CTL, 0x02, 0x02); + /* Bring out of digital reset */ + snd_soc_update_bits(codec, WSA881X_CDC_RST_CTL, 0x01, 0x01); + + snd_soc_update_bits(codec, WSA881X_CLOCK_CONFIG, 0x10, 0x10); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x02, 0x02); + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80); + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06); + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_INT, 0xFF, 0x00); + snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0xF0, 0x40); + snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0x0E, 0x0E); + snd_soc_update_bits(codec, WSA881X_BOOST_LOOP_STABILITY, + 0x03, 0x03); + snd_soc_update_bits(codec, WSA881X_BOOST_MISC2_CTL, 0xFF, 0x14); + snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, 0x80, 0x80); + snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, 0x03, 0x00); + snd_soc_update_bits(codec, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, + 0x0C, 0x04); + snd_soc_update_bits(codec, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, + 0x03, 0x00); + if (snd_soc_read(codec, WSA881X_OTP_REG_0)) + snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1, + 0xF0, 0x70); + snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT2, + 0xF0, 0x30); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x08, 0x08); + snd_soc_update_bits(codec, WSA881X_BOOST_CURRENT_LIMIT, + 0x0F, 0x08); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x30, 0x30); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x0C, 0x00); + snd_soc_update_bits(codec, WSA881X_OTP_REG_28, 0x3F, 0x3A); + snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG1, + 0xFF, 0xB2); + snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG2, + 0xFF, 0x05); +} + +static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec, + bool enable) +{ + wsa881x_clk_ctrl(codec, enable); + wsa881x_bandgap_ctrl(codec, enable); + return 0; +} + +static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec, + struct wsa_temp_register *wsa_temp_reg) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + struct swr_device *dev; + u8 devnum = 0; + + if (!wsa881x) { + dev_err(codec->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + dev = wsa881x->swr_slave; + if (dev && (wsa881x->state == WSA881X_DEV_DOWN)) { + if (swr_get_logical_dev_num(dev, dev->addr, &devnum)) { + dev_err(codec->dev, + "%s get devnum %d for dev addr %lx failed\n", + __func__, devnum, dev->addr); + return -EINVAL; + } + } + mutex_lock(&wsa881x->res_lock); + if (!wsa881x->clk_cnt) { + regcache_mark_dirty(wsa881x->regmap); + regcache_sync(wsa881x->regmap); + } + mutex_unlock(&wsa881x->res_lock); + + wsa881x_resource_acquire(codec, ENABLE); + + snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x00); + wsa_temp_reg->dmeas_msb = snd_soc_read(codec, WSA881X_TEMP_MSB); + wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, WSA881X_TEMP_LSB); + snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x01); + wsa_temp_reg->d1_msb = snd_soc_read(codec, WSA881X_OTP_REG_1); + wsa_temp_reg->d1_lsb = snd_soc_read(codec, WSA881X_OTP_REG_2); + wsa_temp_reg->d2_msb = snd_soc_read(codec, WSA881X_OTP_REG_3); + wsa_temp_reg->d2_lsb = snd_soc_read(codec, WSA881X_OTP_REG_4); + + wsa881x_resource_acquire(codec, DISABLE); + + return 0; +} + +static int wsa881x_probe(struct snd_soc_codec *codec) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + struct swr_device *dev; + + if (!wsa881x) + return -EINVAL; + + dev = wsa881x->swr_slave; + wsa881x->codec = codec; + mutex_init(&wsa881x->bg_lock); + mutex_init(&wsa881x->res_lock); + wsa881x_init(codec); + snprintf(wsa881x->tz_pdata.name, sizeof(wsa881x->tz_pdata.name), + "%s.%x", "wsatz", (u8)dev->addr); + wsa881x->bg_cnt = 0; + wsa881x->clk_cnt = 0; + wsa881x->tz_pdata.codec = codec; + wsa881x->tz_pdata.wsa_temp_reg_read = wsa881x_temp_reg_read; + wsa881x_init_thermal(&wsa881x->tz_pdata); + snd_soc_add_codec_controls(codec, wsa_analog_gain_controls, + ARRAY_SIZE(wsa_analog_gain_controls)); + INIT_DELAYED_WORK(&wsa881x->ocp_ctl_work, wsa881x_ocp_ctl_work); + return 0; +} + +static int wsa881x_remove(struct snd_soc_codec *codec) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + if (wsa881x->tz_pdata.tz_dev) + wsa881x_deinit_thermal(wsa881x->tz_pdata.tz_dev); + mutex_destroy(&wsa881x->bg_lock); + mutex_destroy(&wsa881x->res_lock); + + return 0; +} + +static struct regmap *wsa881x_get_regmap(struct device *dev) +{ + struct wsa881x_priv *control = swr_get_dev_data(to_swr_device(dev)); + + if (!control) + return NULL; + + return control->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_wsa881x = { + .probe = wsa881x_probe, + .remove = wsa881x_remove, + .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), + .get_regmap = wsa881x_get_regmap, +}; + +static int wsa881x_swr_startup(struct swr_device *swr_dev) +{ + int ret = 0; + u8 devnum = 0; + struct wsa881x_priv *wsa881x; + + wsa881x = swr_get_dev_data(swr_dev); + if (!wsa881x) { + dev_err(&swr_dev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + + /* + * Add 5msec delay to provide sufficient time for + * soundwire auto enumeration of slave devices as + * as per HW requirement. + */ + usleep_range(5000, 5010); + ret = swr_get_logical_dev_num(swr_dev, swr_dev->addr, &devnum); + if (ret) { + dev_dbg(&swr_dev->dev, "%s failed to get devnum, err:%d\n", + __func__, ret); + goto err; + } + swr_dev->dev_num = devnum; + + wsa881x->regmap = devm_regmap_init_swr(swr_dev, + &wsa881x_regmap_config); + if (IS_ERR(wsa881x->regmap)) { + ret = PTR_ERR(wsa881x->regmap); + dev_err(&swr_dev->dev, "%s: regmap_init failed %d\n", + __func__, ret); + goto err; + } + + ret = snd_soc_register_codec(&swr_dev->dev, &soc_codec_dev_wsa881x, + NULL, 0); + if (ret) { + dev_err(&swr_dev->dev, "%s: Codec registration failed\n", + __func__); + goto err; + } + +err: + return ret; +} + +static int wsa881x_gpio_ctrl(struct wsa881x_priv *wsa881x, bool enable) +{ + int ret = 0; + + if (wsa881x->pd_gpio < 0) { + dev_err(wsa881x->dev, "%s: gpio is not valid %d\n", + __func__, wsa881x->pd_gpio); + return -EINVAL; + } + + if (wsa881x->wsa_rst_np) { + if (enable) + ret = msm_cdc_pinctrl_select_active_state( + wsa881x->wsa_rst_np); + else + ret = msm_cdc_pinctrl_select_sleep_state( + wsa881x->wsa_rst_np); + if (ret != 0) + dev_err(wsa881x->dev, + "%s: Failed to turn state %d; ret=%d\n", + __func__, enable, ret); + } else { + if (gpio_is_valid(wsa881x->pd_gpio)) + gpio_direction_output(wsa881x->pd_gpio, enable); + } + + return ret; +} + +static int wsa881x_gpio_init(struct swr_device *pdev) +{ + int ret = 0; + struct wsa881x_priv *wsa881x; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(&pdev->dev, "%s: gpio %d request with name %s\n", + __func__, wsa881x->pd_gpio, dev_name(&pdev->dev)); + ret = gpio_request(wsa881x->pd_gpio, dev_name(&pdev->dev)); + if (ret) { + if (ret == -EBUSY) { + /* GPIO was already requested */ + dev_dbg(&pdev->dev, + "%s: gpio %d is already set to high\n", + __func__, wsa881x->pd_gpio); + ret = 0; + } else { + dev_err(&pdev->dev, "%s: Failed to request gpio %d, err: %d\n", + __func__, wsa881x->pd_gpio, ret); + } + } + return ret; +} + +static int wsa881x_swr_probe(struct swr_device *pdev) +{ + int ret = 0; + struct wsa881x_priv *wsa881x; + + wsa881x = devm_kzalloc(&pdev->dev, sizeof(struct wsa881x_priv), + GFP_KERNEL); + if (!wsa881x) + return -ENOMEM; + wsa881x->wsa_rst_np = of_parse_phandle(pdev->dev.of_node, + "qcom,spkr-sd-n-node", 0); + if (!wsa881x->wsa_rst_np) { + dev_dbg(&pdev->dev, "%s: Not using pinctrl, fallback to gpio\n", + __func__); + wsa881x->pd_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,spkr-sd-n-gpio", 0); + if (wsa881x->pd_gpio < 0) { + dev_err(&pdev->dev, "%s: %s property is not found %d\n", + __func__, "qcom,spkr-sd-n-gpio", + wsa881x->pd_gpio); + goto err; + } + dev_dbg(&pdev->dev, "%s: reset gpio %d\n", __func__, + wsa881x->pd_gpio); + } + swr_set_dev_data(pdev, wsa881x); + + wsa881x->swr_slave = pdev; + + if (!wsa881x->wsa_rst_np) { + ret = wsa881x_gpio_init(pdev); + if (ret) + goto err; + } + wsa881x_gpio_ctrl(wsa881x, true); + wsa881x->state = WSA881X_DEV_UP; + + if (!debugfs_wsa881x_dent) { + dbgwsa881x = wsa881x; + debugfs_wsa881x_dent = debugfs_create_dir( + "wsa881x_swr_slave", 0); + if (!IS_ERR(debugfs_wsa881x_dent)) { + debugfs_peek = debugfs_create_file("swrslave_peek", + S_IFREG | 0444, debugfs_wsa881x_dent, + (void *) "swrslave_peek", + &codec_debug_ops); + + debugfs_poke = debugfs_create_file("swrslave_poke", + S_IFREG | 0444, debugfs_wsa881x_dent, + (void *) "swrslave_poke", + &codec_debug_ops); + + debugfs_reg_dump = debugfs_create_file( + "swrslave_reg_dump", + S_IFREG | 0444, + debugfs_wsa881x_dent, + (void *) "swrslave_reg_dump", + &codec_debug_ops); + } + } + return 0; + +err: + return ret; +} + +static int wsa881x_swr_remove(struct swr_device *pdev) +{ + struct wsa881x_priv *wsa881x; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + debugfs_remove_recursive(debugfs_wsa881x_dent); + snd_soc_unregister_codec(&pdev->dev); + if (wsa881x->pd_gpio) + gpio_free(wsa881x->pd_gpio); + swr_set_dev_data(pdev, NULL); + return 0; +} + +static int wsa881x_swr_up(struct swr_device *pdev) +{ + int ret; + struct wsa881x_priv *wsa881x; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + ret = wsa881x_gpio_ctrl(wsa881x, true); + if (ret) + dev_err(&pdev->dev, "%s: Failed to enable gpio\n", __func__); + else + wsa881x->state = WSA881X_DEV_UP; + + return ret; +} + +static int wsa881x_swr_down(struct swr_device *pdev) +{ + struct wsa881x_priv *wsa881x; + int ret; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + if (delayed_work_pending(&wsa881x->ocp_ctl_work)) + cancel_delayed_work_sync(&wsa881x->ocp_ctl_work); + ret = wsa881x_gpio_ctrl(wsa881x, false); + if (ret) + dev_err(&pdev->dev, "%s: Failed to disable gpio\n", __func__); + else + wsa881x->state = WSA881X_DEV_DOWN; + + return ret; +} + +static int wsa881x_swr_reset(struct swr_device *pdev) +{ + struct wsa881x_priv *wsa881x; + u8 retry = WSA881X_NUM_RETRY; + u8 devnum = 0; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + wsa881x->bg_cnt = 0; + wsa881x->clk_cnt = 0; + while (swr_get_logical_dev_num(pdev, pdev->addr, &devnum) && retry--) { + /* Retry after 1 msec delay */ + usleep_range(1000, 1100); + } + regcache_mark_dirty(wsa881x->regmap); + regcache_sync(wsa881x->regmap); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int wsa881x_swr_suspend(struct device *dev) +{ + dev_dbg(dev, "%s: system suspend\n", __func__); + return 0; +} + +static int wsa881x_swr_resume(struct device *dev) +{ + struct wsa881x_priv *wsa881x = swr_get_dev_data(to_swr_device(dev)); + + if (!wsa881x) { + dev_err(dev, "%s: wsa881x private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system resume\n", __func__); + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops wsa881x_swr_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(wsa881x_swr_suspend, wsa881x_swr_resume) +}; + +static const struct swr_device_id wsa881x_swr_id[] = { + {"wsa881x", 0}, + {} +}; + +static const struct of_device_id wsa881x_swr_dt_match[] = { + { + .compatible = "qcom,wsa881x", + }, + {} +}; + +static struct swr_driver wsa881x_codec_driver = { + .driver = { + .name = "wsa881x", + .owner = THIS_MODULE, + .pm = &wsa881x_swr_pm_ops, + .of_match_table = wsa881x_swr_dt_match, + }, + .probe = wsa881x_swr_probe, + .remove = wsa881x_swr_remove, + .id_table = wsa881x_swr_id, + .device_up = wsa881x_swr_up, + .device_down = wsa881x_swr_down, + .reset_device = wsa881x_swr_reset, + .startup = wsa881x_swr_startup, +}; + +static int __init wsa881x_codec_init(void) +{ + return swr_driver_register(&wsa881x_codec_driver); +} + +static void __exit wsa881x_codec_exit(void) +{ + swr_driver_unregister(&wsa881x_codec_driver); +} + +module_init(wsa881x_codec_init); +module_exit(wsa881x_codec_exit); + +MODULE_DESCRIPTION("WSA881x Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wsa881x.h b/sound/soc/codecs/wsa881x.h new file mode 100644 index 0000000000000000000000000000000000000000..be234ac0cd07e4cd8025483b6f8e68420fb85f66 --- /dev/null +++ b/sound/soc/codecs/wsa881x.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _WSA881X_H +#define _WSA881X_H + +#include +#include +#include +#include "wsa881x-registers.h" + +#define WSA881X_MAX_SWR_PORTS 4 + +extern int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, + u8 num_port, unsigned int *ch_mask, + unsigned int *ch_rate); + +extern const u8 wsa881x_reg_readable[WSA881X_CACHE_SIZE]; +extern struct regmap_config wsa881x_regmap_config; +extern int wsa881x_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +void wsa881x_regmap_defaults(struct regmap *regmap, u8 version); + +#endif /* _WSA881X_H */